@nex-ui/system
Version:
A lightweight and performant styling library based on Emotion, focusing on component architecture and developer experience.
118 lines (115 loc) • 3.62 kB
JavaScript
import { mergeWith, isArray, isPlainObject, isString, every, memoize, isNumber } from '@nex-ui/utils';
import serialize from '@x1ngyu/serialize-javascript';
function pathToTokenName(path) {
return path.join('.');
}
function camelToKebab(str) {
return str.replace(/([A-Z])/g, (match)=>{
return `-${match.toLowerCase()}`;
});
}
function isDecimalString(str) {
return /^\d+\.\d+$/.test(str);
}
function createCssVarName(prefix, path) {
return `--${prefix}-${path.map((k)=>{
if (isDecimalString(k)) {
return k.split('.').join('-');
}
return camelToKebab(k);
}).join('-')}`;
}
function isResponsiveColor(value) {
return isPlainObject(value) && (value._light || value._dark || value._DEFAULT);
}
function isValidTokenValue(value) {
if (!isString(value) && !isNumber(value)) {
return false;
}
return true;
}
function isValidSemanticTokenValue(value) {
if (!isString(value) && !isNumber(value) && !isResponsiveColor(value)) {
return false;
}
return true;
}
function isValidTokenCategory(category) {
if (!isString(category)) {
return false;
}
switch(category){
case 'colors':
case 'fontFamilies':
case 'fontSizes':
case 'fontWeights':
case 'sizes':
case 'spaces':
case 'lineHeights':
case 'borders':
case 'radii':
case 'breakpoints':
case 'shadows':
case 'transitions':
case 'borderWidths':
case 'zIndexes':
return true;
default:
return false;
}
}
function isValidAliasValue(value) {
if (isString(value) || isArray(value) && every(value, isString)) {
return true;
}
return false;
}
function isValidBreakpointValue(value) {
if (isString(value) && parseInt(value, 10) >= 0) {
return true;
}
return false;
}
function memoizeFn(fn) {
return memoize(fn, (...args)=>serialize(args));
}
function extractTokenPlaceholders(value) {
const regex = /\{(.*?)\}/g;
const matches = value.matchAll(regex);
return [
...matches
];
}
// TODO 明确 CSS 合并规则
function mergeRecipeConfigs(...args) {
const recipes = JSON.parse(JSON.stringify(args));
return mergeWith({}, ...recipes, (targetValue, srcValue, key)=>{
if (key === 'compoundVariants') {
if (targetValue === undefined) {
return srcValue;
}
if (isArray(targetValue) && isArray(srcValue)) {
return [
...targetValue,
...srcValue
];
}
}
if (typeof targetValue !== typeof srcValue || isArray(targetValue) !== isArray(srcValue) || isPlainObject(targetValue) !== isPlainObject(srcValue)) {
return srcValue;
}
});
}
const toExpression = (operator, ...operands)=>operands.map(String).join(` ${operator} `).replace(/calc/g, '');
const multiply = (...operands)=>`calc(${toExpression('*', ...operands)})`;
const negate = (x)=>{
const value = String(x);
if (value != null && !Number.isNaN(parseFloat(value))) {
if (Number(value) === 0) {
return '0';
}
return value.startsWith('-') ? value.slice(1) : `-${value}`;
}
return multiply(value, -1);
};
export { createCssVarName, extractTokenPlaceholders, isResponsiveColor, isValidAliasValue, isValidBreakpointValue, isValidSemanticTokenValue, isValidTokenCategory, isValidTokenValue, memoizeFn, mergeRecipeConfigs, multiply, negate, pathToTokenName };