@nex-ui/system
Version:
A lightweight and performant styling library based on Emotion, focusing on component architecture and developer experience.
159 lines (156 loc) • 5.91 kB
JavaScript
import { walkObject, __DEV__, forEach, isString, reduce } from '@nex-ui/utils';
import { createToken } from './createToken.mjs';
import { isValidTokenCategory, isValidTokenValue, pathToTokenName, isValidSemanticTokenValue, isResponsiveColor, createCssVarName, negate, extractTokenPlaceholders } from '../utils.mjs';
function createTokens(config) {
const { tokens = {}, semanticTokens = {}, prefix } = config;
const tokenMap = new Map();
const cssVarMap = new Map();
function registerToken(workInProgress) {
tokenMap.set(workInProgress.name, workInProgress);
return workInProgress;
}
function resolveTokenReferences(value) {
if (isString(value)) {
const matches = extractTokenPlaceholders(value);
return reduce(matches, (acc, match)=>{
const [placeholder, tokenName] = match;
const token = tokenMap.get(tokenName);
if (token) {
return acc.replace(placeholder, token.value);
}
console.error('[Nex UI] system: An unknown token %s exists in the token reference syntax.', tokenName);
return acc.replace(placeholder, tokenName);
}, value);
}
return value;
}
function createConditions(workInProgress) {
const { originalValue } = workInProgress;
const conditions = isResponsiveColor(originalValue) ? {
base: resolveTokenReferences(originalValue._DEFAULT),
dark: resolveTokenReferences(originalValue._dark),
light: resolveTokenReferences(originalValue._light)
} : {
base: resolveTokenReferences(originalValue)
};
return {
...workInProgress,
conditions
};
}
function createCssVar(workInProgress) {
const { path } = workInProgress;
const variableName = createCssVarName(prefix, path);
const newValue = `var(${variableName})`;
return {
...workInProgress,
cssVar: {
var: variableName,
ref: newValue
},
value: newValue
};
}
function registerNegativeToken(workInProgress) {
const { category, value, path, originalValue, cssVar } = workInProgress;
if (category !== 'spaces' || originalValue === '0rem' || originalValue === '0px' || originalValue === 0) {
return workInProgress;
}
const newPath = [
...path.slice(0, -1),
`-${path[path.length - 1]}`
];
registerToken(createToken({
category,
originalValue,
path: newPath,
name: pathToTokenName(newPath),
value: negate(cssVar?.ref ?? value)
}));
}
function registerCssVars(workInProgress) {
const { conditions, cssVar } = workInProgress;
if (cssVar) {
const variableName = cssVar.var;
forEach(conditions, // @ts-expect-error
(value, condition)=>{
if (!value) {
return;
}
if (!cssVarMap.has(condition)) {
cssVarMap.set(condition, new Map());
}
cssVarMap.get(condition).set(variableName, value);
});
}
return workInProgress;
}
function handleToken(token) {
const workInProgress = createCssVar(createConditions(token));
registerCssVars(workInProgress);
registerToken(workInProgress);
registerNegativeToken(workInProgress);
}
function workloop() {
walkObject(tokens, (value, path)=>{
if (__DEV__ && !isValidTokenCategory(path[0])) {
console.error('[Nex-UI] system: Unknown token category: %s.', path[0]);
return;
}
if (__DEV__ && !isValidTokenValue(value)) {
console.error('[Nex-UI] system: Expect the token value to be a string or a number. but what is currently received is %o.', value);
return;
}
const category = path[0];
const token = createToken({
path,
category,
value: '',
name: pathToTokenName(path),
originalValue: value
});
handleToken(token);
}, {
predicate: (_, path)=>{
const category = path[0];
switch(category){
case 'colors':
return path.length > 3;
default:
return path.length > 2;
}
}
});
walkObject(semanticTokens, (value, path)=>{
if (__DEV__ && !isValidTokenCategory(path[0])) {
console.error('[Nex-UI] system: Unknown token category: %s.', path[0]);
return;
}
if (__DEV__ && !isValidSemanticTokenValue(value)) {
console.error('[Nex-UI] system: Expect the semanticToken value to be a string, a number, or a responsive color. but what is currently received is %o.', value);
return;
}
const category = path[0];
const newPath = filterDefault(path);
const token = createToken({
value: '',
category,
path: newPath,
name: pathToTokenName(newPath),
originalValue: value
});
handleToken(token);
}, {
predicate: isResponsiveColor
});
}
workloop();
return {
getToken: (key)=>tokenMap.get(key),
getGlobalCssVars: ()=>cssVarMap
};
}
function filterDefault(path) {
return path.filter((item)=>item !== 'DEFAULT');
}
export { createTokens };