@core-rc/cssinjs-utils
Version:
A cssinjs util library to support @core-rc and its ecosystem libraries.
179 lines (178 loc) • 8.66 kB
JavaScript
import React from 'react';
import { token2CSSVar, useCSSVarRegister, useStyleRegister } from '@core-rc/cssinjs';
import genCalc from './calc';
import getCompVarPrefix from './getCompVarPrefix';
import getComponentToken from './getComponentToken';
import getDefaultComponentToken from './getDefaultComponentToken';
import genMaxMin from './maxmin';
import statisticToken, { merge as mergeToken } from './statistic';
import useUniqueMemo from '../_util/hooks/useUniqueMemo';
import useDefaultCSP from '../hooks/useCSP';
function genStyleUtils(config) {
// Dependency inversion for preparing basic config.
const { useCSP = useDefaultCSP, useToken, usePrefix, getResetStyles, getCommonStyle, getCompUnitless, } = config;
function genStyleHooks(component, styleFn, getDefaultToken, options) {
const componentName = Array.isArray(component) ? component[0] : component;
function prefixToken(key) {
return `${String(componentName)}${key.slice(0, 1).toUpperCase()}${key.slice(1)}`;
}
// Fill unitless
const originUnitless = options?.unitless || {};
const originCompUnitless = typeof getCompUnitless === 'function' ? getCompUnitless(component) : {};
const compUnitless = {
...originCompUnitless,
[prefixToken('zIndexPopup')]: true,
};
Object.keys(originUnitless).forEach((key) => {
compUnitless[prefixToken(key)] =
originUnitless[key];
});
// Options
const mergedOptions = {
...options,
unitless: compUnitless,
prefixToken,
};
// Hooks
const useStyle = genComponentStyleHook(component, styleFn, getDefaultToken, mergedOptions);
const useCSSVar = genCSSVarRegister(componentName, getDefaultToken, mergedOptions);
return (prefixCls, rootCls = prefixCls) => {
const [, hashId] = useStyle(prefixCls, rootCls);
const [wrapCSSVar, cssVarCls] = useCSSVar(rootCls);
return [wrapCSSVar, hashId, cssVarCls];
};
}
function genCSSVarRegister(component, getDefaultToken, options) {
const { unitless: compUnitless, injectStyle = true, prefixToken, ignore } = options;
const CSSVarRegister = ({ rootCls, cssVar = {} }) => {
const { realToken } = useToken();
useCSSVarRegister({
path: [component],
prefix: cssVar.prefix,
key: cssVar.key,
unitless: compUnitless,
ignore,
token: realToken,
scope: rootCls,
}, () => {
const defaultToken = getDefaultComponentToken(component, realToken, getDefaultToken);
const componentToken = getComponentToken(component, realToken, defaultToken, {
deprecatedTokens: options?.deprecatedTokens,
});
Object.keys(defaultToken).forEach((key) => {
componentToken[prefixToken(key)] = componentToken[key];
delete componentToken[key];
});
return componentToken;
});
return null;
};
const useCSSVar = (rootCls) => {
const { cssVar } = useToken();
return [
(node) => injectStyle && cssVar ? (React.createElement(React.Fragment, null,
React.createElement(CSSVarRegister, { rootCls: rootCls, cssVar: cssVar, component: component }),
node)) : (node),
cssVar?.key,
];
};
return useCSSVar;
}
function genComponentStyleHook(componentName, styleFn, getDefaultToken, options = {}) {
const cells = (Array.isArray(componentName) ? componentName : [componentName, componentName]);
const [component] = cells;
const concatComponent = cells.join('-');
const mergedLayer = config.layer || {
name: 'antd',
};
// Return new style hook
return (prefixCls, rootCls = prefixCls) => {
const { theme, realToken, hashId, token, cssVar } = useToken();
const { rootPrefixCls, iconPrefixCls } = usePrefix();
const csp = useCSP();
const type = cssVar ? 'css' : 'js';
// Use unique memo to share the result across all instances
const calc = useUniqueMemo(() => {
const unitlessCssVar = new Set();
if (cssVar) {
Object.keys(options.unitless || {}).forEach((key) => {
// Some component proxy the AliasToken (e.g. Image) and some not (e.g. Modal)
// We should both pass in `unitlessCssVar` to make sure the CSSVar can be unitless.
unitlessCssVar.add(token2CSSVar(key, cssVar.prefix));
unitlessCssVar.add(token2CSSVar(key, getCompVarPrefix(component, cssVar.prefix)));
});
}
return genCalc(type, unitlessCssVar);
}, [type, component, cssVar?.prefix]);
const { max, min } = genMaxMin(type);
// Shared config
const sharedConfig = {
theme,
token,
hashId,
nonce: () => csp.nonce,
clientOnly: options.clientOnly,
layer: mergedLayer,
// antd is always at top of styles
order: options.order || -999,
};
// Generate style for all need reset tags.
useStyleRegister({ ...sharedConfig, clientOnly: false, path: ['Shared', rootPrefixCls] }, () => (typeof getResetStyles === 'function' ? getResetStyles(token) : []));
const wrapSSR = useStyleRegister({ ...sharedConfig, path: [concatComponent, prefixCls, iconPrefixCls] }, () => {
if (options.injectStyle === false) {
return [];
}
const { token: proxyToken, flush } = statisticToken(token);
const defaultComponentToken = getDefaultComponentToken(component, realToken, getDefaultToken);
const componentCls = `.${prefixCls}`;
const componentToken = getComponentToken(component, realToken, defaultComponentToken, { deprecatedTokens: options.deprecatedTokens });
if (cssVar && defaultComponentToken && typeof defaultComponentToken === 'object') {
Object.keys(defaultComponentToken).forEach((key) => {
defaultComponentToken[key] = `var(${token2CSSVar(key, getCompVarPrefix(component, cssVar.prefix))})`;
});
}
const mergedToken = mergeToken(proxyToken, {
componentCls,
prefixCls,
iconCls: `.${iconPrefixCls}`,
antCls: `.${rootPrefixCls}`,
calc,
// @ts-ignore
max,
// @ts-ignore
min,
}, cssVar ? defaultComponentToken : componentToken);
const styleInterpolation = styleFn(mergedToken, {
hashId,
prefixCls,
rootPrefixCls,
iconPrefixCls,
});
flush(component, componentToken);
const commonStyle = typeof getCommonStyle === 'function'
? getCommonStyle(mergedToken, prefixCls, rootCls, options.resetFont)
: null;
return [options.resetStyle === false ? null : commonStyle, styleInterpolation];
});
return [wrapSSR, hashId];
};
}
function genSubStyleComponent(componentName, styleFn, getDefaultToken, options = {}) {
const useStyle = genComponentStyleHook(componentName, styleFn, getDefaultToken, {
resetStyle: false,
// Sub Style should default after root one
order: -998,
...options,
});
const StyledComponent = ({ prefixCls, rootCls = prefixCls, }) => {
useStyle(prefixCls, rootCls);
return null;
};
if (process.env.NODE_ENV !== 'production') {
StyledComponent.displayName = `SubStyle_${String(Array.isArray(componentName) ? componentName.join('.') : componentName)}`;
}
return StyledComponent;
}
return { genStyleHooks, genSubStyleComponent, genComponentStyleHook };
}
export default genStyleUtils;