UNPKG

@nex-ui/system

Version:

A lightweight and performant styling library based on Emotion, focusing on component architecture and developer experience.

154 lines (151 loc) 4.54 kB
import { mergeWith, isPlainObject, camelCase, isString, memoize, kebabCase } from '@nex-ui/utils'; import serialize from '@x1ngyu/serialize-javascript'; import { all } from 'known-css-properties'; function pathToTokenName(path) { return path.join('.'); } 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 kebabCase(k); }).join('-')}`; } function isResponsiveColor(value) { return isPlainObject(value) && (value._light || value._dark || value._DEFAULT); } function isValidTokenValue(value) { if (!isString(value) && typeof value !== 'number') { return false; } return true; } function isValidSemanticTokenValue(value) { if (!isString(value) && typeof value !== 'number' && !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 'zIndices': return true; default: return false; } } function isValidAliasValue(value) { if (isString(value) || Array.isArray(value) && value.every(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 (Array.isArray(targetValue) && Array.isArray(srcValue)) { return [ ...targetValue, ...srcValue ]; } } if (typeof targetValue !== typeof srcValue || Array.isArray(targetValue) !== Array.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); }; const ALL_CSS_PROPERTIES = new Set(all.map(camelCase)); const isCSSProperty = (key)=>{ // CSS variable if (key.startsWith('--')) return true; return ALL_CSS_PROPERTIES.has(key); }; const isSelector = (key)=>{ // start with & if (/&/.test(key)) { return true; } // combinators if (/^(?!.*@)(?!.*\()(\s*[>+~]\s*|[a-zA-Z]\s+[a-zA-Z])/.test(key)) { return true; } // special rules if (/^@(media|keyframes|supports|import|namespace|layer)/.test(key)) { return true; } // id or class selector if (/^[#.]/.test(key)) { return true; } // attribute selector if (/\[[^\]]*[=~|^$*]?[^\]]*\]/.test(key)) { return true; } // multiple selectors if (/,/.test(key)) { return true; } // wildcard selector if (/\*/.test(key)) { return true; } if (isCSSProperty(key)) { return false; } return true; }; export { createCssVarName, extractTokenPlaceholders, isResponsiveColor, isSelector, isValidAliasValue, isValidBreakpointValue, isValidSemanticTokenValue, isValidTokenCategory, isValidTokenValue, memoizeFn, mergeRecipeConfigs, multiply, negate, pathToTokenName };