create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
111 lines (87 loc) • 3.31 kB
JavaScript
const matchAll = require('match-all');
// Tailwind started using CSS variables for color opacity since v1.4.0,
// this helper adds a primitive support for these
const useVariables = object => {
const newObject = {};
for (const [key, value] of Object.entries(object)) {
if (!key.startsWith('--')) {
if (typeof value === 'string') {
newObject[key] = value.replace(/var\(([a-zA-Z-]+)\)/, (_, name) => {
return object[name];
});
} else {
newObject[key] = value;
}
}
}
return newObject;
};
// Font variant numeric utilities need a special treatment, because
// there can be many font variant classes and they need to be transformed to an array
const FONT_VARIANT_REGEX = /(oldstyle-nums|lining-nums|tabular-nums|proportional-nums)/g;
const addFontVariant = (style, classNames) => {
const matches = matchAll(classNames, FONT_VARIANT_REGEX).toArray();
if (matches.length > 0) {
style.fontVariant = matches;
}
};
// Letter spacing also needs a special treatment, because its value is set
// in em unit, that's why it requires a font size to be set too
const FONT_SIZE_REGEX = /text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl)/;
const LETTER_SPACING_REGEX = /(tracking-[a-z]+)/;
const addLetterSpacing = (tailwindStyles, style, classNames) => {
const letterSpacingMatches = LETTER_SPACING_REGEX.exec(classNames);
if (!letterSpacingMatches) {
return;
}
const fontSizeMatches = FONT_SIZE_REGEX.exec(classNames);
if (!fontSizeMatches) {
throw new Error(
"Font size is required when applying letter spacing, e.g. 'text-lg tracking-tighter'" // eslint-disable-line quotes
);
}
const letterSpacingClass = letterSpacingMatches[0];
const {letterSpacing} = tailwindStyles[letterSpacingClass];
const fontSizeClass = fontSizeMatches[0];
const {fontSize} = tailwindStyles[fontSizeClass];
style.letterSpacing = Number.parseFloat(letterSpacing) * fontSize;
};
const create = tailwindStyles => {
// Pass a list of class names separated by a space, for example:
// "bg-green-100 text-green-800 font-semibold")
// and receive a styles object for use in React Native views
const tailwind = classNames => {
const style = {};
if (!classNames) {
return style;
}
addFontVariant(style, classNames);
addLetterSpacing(tailwindStyles, style, classNames);
const separateClassNames = classNames
.replace(/\s+/g, ' ')
.trim()
.split(' ')
.filter(className => !className.startsWith('tracking-'));
for (const className of separateClassNames) {
if (tailwindStyles[className]) {
Object.assign(style, tailwindStyles[className]);
} else {
console.warn(`Unsupported Tailwind class: "${className}"`);
}
}
return useVariables(style);
};
// Pass the name of a color (e.g. "blue-500") and receive a color value (e.g. "#4399e1"),
// or a color and opacity (e.g. "black opacity-50") and get a color with opacity (e.g. "rgba(0,0,0,0.5)")
const getColor = name => {
const style = tailwind(name.split(' ').map(className => `bg-${className}`).join(' '));
return style.backgroundColor;
};
return {tailwind, getColor};
};
const {tailwind, getColor} = create(require('./styles.json'));
module.exports = tailwind;
module.exports.default = tailwind;
module.exports.getColor = getColor;
module.exports.create = create;
;