@nex-ui/system
Version:
A lightweight and performant styling library based on Emotion, focusing on component architecture and developer experience.
108 lines (105 loc) • 4.41 kB
JavaScript
import { isPlainObject, walkObject, merge, get } from '@nex-ui/utils';
import { memoizeFn, isSelector } from './utils.mjs';
const isCustomSelector = (key)=>{
return key.startsWith('_') && key !== '_DEFAULT';
};
const createCssFn = ({ normalize, isAlias, getCustomizedSelector })=>{
const css = (interpolation)=>{
if (!interpolation) {
return '';
}
// TODO: unused
const componentSelector = interpolation;
if (componentSelector.__emotion_styles !== undefined) {
return componentSelector;
}
if (Array.isArray(interpolation)) {
const arrayInterpolation = interpolation;
return arrayInterpolation.map((v)=>css(v));
}
if (isPlainObject(interpolation)) {
const keyframes = interpolation;
if (keyframes.anim === 1) {
return keyframes;
}
// TODO: unused
const serializedStyles = interpolation;
if (serializedStyles.styles !== undefined) {
return serializedStyles;
}
const cssOjbect = interpolation;
const handlePath = (path)=>{
return path.filter((v)=>v !== '_DEFAULT').sort((a)=>{
// Filter out _DEFAULT first, ensure that those starting with letters are in front,
// and those starting with _ are at the back, to support suffix selectors
const charCode = a.charCodeAt(0);
if (charCode > 64 && charCode < 91) {
return -1;
}
return 96 - a.charCodeAt(0);
}).map((p)=>{
// 0 - 9
if (p.charCodeAt(0) > 47 && p.charCodeAt(0) < 58) {
const part = path.slice(0, path.length - 1);
const prevValue = get(cssOjbect, part);
const index = Number(p);
if (!Number.isNaN(index) && Array.isArray(prevValue)) {
// Handle array breakpoints
return getCustomizedSelector(`_${index}`) ?? '';
}
}
// Handle custom selectors and object breakpoints
return isCustomSelector(p) ? getCustomizedSelector(p) ?? p : p;
});
};
const getColorPalette = (path)=>{
if (path.length === 0) {
return cssOjbect['colorPalette'];
}
return get(cssOjbect, [
...path,
'colorPalette'
], getColorPalette(path.slice(0, path.length - 1)));
};
const result = {};
walkObject(cssOjbect, (propValue, path)=>{
if (Array.isArray(propValue)) {
const selectors = path.map((p)=>isCustomSelector(p) ? getCustomizedSelector(p) ?? p : p);
const lastSelector = selectors[selectors.length - 1];
mergeByPath(result, selectors, css(propValue), (v)=>v === lastSelector ? [] : {});
return;
}
const prefix = path.slice(0, path.length - 1);
if (path[path.length - 1] === 'colorPalette') {
return;
}
const colorPalette = getColorPalette(prefix);
const [propKey, ...selectors] = handlePath(path);
const normalized = normalize({
propKey,
propValue,
colorPalette
});
mergeByPath(result, selectors, normalized);
}, {
predicate: (v, path)=>{
const key = path[path.length - 2];
return Array.isArray(v) && !isAlias(key) && (isCustomSelector(key) || isSelector(key));
}
});
return result;
}
return interpolation;
};
return memoizeFn(css);
};
function mergeByPath(target, path, value, customizer) {
let acc = target;
path.forEach((k)=>{
if (!k) return;
if (!acc[k]) acc[k] = customizer ? customizer(k) : {};
acc = acc[k];
});
return merge(acc, value);
}
export { createCssFn };