Skip to content

Commit afc4b67

Browse files
authored
feat: css var support hash (#214)
* feat: css var support hash * chore: remove useless comment * chore: fix tsc * test: add test case * chore: rm only
1 parent fcb0e28 commit afc4b67

File tree

15 files changed

+468
-288
lines changed

15 files changed

+468
-288
lines changed

docs/examples/components/Button.tsx

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1-
import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
2-
import { unit, useStyleRegister } from '@ant-design/cssinjs';
1+
import {
2+
CSSInterpolation,
3+
CSSObject,
4+
unit,
5+
useCSSVarRegister,
6+
useStyleRegister,
7+
} from '@ant-design/cssinjs';
38
import classNames from 'classnames';
49
import React from 'react';
510
import type { DerivativeToken } from './theme';
611
import { useToken } from './theme';
712

13+
interface ButtonToken extends DerivativeToken {
14+
buttonPadding: string;
15+
}
16+
817
// 通用框架
918
const genSharedButtonStyle = (
1019
prefixCls: string,
11-
token: DerivativeToken,
20+
token: ButtonToken,
1221
): CSSInterpolation => ({
1322
[`.${prefixCls}`]: {
1423
borderColor: token.borderColor,
1524
borderWidth: token.borderWidth,
1625
borderRadius: token.borderRadius,
1726
lineHeight: token.lineHeight,
27+
padding: token.buttonPadding,
1828

1929
cursor: 'pointer',
2030

@@ -33,7 +43,7 @@ const genSharedButtonStyle = (
3343
// 实心底色样式
3444
const genSolidButtonStyle = (
3545
prefixCls: string,
36-
token: DerivativeToken,
46+
token: ButtonToken,
3747
postFn: () => CSSObject,
3848
): CSSInterpolation => [
3949
genSharedButtonStyle(prefixCls, token),
@@ -47,7 +57,7 @@ const genSolidButtonStyle = (
4757
// 默认样式
4858
const genDefaultButtonStyle = (
4959
prefixCls: string,
50-
token: DerivativeToken,
60+
token: ButtonToken,
5161
): CSSInterpolation =>
5262
genSolidButtonStyle(prefixCls, token, () => ({
5363
backgroundColor: token.componentBackgroundColor,
@@ -62,7 +72,7 @@ const genDefaultButtonStyle = (
6272
// 主色样式
6373
const genPrimaryButtonStyle = (
6474
prefixCls: string,
65-
token: DerivativeToken,
75+
token: ButtonToken,
6676
): CSSInterpolation =>
6777
genSolidButtonStyle(prefixCls, token, () => ({
6878
backgroundColor: token.primaryColor,
@@ -77,7 +87,7 @@ const genPrimaryButtonStyle = (
7787
// 透明按钮
7888
const genGhostButtonStyle = (
7989
prefixCls: string,
80-
token: DerivativeToken,
90+
token: ButtonToken,
8191
): CSSInterpolation => [
8292
genSharedButtonStyle(prefixCls, token),
8393
{
@@ -103,23 +113,45 @@ const Button = ({ className, type, ...restProps }: ButtonProps) => {
103113
const prefixCls = 'ant-btn';
104114

105115
// 【自定义】制造样式
106-
const [theme, token, hashId, cssVarKey] = useToken();
116+
const [theme, token, hashId, cssVarKey, realToken] = useToken();
107117

108118
// default 添加默认样式选择器后可以省很多冲突解决问题
109119
const defaultCls = `${prefixCls}-default`;
110120
const primaryCls = `${prefixCls}-primary`;
111121
const ghostCls = `${prefixCls}-ghost`;
112122

113-
// 全局注册,内部会做缓存优化
114-
useStyleRegister(
115-
{ theme, token, hashId, path: [prefixCls] },
116-
() => [
117-
genDefaultButtonStyle(defaultCls, token),
118-
genPrimaryButtonStyle(primaryCls, token),
119-
genGhostButtonStyle(ghostCls, token),
120-
],
123+
const [cssVarToken] = useCSSVarRegister(
124+
{
125+
path: [prefixCls],
126+
key: cssVarKey,
127+
token: realToken,
128+
prefix: prefixCls,
129+
unitless: {
130+
lineHeight: true,
131+
},
132+
ignore: {
133+
lineHeightBase: true,
134+
},
135+
scope: prefixCls,
136+
hashId,
137+
},
138+
() => ({
139+
buttonPadding: '4px 8px',
140+
}),
121141
);
122142

143+
const mergedToken: any = {
144+
...token,
145+
...cssVarToken,
146+
};
147+
148+
// 全局注册,内部会做缓存优化
149+
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [
150+
genDefaultButtonStyle(defaultCls, mergedToken),
151+
genPrimaryButtonStyle(primaryCls, mergedToken),
152+
genGhostButtonStyle(ghostCls, mergedToken),
153+
]);
154+
123155
const typeCls =
124156
(
125157
{

docs/examples/components/theme.tsx

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CSSObject, Theme } from '@ant-design/cssinjs';
22
import { createTheme, useCacheToken } from '@ant-design/cssinjs';
33
import { TinyColor } from '@ctrl/tinycolor';
4-
import React from 'react';
4+
import React, { PropsWithChildren } from 'react';
55

66
export type GetStyle = (prefixCls: string, token: DerivativeToken) => CSSObject;
77

@@ -61,32 +61,68 @@ export const DesignTokenContext = React.createContext<{
6161
token: defaultDesignToken,
6262
});
6363

64+
export const DesignTokenProvider: React.FC<
65+
PropsWithChildren<{
66+
value?: {
67+
token?: Partial<DesignToken>;
68+
hashed?: string | boolean;
69+
cssVar?: {
70+
key?: string;
71+
prefix?: string;
72+
};
73+
};
74+
}>
75+
> = ({ children, value }) => {
76+
const { token, hashed, cssVar } = value || {};
77+
const themeKey = React.useId();
78+
const cssVarKey = `css-var-${themeKey.replace(/:/g, '')}`;
79+
return (
80+
<DesignTokenContext.Provider
81+
value={{
82+
token,
83+
hashed,
84+
cssVar: {
85+
...cssVar,
86+
key: cssVar?.key || cssVarKey,
87+
},
88+
}}
89+
>
90+
{children}
91+
</DesignTokenContext.Provider>
92+
);
93+
};
94+
6495
export function useToken(): [
6596
Theme<any, any>,
6697
DerivativeToken,
6798
string,
68-
string | undefined,
99+
string,
100+
DerivativeToken,
69101
] {
70102
const {
71103
token: rootDesignToken = {},
72104
hashed,
73-
cssVar,
105+
cssVar: ctxCssVar,
74106
} = React.useContext(DesignTokenContext);
75107
const theme = React.useContext(ThemeContext);
76108

77-
const [token, hashId] = useCacheToken<DerivativeToken, DesignToken>(
78-
theme,
79-
[defaultDesignToken, rootDesignToken],
80-
{
81-
salt: typeof hashed === 'string' ? hashed : '',
82-
cssVar: {
83-
prefix: 'rc',
84-
key: cssVar?.key || 'css-var-root',
85-
unitless: {
86-
lineHeight: true,
87-
},
109+
const cssVar = {
110+
key: ctxCssVar?.key || 'css-var-root',
111+
};
112+
113+
const [token, hashId, actualToken] = useCacheToken<
114+
DerivativeToken,
115+
DesignToken
116+
>(theme, [defaultDesignToken, rootDesignToken], {
117+
salt: typeof hashed === 'string' ? hashed : '',
118+
cssVar: {
119+
prefix: 'rc',
120+
key: cssVar?.key || 'css-var-root',
121+
unitless: {
122+
lineHeight: true,
88123
},
124+
hashed: !!hashed,
89125
},
90-
);
91-
return [theme, token, hashed ? hashId : '', cssVar?.key];
126+
});
127+
return [theme, token, hashed ? hashId : '', cssVar.key, actualToken];
92128
}

docs/examples/css-var.tsx

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
import React from 'react';
22
import './basic.less';
33
import Button from './components/Button';
4-
import { DesignTokenContext } from './components/theme';
4+
import { DesignTokenProvider } from './components/theme';
5+
6+
const Demo = () => (
7+
<>
8+
<Button>Default</Button>
9+
<Button type="primary">Primary</Button>
10+
<Button type="ghost">Ghost</Button>
11+
12+
<Button className="btn-override">Override By ClassName</Button>
13+
</>
14+
);
515

616
export default function App() {
717
const [show, setShow] = React.useState(true);
@@ -22,29 +32,24 @@ export default function App() {
2232

2333
{show && (
2434
<div>
25-
<DesignTokenContext.Provider
26-
value={{ cssVar: { key: 'default' }, hashed: false }}
27-
>
28-
<Button>Default</Button>
29-
<Button type="primary">Primary</Button>
30-
<Button type="ghost">Ghost</Button>
31-
32-
<Button className="btn-override">Override By ClassName</Button>
33-
</DesignTokenContext.Provider>
35+
<Demo />
3436
<br />
35-
<DesignTokenContext.Provider
37+
<DesignTokenProvider
3638
value={{
3739
token: { primaryColor: 'green' },
38-
cssVar: { key: 'default2' },
39-
hashed: false,
4040
}}
4141
>
42-
<Button>Default</Button>
43-
<Button type="primary">Primary</Button>
44-
<Button type="ghost">Ghost</Button>
45-
46-
<Button className="btn-override">Override By ClassName</Button>
47-
</DesignTokenContext.Provider>
42+
<Demo />
43+
</DesignTokenProvider>
44+
<br />
45+
<DesignTokenProvider
46+
value={{
47+
token: { primaryColor: 'orange' },
48+
hashed: true,
49+
}}
50+
>
51+
<Demo />
52+
</DesignTokenProvider>
4853
</div>
4954
)}
5055
</div>

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"pretty-quick": "pretty-quick",
3939
"test": "vitest --watch=false",
4040
"test:watch": "vitest",
41+
"tsc": "tsc --noEmit",
4142
"coverage": "vitest run --coverage"
4243
},
4344
"dependencies": {

src/hooks/useCSSVarRegister.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ const useCSSVarRegister = <V, T extends Record<string, V>>(
3030
ignore?: Record<string, boolean>;
3131
scope?: string;
3232
token: any;
33+
hashId?: string;
3334
},
3435
fn: () => T,
3536
) => {
36-
const { key, prefix, unitless, ignore, token, scope = '' } = config;
37+
const { key, prefix, unitless, ignore, token, hashId, scope = '' } = config;
3738
const {
3839
cache: { instanceId },
3940
container,
41+
hashPriority,
4042
} = useContext(StyleContext);
4143
const { _tokenKey: tokenKey } = token;
4244

@@ -52,6 +54,8 @@ const useCSSVarRegister = <V, T extends Record<string, V>>(
5254
unitless,
5355
ignore,
5456
scope,
57+
hashPriority,
58+
hashCls: hashId,
5559
});
5660
const styleId = uniqueHash(stylePath, cssVarsStr);
5761
return [mergedToken, cssVarsStr, styleId, key];

src/hooks/useCacheToken.tsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ export interface Option<DerivativeToken, DesignToken> {
5656
* Transform token to css variables.
5757
*/
5858
cssVar: {
59+
hashed?: boolean;
5960
/** Prefix for css variables */
6061
prefix?: string;
6162
/** Tokens that should not be appended with unit */
@@ -161,6 +162,7 @@ export default function useCacheToken<
161162
const {
162163
cache: { instanceId },
163164
container,
165+
hashPriority,
164166
} = useContext(StyleContext);
165167
const {
166168
salt = '',
@@ -176,7 +178,7 @@ export default function useCacheToken<
176178
const tokenStr = flattenToken(mergedToken);
177179
const overrideTokenStr = flattenToken(override);
178180

179-
const cssVarStr = cssVar ? flattenToken(cssVar) : '';
181+
const cssVarStr = flattenToken(cssVar);
180182

181183
const cachedToken = useGlobalCache<TokenCacheValue<DerivativeToken>>(
182184
TOKEN_PREFIX,
@@ -185,9 +187,15 @@ export default function useCacheToken<
185187
const mergedDerivativeToken = compute
186188
? compute(mergedToken, override, theme)
187189
: getComputedToken(mergedToken, override, theme, formatToken);
190+
const actualToken = { ...mergedDerivativeToken };
191+
192+
// Optimize for `useStyleRegister` performance
193+
const mergedSalt = `${salt}_${cssVar.prefix}`;
194+
const hashId = hash(mergedSalt);
195+
const hashCls = `${hashPrefix}-${hash(mergedSalt)}`;
196+
actualToken._tokenKey = token2key(actualToken, mergedSalt);
188197

189198
// Replace token value with css variables
190-
const actualToken = { ...mergedDerivativeToken };
191199
const [tokenWithCssVar, cssVarsStr] = transformToken(
192200
mergedDerivativeToken,
193201
cssVar.key,
@@ -196,19 +204,14 @@ export default function useCacheToken<
196204
ignore: cssVar.ignore,
197205
unitless: cssVar.unitless,
198206
preserve: cssVar.preserve,
207+
hashPriority,
208+
hashCls: cssVar.hashed ? hashCls : undefined,
199209
},
200210
) as [any, string];
211+
tokenWithCssVar._hashId = hashId;
201212

202-
// Optimize for `useStyleRegister` performance
203-
const mergedSalt = `${salt}_${cssVar.prefix || ''}`;
204-
actualToken._tokenKey = token2key(actualToken, mergedSalt);
205-
206-
const themeKey = cssVar.key;
207-
recordCleanToken(themeKey);
208-
209-
const hashId = `${hashPrefix}-${hash(mergedSalt)}`;
210-
211-
return [tokenWithCssVar, hashId, actualToken, cssVarsStr, cssVar.key];
213+
recordCleanToken(hashId);
214+
return [tokenWithCssVar, hashCls, actualToken, cssVarsStr, cssVar.key];
212215
},
213216
([, , , , themeKey]) => {
214217
// Remove token will remove all related style

0 commit comments

Comments
 (0)