UNPKG

@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
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 };