UNPKG

@core-rc/cssinjs-utils

Version:

A cssinjs util library to support @core-rc and its ecosystem libraries.

179 lines (178 loc) 8.66 kB
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;