Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: css var support hash #214

Merged
merged 5 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 48 additions & 16 deletions docs/examples/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
import { unit, useStyleRegister } from '@ant-design/cssinjs';
import {
CSSInterpolation,
CSSObject,
unit,
useCSSVarRegister,
useStyleRegister,
} from '@ant-design/cssinjs';
import classNames from 'classnames';
import React from 'react';
import type { DerivativeToken } from './theme';
import { useToken } from './theme';

interface ButtonToken extends DerivativeToken {
buttonPadding: string;
}

// 通用框架
const genSharedButtonStyle = (
prefixCls: string,
token: DerivativeToken,
token: ButtonToken,
): CSSInterpolation => ({
[`.${prefixCls}`]: {
borderColor: token.borderColor,
borderWidth: token.borderWidth,
borderRadius: token.borderRadius,
lineHeight: token.lineHeight,
padding: token.buttonPadding,

cursor: 'pointer',

Expand All @@ -33,7 +43,7 @@ const genSharedButtonStyle = (
// 实心底色样式
const genSolidButtonStyle = (
prefixCls: string,
token: DerivativeToken,
token: ButtonToken,
postFn: () => CSSObject,
): CSSInterpolation => [
genSharedButtonStyle(prefixCls, token),
Expand All @@ -47,7 +57,7 @@ const genSolidButtonStyle = (
// 默认样式
const genDefaultButtonStyle = (
prefixCls: string,
token: DerivativeToken,
token: ButtonToken,
): CSSInterpolation =>
genSolidButtonStyle(prefixCls, token, () => ({
backgroundColor: token.componentBackgroundColor,
Expand All @@ -62,7 +72,7 @@ const genDefaultButtonStyle = (
// 主色样式
const genPrimaryButtonStyle = (
prefixCls: string,
token: DerivativeToken,
token: ButtonToken,
): CSSInterpolation =>
genSolidButtonStyle(prefixCls, token, () => ({
backgroundColor: token.primaryColor,
Expand All @@ -77,7 +87,7 @@ const genPrimaryButtonStyle = (
// 透明按钮
const genGhostButtonStyle = (
prefixCls: string,
token: DerivativeToken,
token: ButtonToken,
): CSSInterpolation => [
genSharedButtonStyle(prefixCls, token),
{
Expand All @@ -103,23 +113,45 @@ const Button = ({ className, type, ...restProps }: ButtonProps) => {
const prefixCls = 'ant-btn';

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

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

// 全局注册,内部会做缓存优化
useStyleRegister(
{ theme, token, hashId, path: [prefixCls] },
() => [
genDefaultButtonStyle(defaultCls, token),
genPrimaryButtonStyle(primaryCls, token),
genGhostButtonStyle(ghostCls, token),
],
const [cssVarToken] = useCSSVarRegister(
{
path: [prefixCls],
key: cssVarKey,
token: realToken,
prefix: prefixCls,
unitless: {
lineHeight: true,
},
ignore: {
lineHeightBase: true,
},
scope: prefixCls,
hashId,
},
() => ({
buttonPadding: '4px 8px',
}),
);

const mergedToken: any = {
...token,
...cssVarToken,
};

// 全局注册,内部会做缓存优化
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [
genDefaultButtonStyle(defaultCls, mergedToken),
genPrimaryButtonStyle(primaryCls, mergedToken),
genGhostButtonStyle(ghostCls, mergedToken),
]);

const typeCls =
(
{
Expand Down
68 changes: 52 additions & 16 deletions docs/examples/components/theme.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { CSSObject, Theme } from '@ant-design/cssinjs';
import { createTheme, useCacheToken } from '@ant-design/cssinjs';
import { TinyColor } from '@ctrl/tinycolor';
import React from 'react';
import React, { PropsWithChildren } from 'react';

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

Expand Down Expand Up @@ -61,32 +61,68 @@ export const DesignTokenContext = React.createContext<{
token: defaultDesignToken,
});

export const DesignTokenProvider: React.FC<
PropsWithChildren<{
value?: {
token?: Partial<DesignToken>;
hashed?: string | boolean;
cssVar?: {
key?: string;
prefix?: string;
};
};
}>
> = ({ children, value }) => {
const { token, hashed, cssVar } = value || {};
const themeKey = React.useId();
const cssVarKey = `css-var-${themeKey.replace(/:/g, '')}`;
return (
<DesignTokenContext.Provider
value={{
token,
hashed,
cssVar: {
...cssVar,
key: cssVar?.key || cssVarKey,
},
}}
>
{children}
</DesignTokenContext.Provider>
);
};

export function useToken(): [
Theme<any, any>,
DerivativeToken,
string,
string | undefined,
string,
DerivativeToken,
] {
const {
token: rootDesignToken = {},
hashed,
cssVar,
cssVar: ctxCssVar,
} = React.useContext(DesignTokenContext);
const theme = React.useContext(ThemeContext);

const [token, hashId] = useCacheToken<DerivativeToken, DesignToken>(
theme,
[defaultDesignToken, rootDesignToken],
{
salt: typeof hashed === 'string' ? hashed : '',
cssVar: {
prefix: 'rc',
key: cssVar?.key || 'css-var-root',
unitless: {
lineHeight: true,
},
const cssVar = {
key: ctxCssVar?.key || 'css-var-root',
};

const [token, hashId, actualToken] = useCacheToken<
DerivativeToken,
DesignToken
>(theme, [defaultDesignToken, rootDesignToken], {
salt: typeof hashed === 'string' ? hashed : '',
cssVar: {
prefix: 'rc',
key: cssVar?.key || 'css-var-root',
unitless: {
lineHeight: true,
},
hashed: !!hashed,
},
);
return [theme, token, hashed ? hashId : '', cssVar?.key];
});
return [theme, token, hashed ? hashId : '', cssVar.key, actualToken];
}
43 changes: 24 additions & 19 deletions docs/examples/css-var.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import React from 'react';
import './basic.less';
import Button from './components/Button';
import { DesignTokenContext } from './components/theme';
import { DesignTokenProvider } from './components/theme';

const Demo = () => (
<>
<Button>Default</Button>
<Button type="primary">Primary</Button>
<Button type="ghost">Ghost</Button>

<Button className="btn-override">Override By ClassName</Button>
</>
);

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

{show && (
<div>
<DesignTokenContext.Provider
value={{ cssVar: { key: 'default' }, hashed: false }}
>
<Button>Default</Button>
<Button type="primary">Primary</Button>
<Button type="ghost">Ghost</Button>

<Button className="btn-override">Override By ClassName</Button>
</DesignTokenContext.Provider>
<Demo />
<br />
<DesignTokenContext.Provider
<DesignTokenProvider
value={{
token: { primaryColor: 'green' },
cssVar: { key: 'default2' },
hashed: false,
}}
>
<Button>Default</Button>
<Button type="primary">Primary</Button>
<Button type="ghost">Ghost</Button>

<Button className="btn-override">Override By ClassName</Button>
</DesignTokenContext.Provider>
<Demo />
</DesignTokenProvider>
<br />
<DesignTokenProvider
value={{
token: { primaryColor: 'orange' },
hashed: true,
}}
>
<Demo />
</DesignTokenProvider>
</div>
)}
</div>
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"pretty-quick": "pretty-quick",
"test": "vitest --watch=false",
"test:watch": "vitest",
"tsc": "tsc --noEmit",
"coverage": "vitest run --coverage"
},
"dependencies": {
Expand Down
6 changes: 5 additions & 1 deletion src/hooks/useCSSVarRegister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ const useCSSVarRegister = <V, T extends Record<string, V>>(
ignore?: Record<string, boolean>;
scope?: string;
token: any;
hashId?: string;
},
fn: () => T,
) => {
const { key, prefix, unitless, ignore, token, scope = '' } = config;
const { key, prefix, unitless, ignore, token, hashId, scope = '' } = config;
const {
cache: { instanceId },
container,
hashPriority,
} = useContext(StyleContext);
const { _tokenKey: tokenKey } = token;

Expand All @@ -52,6 +54,8 @@ const useCSSVarRegister = <V, T extends Record<string, V>>(
unitless,
ignore,
scope,
hashPriority,
hashCls: hashId,
});
const styleId = uniqueHash(stylePath, cssVarsStr);
return [mergedToken, cssVarsStr, styleId, key];
Expand Down
27 changes: 15 additions & 12 deletions src/hooks/useCacheToken.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface Option<DerivativeToken, DesignToken> {
* Transform token to css variables.
*/
cssVar: {
hashed?: boolean;
/** Prefix for css variables */
prefix?: string;
/** Tokens that should not be appended with unit */
Expand Down Expand Up @@ -161,6 +162,7 @@ export default function useCacheToken<
const {
cache: { instanceId },
container,
hashPriority,
} = useContext(StyleContext);
const {
salt = '',
Expand All @@ -176,7 +178,7 @@ export default function useCacheToken<
const tokenStr = flattenToken(mergedToken);
const overrideTokenStr = flattenToken(override);

const cssVarStr = cssVar ? flattenToken(cssVar) : '';
const cssVarStr = flattenToken(cssVar);

const cachedToken = useGlobalCache<TokenCacheValue<DerivativeToken>>(
TOKEN_PREFIX,
Expand All @@ -185,9 +187,15 @@ export default function useCacheToken<
const mergedDerivativeToken = compute
? compute(mergedToken, override, theme)
: getComputedToken(mergedToken, override, theme, formatToken);
const actualToken = { ...mergedDerivativeToken };

// Optimize for `useStyleRegister` performance
const mergedSalt = `${salt}_${cssVar.prefix}`;
const hashId = hash(mergedSalt);
const hashCls = `${hashPrefix}-${hash(mergedSalt)}`;
actualToken._tokenKey = token2key(actualToken, mergedSalt);

// Replace token value with css variables
const actualToken = { ...mergedDerivativeToken };
const [tokenWithCssVar, cssVarsStr] = transformToken(
mergedDerivativeToken,
cssVar.key,
Expand All @@ -196,19 +204,14 @@ export default function useCacheToken<
ignore: cssVar.ignore,
unitless: cssVar.unitless,
preserve: cssVar.preserve,
hashPriority,
hashCls: cssVar.hashed ? hashCls : undefined,
},
) as [any, string];
tokenWithCssVar._hashId = hashId;

// Optimize for `useStyleRegister` performance
const mergedSalt = `${salt}_${cssVar.prefix || ''}`;
actualToken._tokenKey = token2key(actualToken, mergedSalt);

const themeKey = cssVar.key;
recordCleanToken(themeKey);

const hashId = `${hashPrefix}-${hash(mergedSalt)}`;

return [tokenWithCssVar, hashId, actualToken, cssVarsStr, cssVar.key];
recordCleanToken(hashId);
return [tokenWithCssVar, hashCls, actualToken, cssVarsStr, cssVar.key];
},
([, , , , themeKey]) => {
// Remove token will remove all related style
Expand Down
Loading