feat: css var (#45589)

* feat: css variables theme

* chore: temp

* chore temp

* chore: temp

* chore: temp

* chore: tmp

* chore: temp

* feat: full css variables

* feat: css var

* chore: code clean

* chore: code clean

* chore: bump cssinjs

* test: fix lint

* feat: better key logic

* feat: useStyle add param rootCls for cssVar scope

* chore: fix lint

* chore: code clean

* chore: fix lint

* perf: minimize component token size

* chore: make useId compatible

* chore: code clean

* chore: fix lint

* chore: code clean

* chore: update test case

* feat: genCSSVarRegister

* feat: RPN Calculator

* chore: add test for css var

* chore: code clean

* test: add test for calc

* feat: better calc type

* chore: code clean

* chore: update size limit

* feat: better useCSSVar

* chore: better useCSSVar

* test: add cov

* feat: better calc logic

* test: add test case

* chore: code clean

---------

Signed-off-by: MadCcc <madccc@foxmail.com>
This commit is contained in:
MadCcc 2023-11-06 10:31:51 +08:00 committed by GitHub
parent 99138cb93e
commit 5f1dd427df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 867 additions and 308 deletions

View File

@ -5,7 +5,7 @@ import stackblitzSdk from '@stackblitz/sdk';
import { Alert, Badge, Space, Tooltip } from 'antd';
import { createStyles, css } from 'antd-style';
import classNames from 'classnames';
import { FormattedMessage, useSiteData, LiveContext } from 'dumi';
import { FormattedMessage, LiveContext, useSiteData } from 'dumi';
import LZString from 'lz-string';
import type { AntdPreviewerProps } from './Previewer';

View File

@ -6,7 +6,7 @@ import DayJS from 'dayjs';
import { FormattedMessage, useIntl, useRouteMeta, useTabMeta } from 'dumi';
import type { ReactNode } from 'react';
import React, { useContext, useLayoutEffect, useMemo, useState } from 'react';
import { Anchor, Avatar, Col, Skeleton, Space, Tooltip, Typography } from 'antd';
import { Anchor, Avatar, Col, ConfigProvider, Skeleton, Space, Tooltip, Typography } from 'antd';
import useLayoutState from '../../../hooks/useLayoutState';
import useLocation from '../../../hooks/useLocation';
import EditButton from '../../common/EditButton';
@ -275,7 +275,9 @@ const Content: React.FC<{ children: ReactNode }> = ({ children }) => {
</Typography.Paragraph>
) : null}
{!meta.frontmatter.__autoDescription && meta.frontmatter.description}
<div style={{ minHeight: 'calc(100vh - 64px)' }}>{children}</div>
<ConfigProvider theme={{ cssVar: {} }}>
<div style={{ minHeight: 'calc(100vh - 64px)' }}>{children}</div>
</ConfigProvider>
{(meta.frontmatter?.zhihu_url ||
meta.frontmatter?.yuque_url ||
meta.frontmatter?.juejin_url) && (

View File

@ -62,7 +62,7 @@ const validateBreakpoints = (token: GlobalToken) => {
};
export default function useResponsiveObserver() {
const [, token] = useToken();
const [, , , token] = useToken();
const responsiveMap: BreakpointMap = getResponsiveMap(validateBreakpoints(token));
// To avoid repeat create instance, we add `useMemo` here.

View File

@ -26,6 +26,7 @@ import IconWrapper from './IconWrapper';
import LoadingIcon from './LoadingIcon';
import useStyle from './style';
import CompactCmp from './style/compactCmp';
import useCSSVar from './style/cssVar';
export type LegacyButtonType = ButtonType | 'danger';
@ -118,7 +119,8 @@ const InternalButton: React.ForwardRefRenderFunction<
const { getPrefixCls, autoInsertSpaceInButton, direction, button } = useContext(ConfigContext);
const prefixCls = getPrefixCls('btn', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);
const [, hashId] = useStyle(prefixCls);
const wrapCSSVar = useCSSVar(prefixCls);
const disabled = useContext(DisabledContext);
const mergedDisabled = customDisabled ?? disabled;
@ -254,7 +256,7 @@ const InternalButton: React.ForwardRefRenderFunction<
children || children === 0 ? spaceChildren(children, needInserted && autoInsertSpace) : null;
if (linkButtonRestProps.href !== undefined) {
return wrapSSR(
return wrapCSSVar(
<a
{...linkButtonRestProps}
className={classNames(classes, {
@ -296,7 +298,7 @@ const InternalButton: React.ForwardRefRenderFunction<
);
}
return wrapSSR(buttonNode);
return wrapCSSVar(buttonNode);
};
const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>(

View File

@ -0,0 +1,4 @@
import { genCSSVarRegister } from '../../theme/internal';
import { prepareComponentToken } from '.';
export default genCSSVarRegister('Button', prepareComponentToken);

View File

@ -1,9 +1,9 @@
import type { CSSProperties } from 'react';
import { unit } from '@ant-design/cssinjs';
import type { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
import { genFocusStyle } from '../../style';
import type { GlobalToken } from '../../theme';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import type { GenStyleFn } from '../../theme/util/genComponentStyleHook';
import genGroupStyle from './group';
@ -90,6 +90,21 @@ export interface ComponentToken {
* @descEN Horizontal padding of small button
*/
paddingInlineSM: CSSProperties['paddingInline'];
/**
* @desc
* @descEN Horizontal padding of button
*/
paddingBlock: CSSProperties['paddingInline'];
/**
* @desc
* @descEN Horizontal padding of large button
*/
paddingBlockLG: CSSProperties['paddingInline'];
/**
* @desc
* @descEN Horizontal padding of small button
*/
paddingBlockSM: CSSProperties['paddingInline'];
/**
* @desc
* @descEN Icon size of button which only contains icon
@ -139,6 +154,7 @@ export interface ComponentToken {
export interface ButtonToken extends FullToken<'Button'> {
buttonPaddingHorizontal: CSSProperties['paddingInline'];
buttonPaddingVertical: CSSProperties['paddingBlock'];
buttonIconOnlyFontSize: number;
}
@ -156,7 +172,7 @@ const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSS
textAlign: 'center',
backgroundImage: 'none',
backgroundColor: 'transparent',
border: `${token.lineWidth}px ${token.lineType} transparent`,
border: `${unit(token.lineWidth)} ${token.lineType} transparent`,
cursor: 'pointer',
transition: `all ${token.motionDurationMid} ${token.motionEaseInOut}`,
userSelect: 'none',
@ -220,7 +236,7 @@ const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSS
insetInlineStart: -token.lineWidth,
display: 'inline-block',
width: token.lineWidth,
height: `calc(100% + ${token.lineWidth * 2}px)`,
height: `calc(100% + ${unit(token.lineWidth)} * 2)`,
backgroundColor: token.colorPrimaryHover,
content: '""',
},
@ -238,7 +254,7 @@ const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSS
top: -token.lineWidth,
insetInlineStart: -token.lineWidth,
display: 'inline-block',
width: `calc(100% + ${token.lineWidth * 2}px)`,
width: `calc(100% + ${unit(token.lineWidth)} * 2)`,
height: token.lineWidth,
backgroundColor: token.colorPrimaryHover,
content: '""',
@ -271,8 +287,8 @@ const genCircleButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => (
const genRoundButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token) => ({
borderRadius: token.controlHeight,
paddingInlineStart: token.controlHeight / 2,
paddingInlineEnd: token.controlHeight / 2,
paddingInlineStart: token.calc(token.controlHeight).div(2).equal(),
paddingInlineEnd: token.calc(token.controlHeight).div(2).equal(),
});
// =============================== Type ===============================
@ -569,15 +585,12 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
componentCls,
controlHeight,
fontSize,
lineHeight,
lineWidth,
borderRadius,
buttonPaddingHorizontal,
iconCls,
buttonPaddingVertical,
} = token;
const paddingVertical = Math.max(0, (controlHeight - fontSize * lineHeight) / 2 - lineWidth);
const iconOnlyCls = `${componentCls}-icon-only`;
return [
@ -586,7 +599,7 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
[`${componentCls}${sizePrefixCls}`]: {
fontSize,
height: controlHeight,
padding: `${paddingVertical}px ${buttonPaddingHorizontal}px`,
padding: `${unit(buttonPaddingVertical!)} ${unit(buttonPaddingHorizontal!)}`,
borderRadius,
[`&${iconOnlyCls}`]: {
@ -635,7 +648,8 @@ const genSizeSmallButtonStyle: GenerateStyle<ButtonToken> = (token) => {
controlHeight: token.controlHeightSM,
fontSize: token.contentFontSizeSM,
padding: token.paddingXS,
buttonPaddingHorizontal: token.paddingInlineSM, // Fixed padding
buttonPaddingHorizontal: token.paddingInlineSM,
buttonPaddingVertical: token.paddingBlockSM,
borderRadius: token.borderRadiusSM,
buttonIconOnlyFontSize: token.onlyIconSizeSM,
});
@ -648,6 +662,7 @@ const genSizeLargeButtonStyle: GenerateStyle<ButtonToken> = (token) => {
controlHeight: token.controlHeightLG,
fontSize: token.contentFontSizeLG,
buttonPaddingHorizontal: token.paddingInlineLG,
buttonPaddingVertical: token.paddingBlockLG,
borderRadius: token.borderRadiusLG,
buttonIconOnlyFontSize: token.onlyIconSizeLG,
});
@ -670,44 +685,63 @@ const genBlockButtonStyle: GenerateStyle<ButtonToken> = (token) => {
export const prepareToken: (token: Parameters<GenStyleFn<'Button'>>[0]) => ButtonToken = (
token,
) => {
const { paddingInline, onlyIconSize } = token;
const { paddingInline, onlyIconSize, paddingBlock } = token;
const buttonToken = mergeToken<ButtonToken>(token, {
buttonPaddingHorizontal: paddingInline,
buttonPaddingVertical: paddingBlock,
buttonIconOnlyFontSize: onlyIconSize,
});
return buttonToken;
};
export const prepareComponentToken = (token: GlobalToken) => ({
fontWeight: 400,
defaultShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlTmpOutline}`,
primaryShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlOutline}`,
dangerShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`,
primaryColor: token.colorTextLightSolid,
dangerColor: token.colorTextLightSolid,
borderColorDisabled: token.colorBorder,
defaultGhostColor: token.colorBgContainer,
ghostBg: 'transparent',
defaultGhostBorderColor: token.colorBgContainer,
paddingInline: token.paddingContentHorizontal - token.lineWidth,
paddingInlineLG: token.paddingContentHorizontal - token.lineWidth,
paddingInlineSM: 8 - token.lineWidth,
onlyIconSize: token.fontSizeLG,
onlyIconSizeSM: token.fontSizeLG - 2,
onlyIconSizeLG: token.fontSizeLG + 2,
groupBorderColor: token.colorPrimaryHover,
linkHoverBg: 'transparent',
textHoverBg: token.colorBgTextHover,
defaultColor: token.colorText,
defaultBg: token.colorBgContainer,
defaultBorderColor: token.colorBorder,
defaultBorderColorDisabled: token.colorBorder,
contentFontSize: token.fontSize,
contentFontSizeSM: token.fontSize,
contentFontSizeLG: token.fontSizeLG,
});
export const prepareComponentToken: GetDefaultToken<'Button'> = (token) => {
const contentFontSize = token.fontSize;
const contentFontSizeSM = token.fontSize;
const contentFontSizeLG = token.fontSizeLG;
return {
fontWeight: 400,
defaultShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlTmpOutline}`,
primaryShadow: `0 ${token.controlOutlineWidth}px 0 ${token.controlOutline}`,
dangerShadow: `0 ${token.controlOutlineWidth}px 0 ${token.colorErrorOutline}`,
primaryColor: token.colorTextLightSolid,
dangerColor: token.colorTextLightSolid,
borderColorDisabled: token.colorBorder,
defaultGhostColor: token.colorBgContainer,
ghostBg: 'transparent',
defaultGhostBorderColor: token.colorBgContainer,
paddingInline: token.paddingContentHorizontal - token.lineWidth,
paddingInlineLG: token.paddingContentHorizontal - token.lineWidth,
paddingInlineSM: 8 - token.lineWidth,
paddingBlock: Math.max(
(token.controlHeight - contentFontSize * token.lineHeight) / 2 - token.lineWidth,
0,
),
paddingBlockSM: Math.max(
(token.controlHeightSM - contentFontSizeSM * token.lineHeight) / 2 - token.lineWidth,
0,
),
paddingBlockLG: Math.max(
(token.controlHeightLG - contentFontSizeLG * token.lineHeight) / 2 - token.lineWidth,
0,
),
onlyIconSize: token.fontSizeLG,
onlyIconSizeSM: token.fontSizeLG - 2,
onlyIconSizeLG: token.fontSizeLG + 2,
groupBorderColor: token.colorPrimaryHover,
linkHoverBg: 'transparent',
textHoverBg: token.colorBgTextHover,
defaultColor: token.colorText,
defaultBg: token.colorBgContainer,
defaultBorderColor: token.colorBorder,
defaultBorderColorDisabled: token.colorBorder,
contentFontSize,
contentFontSizeSM,
contentFontSizeLG,
};
};
export default genComponentStyleHook(
'Button',

View File

@ -3,9 +3,9 @@ import kebabCase from 'lodash/kebabCase';
import canUseDom from 'rc-util/lib/Dom/canUseDom';
import ConfigProvider from '..';
import { InputNumber } from '../..';
import { resetWarned } from '../../_util/warning';
import { InputNumber, Button } from '../..';
import { render } from '../../../tests/utils';
import { resetWarned } from '../../_util/warning';
import theme from '../../theme';
import { useToken } from '../../theme/internal';
@ -197,4 +197,53 @@ describe('ConfigProvider.Theme', () => {
);
expect(tokenRef?.colorPrimaryText).toBe('#1677ff');
});
describe('cssVar', () => {
it('should work', () => {
const { container } = render(
<ConfigProvider theme={{ cssVar: { key: 'foo' } }}>
<Button>Button</Button>
</ConfigProvider>,
);
const button = container.querySelector('button')!;
expect(button).toHaveClass('foo');
expect(button).toHaveStyle({
'--antd-color-text': 'rgba(0, 0, 0, 0.88)',
boxShadow: 'var(--antd-button-default-shadow)',
'line-height': 'var(--antd-line-height)',
});
});
it('prefix', () => {
const { container } = render(
<>
<ConfigProvider theme={{ cssVar: { key: 'foo' }, hashed: true }}>
<Button className="button-foo">Button</Button>
</ConfigProvider>
<ConfigProvider theme={{ cssVar: { key: 'bar', prefix: 'bar' }, hashed: true }}>
<Button className="button-bar">Button</Button>
</ConfigProvider>
</>,
);
const fooBtn = container.querySelector('.button-foo')!;
const barBtn = container.querySelector('.button-bar')!;
expect(fooBtn).toHaveClass('foo');
expect(fooBtn).toHaveStyle({
'--antd-color-text': 'rgba(0, 0, 0, 0.88)',
boxShadow: 'var(--antd-button-default-shadow)',
'line-height': 'var(--antd-line-height)',
});
expect(barBtn).toHaveClass('bar');
expect(barBtn).toHaveStyle({
'--bar-color-text': 'rgba(0, 0, 0, 0.88)',
boxShadow: 'var(--bar-button-default-shadow)',
'line-height': 'var(--bar-line-height)',
});
});
});
});

View File

@ -47,6 +47,16 @@ export interface ThemeConfig {
algorithm?: MappingAlgorithm | MappingAlgorithm[];
hashed?: boolean;
inherit?: boolean;
cssVar?: {
/**
* Prefix for css variable, default to `antd`.
*/
prefix?: string;
/**
* Unique key for theme, should be set manually < react@18.
*/
key?: string;
};
}
export interface ComponentStyleConfig {

View File

@ -3,15 +3,31 @@ import isEqual from 'rc-util/lib/isEqual';
import type { OverrideToken } from '../../theme/interface';
import type { ThemeConfig } from '../context';
import { defaultConfig } from '../../theme/internal';
import useThemeKey from './useThemeKey';
import { devUseWarning } from '../../_util/warning';
export default function useTheme(
theme?: ThemeConfig,
parentTheme?: ThemeConfig,
): ThemeConfig | undefined {
const warning = devUseWarning('ConfigProvider');
const themeConfig = theme || {};
const parentThemeConfig: ThemeConfig =
themeConfig.inherit === false || !parentTheme ? defaultConfig : parentTheme;
const themeKey = useThemeKey();
if (process.env.NODE_ENV !== 'production') {
const cssVarEnabled = themeConfig.cssVar || parentThemeConfig.cssVar;
const validKey = !!(themeConfig.cssVar?.key || themeKey);
warning(
!cssVarEnabled || validKey,
'breaking',
'Missing key in `cssVar` config. Please upgrade to React 18 or set `cssVar.key` manually in each ConfigProvider inside `cssVar` enabled ConfigProvider.',
);
}
return useMemo<ThemeConfig | undefined>(
() => {
if (!theme) {
@ -30,6 +46,14 @@ export default function useTheme(
} as any;
});
const cssVarKey = `css-var-${themeKey.replace(/:/g, '')}`;
const mergedCssVar = (themeConfig.cssVar || parentThemeConfig.cssVar) && {
prefix: 'antd', // Default to antd
...parentThemeConfig.cssVar,
...themeConfig.cssVar,
key: themeConfig.cssVar?.key || cssVarKey,
};
// Base token
return {
...parentThemeConfig,
@ -40,6 +64,7 @@ export default function useTheme(
...themeConfig.token,
},
components: mergedComponents,
cssVar: mergedCssVar,
};
},
[themeConfig, parentThemeConfig],

View File

@ -0,0 +1,7 @@
import { useId } from 'react';
const useEmptyId = () => '';
const useThemeKey = typeof useId === 'undefined' ? useEmptyId : useId;
export default useThemeKey;

View File

@ -5,7 +5,7 @@ import type { DrawerClassNames, DrawerStyles } from '../DrawerPanel';
const useStyle = createStyles(({ token }) => ({
'my-drawer-body': {
background: token['blue-1'],
background: token.blue1,
},
'my-drawer-mask': {
boxShadow: `inset 0 0 15px #fff`,

View File

@ -4,7 +4,7 @@ import { createStyles, useTheme } from 'antd-style';
const useStyle = createStyles(({ token }) => ({
'my-modal-body': {
background: token['blue-1'],
background: token.blue1,
padding: token.paddingSM,
},
'my-modal-mask': {

View File

@ -25,6 +25,7 @@ describe('Theme', () => {
);
delete token._hashId;
delete token._tokenKey;
delete token._themeKey;
return token;
};

View File

@ -1,4 +1,6 @@
import getAlphaColor from '../util/getAlphaColor';
import genCalc from '../util/calc';
import type AbstractCalculator from 'antd/es/theme/util/calc/calculator';
describe('util', () => {
describe('getAlphaColor', () => {
@ -6,4 +8,129 @@ describe('util', () => {
expect(getAlphaColor('rgba(0, 0, 0, 0.5)', 'rgba(255, 255, 255)')).toBe('rgba(0, 0, 0, 0.5)');
});
});
describe('calculator', () => {
const cases: [
(calc: (num: number | AbstractCalculator) => AbstractCalculator) => string | number,
{ js: number; css: string },
][] = [
[
// 1 + 1
(calc) => calc(1).add(1).equal(),
{
js: 2,
css: 'calc(1px + 1px)',
},
],
[
// (1 + 1) * 4
(calc) => calc(1).add(1).mul(4).equal(),
{
js: 8,
css: 'calc((1px + 1px) * 4)',
},
],
[
// (2 + 4) / 2 - 2
(calc) => calc(2).add(4).div(2).sub(2).equal(),
{
js: 1,
css: 'calc((2px + 4px) / 2 - 2px)',
},
],
[
// Bad case
// (2 + 4) / (3 - 2) - 2
(calc) => calc(2).add(4).div(calc(3).sub(2)).sub(2).equal(),
{
js: 4,
css: 'calc((2px + 4px) / (3px - 2px) - 2px)',
},
],
[
// Bad case
// 2 * (2 + 3)
(calc) => calc(2).mul(calc(2).add(3)).equal(),
{
js: 10,
css: 'calc(2px * (2px + 3px))',
},
],
[
// (1 + 2) * 3
(calc) => calc(calc(1).add(2)).mul(3).equal(),
{
js: 9,
css: 'calc((1px + 2px) * 3)',
},
],
[
// 1 + (2 - 1)
(calc) => calc(1).add(calc(2).sub(1)).equal(),
{
js: 2,
css: 'calc(1px + (2px - 1px))',
},
],
[
// 1 + 2 * 2
(calc) => calc(1).add(calc(2).mul(2)).equal(),
{
js: 5,
css: 'calc(1px + 2px * 2)',
},
],
[
// 5 - (2 - 1)
(calc) => calc(5).sub(calc(2).sub(1)).equal(),
{
js: 4,
css: 'calc(5px - (2px - 1px))',
},
],
[
// 2 * 6 / 3
(calc) => calc(2).mul(6).div(3).equal(),
{
js: 4,
css: 'calc(2px * 6 / 3)',
},
],
[
// 6 / 3 * 2
(calc) => calc(6).div(3).mul(2).equal(),
{
js: 4,
css: 'calc(6px / 3 * 2)',
},
],
[
// Bad case
// 6 / (3 * 2)
(calc) => calc(6).div(calc(3).mul(2)).equal(),
{
js: 1,
css: 'calc(6px / (3px * 2))',
},
],
[
// 6
(calc) => calc(6).equal(),
{
js: 6,
css: '6px',
},
],
];
cases.forEach(([exp, { js, css }], index) => {
it(`js calc ${index + 1}`, () => {
expect(exp(genCalc('js'))).toBe(js);
});
it(`css calc ${index + 1}`, () => {
expect(exp(genCalc('css'))).toBe(css);
});
});
});
});

View File

@ -29,6 +29,10 @@ export interface DesignTokenProviderProps {
/** Just merge `token` & `override` at top to save perf */
override: { override: Partial<AliasToken> } & ComponentsToken;
hashed?: string | boolean;
cssVar?: {
prefix?: string;
key?: string;
};
}
export const DesignTokenContext = React.createContext<DesignTokenProviderProps>(defaultConfig);

View File

@ -28,7 +28,6 @@ export type {
export { PresetColors } from './presetColors';
export type {
ColorPalettes,
LegacyColorPalettes,
PresetColorKey,
PresetColorType,
} from './presetColors';

View File

@ -1,4 +1,4 @@
import type { ColorPalettes, LegacyColorPalettes } from '../presetColors';
import type { ColorPalettes } from '../presetColors';
import type { SeedToken } from '../seeds';
import type { ColorMapToken } from './colors';
import type { FontMapToken } from './font';
@ -36,7 +36,6 @@ export interface CommonMapToken extends StyleMapToken {
export interface MapToken
extends SeedToken,
LegacyColorPalettes,
ColorPalettes,
ColorMapToken,
SizeMapToken,

View File

@ -20,13 +20,6 @@ export type PresetColorType = Record<PresetColorKey, string>;
type ColorPaletteKeyIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
export type LegacyColorPalettes = {
/**
* @deprecated
*/
[key in `${keyof PresetColorType}-${ColorPaletteKeyIndex}`]: string;
};
export type ColorPalettes = {
[key in `${keyof PresetColorType}${ColorPaletteKeyIndex}`]: string;
};

View File

@ -10,20 +10,26 @@ import type {
} from './interface';
import { PresetColors } from './interface';
import useToken from './useToken';
import type { FullToken } from './util/genComponentStyleHook';
import genComponentStyleHook, { genSubStyleComponent } from './util/genComponentStyleHook';
import type { FullToken, GetDefaultToken } from './util/genComponentStyleHook';
import genComponentStyleHook, {
genSubStyleComponent,
genCSSVarRegister,
} from './util/genComponentStyleHook';
import genPresetColor from './util/genPresetColor';
import statisticToken, { merge as mergeToken } from './util/statistic';
import useResetIconStyle from './util/useResetIconStyle';
import calc from './util/calc';
export { DesignTokenContext, defaultConfig } from './context';
export {
PresetColors,
genComponentStyleHook,
genSubStyleComponent,
genCSSVarRegister,
genPresetColor,
mergeToken,
statisticToken,
calc,
// hooks
useResetIconStyle,
useStyleRegister,
@ -39,4 +45,5 @@ export type {
PresetColorType,
SeedToken,
UseComponentStyleResult,
GetDefaultToken,
};

View File

@ -1,12 +1,6 @@
import { generate } from '@ant-design/colors';
import type { DerivativeFunc } from '@ant-design/cssinjs';
import type {
ColorPalettes,
LegacyColorPalettes,
MapToken,
PresetColorType,
SeedToken,
} from '../../interface';
import type { ColorPalettes, MapToken, PresetColorType, SeedToken } from '../../interface';
import { defaultPresetColors } from '../seed';
import genColorMapToken from '../shared/genColorMapToken';
import { generateColorPalettes, generateNeutralColorPalettes } from './colors';
@ -18,7 +12,6 @@ const derivative: DerivativeFunc<SeedToken, MapToken> = (token, mapToken) => {
const colors = generate(token[colorKey], { theme: 'dark' });
return new Array(10).fill(1).reduce((prev, _, i) => {
prev[`${colorKey}-${i + 1}`] = colors[i];
prev[`${colorKey}${i + 1}`] = colors[i];
return prev;
}, {}) as ColorPalettes;
@ -29,7 +22,7 @@ const derivative: DerivativeFunc<SeedToken, MapToken> = (token, mapToken) => {
...cur,
};
return prev;
}, {} as ColorPalettes & LegacyColorPalettes);
}, {} as ColorPalettes);
const mergedMapToken = mapToken ?? defaultAlgorithm(token);

View File

@ -1,13 +1,7 @@
import { generate } from '@ant-design/colors';
import genControlHeight from '../shared/genControlHeight';
import genSizeMapToken from '../shared/genSizeMapToken';
import type {
ColorPalettes,
LegacyColorPalettes,
MapToken,
PresetColorType,
SeedToken,
} from '../../interface';
import type { ColorPalettes, MapToken, PresetColorType, SeedToken } from '../../interface';
import { defaultPresetColors } from '../seed';
import genColorMapToken from '../shared/genColorMapToken';
import genCommonMapToken from '../shared/genCommonMapToken';
@ -20,10 +14,9 @@ export default function derivative(token: SeedToken): MapToken {
const colors = generate(token[colorKey]);
return new Array(10).fill(1).reduce((prev, _, i) => {
prev[`${colorKey}-${i + 1}`] = colors[i];
prev[`${colorKey}${i + 1}`] = colors[i];
return prev;
}, {}) as ColorPalettes & LegacyColorPalettes;
}, {}) as ColorPalettes;
})
.reduce((prev, cur) => {
prev = {
@ -31,7 +24,7 @@ export default function derivative(token: SeedToken): MapToken {
...cur,
};
return prev;
}, {} as ColorPalettes & LegacyColorPalettes);
}, {} as ColorPalettes);
return {
...token,

View File

@ -9,6 +9,41 @@ import type { AliasToken, GlobalToken, MapToken, SeedToken } from './interface';
import defaultSeedToken from './themes/seed';
import formatToken from './util/alias';
export const unitless: {
[key in keyof AliasToken]?: boolean;
} = {
lineHeight: true,
lineHeightSM: true,
lineHeightLG: true,
lineHeightHeading1: true,
lineHeightHeading2: true,
lineHeightHeading3: true,
lineHeightHeading4: true,
lineHeightHeading5: true,
opacityLoading: true,
fontWeightStrong: true,
zIndexPopupBase: true,
zIndexBase: true,
};
export const ignore: {
[key in keyof AliasToken]?: boolean;
} = {
size: true,
sizeSM: true,
sizeLG: true,
sizeMD: true,
sizeXS: true,
sizeXXS: true,
sizeMS: true,
sizeXL: true,
sizeXXL: true,
sizeUnit: true,
sizeStep: true,
motionBase: true,
motionUnit: true,
};
export const getComputedToken = (
originToken: SeedToken,
overrideToken: DesignTokenProviderProps['components'] & {
@ -57,14 +92,22 @@ export default function useToken(): [
theme: Theme<SeedToken, MapToken>,
token: GlobalToken,
hashId: string,
realToken: GlobalToken,
cssVar?: DesignTokenProviderProps['cssVar'],
] {
const { token: rootDesignToken, hashed, theme, override } = React.useContext(DesignTokenContext);
const {
token: rootDesignToken,
hashed,
theme,
override,
cssVar,
} = React.useContext(DesignTokenContext);
const salt = `${version}-${hashed || ''}`;
const mergedTheme = theme || defaultTheme;
const [token, hashId] = useCacheToken<GlobalToken, SeedToken>(
const [token, hashId, realToken] = useCacheToken<GlobalToken, SeedToken>(
mergedTheme,
[defaultSeedToken, rootDesignToken],
{
@ -74,8 +117,14 @@ export default function useToken(): [
// formatToken will not be consumed after 1.15.0 with getComputedToken.
// But token will break if @ant-design/cssinjs is under 1.15.0 without it
formatToken,
cssVar: cssVar && {
prefix: cssVar.prefix,
key: cssVar.key,
unitless,
ignore,
},
},
);
return [mergedTheme, token, hashed ? hashId : ''];
return [mergedTheme, token, hashed ? hashId : '', realToken, cssVar];
}

View File

@ -0,0 +1,74 @@
import AbstractCalculator from './calculator';
import { unit } from '@ant-design/cssinjs';
export default class CSSCalculator extends AbstractCalculator {
result: string = '';
lowPriority?: boolean;
constructor(num: number | AbstractCalculator) {
super();
if (num instanceof CSSCalculator) {
this.result = `(${num.result})`;
} else if (typeof num === 'number') {
this.result = unit(num);
}
}
add(num: number | AbstractCalculator): this {
if (num instanceof CSSCalculator) {
this.result = `${this.result} + ${num.getResult()}`;
} else if (typeof num === 'number') {
this.result = `${this.result} + ${unit(num)}`;
}
this.lowPriority = true;
return this;
}
sub(num: number | AbstractCalculator): this {
if (num instanceof CSSCalculator) {
this.result = `${this.result} - ${num.getResult()}`;
} else if (typeof num === 'number') {
this.result = `${this.result} - ${unit(num)}`;
}
this.lowPriority = true;
return this;
}
mul(num: number | AbstractCalculator): this {
if (this.lowPriority) {
this.result = `(${this.result})`;
}
if (num instanceof CSSCalculator) {
this.result = `${this.result} * ${num.getResult(true)}`;
} else if (typeof num === 'number') {
this.result = `${this.result} * ${num}`;
}
this.lowPriority = false;
return this;
}
div(num: number | AbstractCalculator): this {
if (this.lowPriority) {
this.result = `(${this.result})`;
}
if (num instanceof CSSCalculator) {
this.result = `${this.result} / ${num.getResult(true)}`;
} else if (typeof num === 'number') {
this.result = `${this.result} / ${num}`;
}
this.lowPriority = false;
return this;
}
getResult(force?: boolean): string {
return this.lowPriority || force ? `(${this.result})` : this.result;
}
equal(): string {
if (typeof this.lowPriority !== 'undefined') {
return `calc(${this.result})`;
}
return this.result;
}
}

View File

@ -0,0 +1,54 @@
import AbstractCalculator from './calculator';
export default class NumCalculator extends AbstractCalculator {
result: number = 0;
constructor(num: number | string | AbstractCalculator) {
super();
if (num instanceof NumCalculator) {
this.result = num.result;
} else if (typeof num === 'number') {
this.result = num;
}
}
add(num: number | AbstractCalculator): this {
if (num instanceof NumCalculator) {
this.result += num.result;
} else if (typeof num === 'number') {
this.result += num;
}
return this;
}
sub(num: number | AbstractCalculator): this {
if (num instanceof NumCalculator) {
this.result -= num.result;
} else if (typeof num === 'number') {
this.result -= num;
}
return this;
}
mul(num: number | AbstractCalculator): this {
if (num instanceof NumCalculator) {
this.result *= num.result;
} else if (typeof num === 'number') {
this.result *= num;
}
return this;
}
div(num: number | AbstractCalculator): this {
if (num instanceof NumCalculator) {
this.result /= num.result;
} else if (typeof num === 'number') {
this.result /= num;
}
return this;
}
equal(): number {
return this.result;
}
}

View File

@ -0,0 +1,11 @@
export default abstract class AbstractCalculator {
abstract add(num: number | AbstractCalculator): this;
abstract sub(num: number | AbstractCalculator): this;
abstract mul(num: number | AbstractCalculator): this;
abstract div(num: number | AbstractCalculator): this;
abstract equal(): string | number;
}

View File

@ -0,0 +1,11 @@
import NumCalculator from './NumCalculator';
import CSSCalculator from './CSSCalculator';
import type AbstractCalculator from './calculator';
const genCalc = (type: 'css' | 'js') => {
const Calculator = type === 'css' ? CSSCalculator : NumCalculator;
return (num: number | AbstractCalculator) => new Calculator(num);
};
export default genCalc;

View File

@ -1,216 +0,0 @@
/* eslint-disable no-redeclare */
import { useContext, type ComponentType } from 'react';
import type { CSSInterpolation } from '@ant-design/cssinjs';
import { useStyleRegister } from '@ant-design/cssinjs';
import { warning } from 'rc-util';
import { ConfigContext } from '../../config-provider/context';
import { genCommonStyle, genLinkStyle } from '../../style';
import type {
ComponentTokenMap,
GlobalToken,
OverrideToken,
UseComponentStyleResult,
} from '../interface';
import useToken from '../useToken';
import statisticToken, { merge as mergeToken } from './statistic';
import useResetIconStyle from './useResetIconStyle';
export type OverrideTokenWithoutDerivative = ComponentTokenMap;
export type OverrideComponent = keyof OverrideTokenWithoutDerivative;
export type GlobalTokenWithComponent<ComponentName extends OverrideComponent> = GlobalToken &
ComponentTokenMap[ComponentName];
type ComponentToken<ComponentName extends OverrideComponent> = Exclude<
OverrideToken[ComponentName],
undefined
>;
type ComponentTokenKey<ComponentName extends OverrideComponent> =
keyof ComponentToken<ComponentName>;
export interface StyleInfo<ComponentName extends OverrideComponent> {
hashId: string;
prefixCls: string;
rootPrefixCls: string;
iconPrefixCls: string;
overrideComponentToken: ComponentTokenMap[ComponentName];
}
export type TokenWithCommonCls<T> = T & {
/** Wrap component class with `.` prefix */
componentCls: string;
/** Origin prefix which do not have `.` prefix */
prefixCls: string;
/** Wrap icon class with `.` prefix */
iconCls: string;
/** Wrap ant prefixCls class with `.` prefix */
antCls: string;
};
export type FullToken<ComponentName extends OverrideComponent> = TokenWithCommonCls<
GlobalTokenWithComponent<ComponentName>
>;
export type GenStyleFn<ComponentName extends OverrideComponent> = (
token: FullToken<ComponentName>,
info: StyleInfo<ComponentName>,
) => CSSInterpolation;
export default function genComponentStyleHook<ComponentName extends OverrideComponent>(
componentName: ComponentName | [ComponentName, string],
styleFn: GenStyleFn<ComponentName>,
getDefaultToken?:
| null
| OverrideTokenWithoutDerivative[ComponentName]
| ((token: GlobalToken) => OverrideTokenWithoutDerivative[ComponentName]),
options: {
resetStyle?: boolean;
// Deprecated token key map [["oldTokenKey", "newTokenKey"], ["oldTokenKey", "newTokenKey"]]
deprecatedTokens?: [ComponentTokenKey<ComponentName>, ComponentTokenKey<ComponentName>][];
/**
* Only use component style in client side. Ignore in SSR.
*/
clientOnly?: boolean;
/**
* Set order of component style. Default is -999.
*/
order?: number;
} = {},
) {
const cells = (Array.isArray(componentName) ? componentName : [componentName, componentName]) as [
ComponentName,
string,
];
const [component] = cells;
const concatComponent = cells.join('-');
return (prefixCls: string): UseComponentStyleResult => {
const [theme, token, hashId] = useToken();
const { getPrefixCls, iconPrefixCls, csp } = useContext(ConfigContext);
const rootPrefixCls = getPrefixCls();
// Shared config
const sharedConfig: Omit<Parameters<typeof useStyleRegister>[0], 'path'> = {
theme,
token,
hashId,
nonce: () => csp?.nonce!,
clientOnly: options.clientOnly,
// antd is always at top of styles
order: options.order || -999,
};
// Generate style for all a tags in antd component.
useStyleRegister(
{ ...sharedConfig, clientOnly: false, path: ['Shared', rootPrefixCls] },
() => [
{
// Link
'&': genLinkStyle(token),
},
],
);
// Generate style for icons
useResetIconStyle(iconPrefixCls, csp);
return [
useStyleRegister(
{ ...sharedConfig, path: [concatComponent, prefixCls, iconPrefixCls] },
() => {
const { token: proxyToken, flush } = statisticToken(token);
const customComponentToken = { ...(token[component] as ComponentToken<ComponentName>) };
if (options.deprecatedTokens) {
const { deprecatedTokens } = options;
deprecatedTokens.forEach(([oldTokenKey, newTokenKey]) => {
if (process.env.NODE_ENV !== 'production') {
warning(
!customComponentToken?.[oldTokenKey],
`The token '${String(oldTokenKey)}' of ${component} had deprecated, use '${String(
newTokenKey,
)}' instead.`,
);
}
// Should wrap with `if` clause, or there will be `undefined` in object.
if (customComponentToken?.[oldTokenKey] || customComponentToken?.[newTokenKey]) {
customComponentToken[newTokenKey] ??= customComponentToken?.[oldTokenKey];
}
});
}
const defaultComponentToken =
typeof getDefaultToken === 'function'
? getDefaultToken(mergeToken(proxyToken, customComponentToken ?? {}))
: getDefaultToken;
const mergedComponentToken = { ...defaultComponentToken, ...customComponentToken };
const componentCls = `.${prefixCls}`;
const mergedToken = mergeToken<
TokenWithCommonCls<GlobalTokenWithComponent<OverrideComponent>>
>(
proxyToken,
{
componentCls,
prefixCls,
iconCls: `.${iconPrefixCls}`,
antCls: `.${rootPrefixCls}`,
},
mergedComponentToken,
);
const styleInterpolation = styleFn(mergedToken as unknown as FullToken<ComponentName>, {
hashId,
prefixCls,
rootPrefixCls,
iconPrefixCls,
overrideComponentToken: customComponentToken as any,
});
flush(component, mergedComponentToken);
return [
options.resetStyle === false ? null : genCommonStyle(token, prefixCls),
styleInterpolation,
];
},
),
hashId,
];
};
}
export interface SubStyleComponentProps {
prefixCls: string;
}
// Get from second argument
type RestParameters<T extends any[]> = T extends [any, ...infer Rest] ? Rest : never;
export const genSubStyleComponent: <ComponentName extends OverrideComponent>(
componentName: [ComponentName, string],
...args: RestParameters<Parameters<typeof genComponentStyleHook<ComponentName>>>
) => ComponentType<SubStyleComponentProps> = (componentName, styleFn, getDefaultToken, options) => {
const useStyle = genComponentStyleHook(componentName, styleFn, getDefaultToken, {
resetStyle: false,
// Sub Style should default after root one
order: -998,
...options,
});
const StyledComponent: ComponentType<SubStyleComponentProps> = ({
prefixCls,
}: SubStyleComponentProps) => {
useStyle(prefixCls);
return null;
};
if (process.env.NODE_ENV !== 'production') {
StyledComponent.displayName = `SubStyle_${
Array.isArray(componentName) ? componentName.join('.') : componentName
}`;
}
return StyledComponent;
};

View File

@ -0,0 +1,327 @@
/* eslint-disable no-redeclare */
import type { ComponentType, FC, ReactElement } from 'react';
import React, { useContext } from 'react';
import type { CSSInterpolation } from '@ant-design/cssinjs';
import { token2CSSVar, useCSSVarRegister, useStyleRegister } from '@ant-design/cssinjs';
import { warning } from 'rc-util';
import { ConfigContext } from '../../config-provider/context';
import { genCommonStyle, genLinkStyle } from '../../style';
import type {
ComponentTokenMap,
GlobalToken,
OverrideToken,
UseComponentStyleResult,
} from '../interface';
import useToken, { ignore, unitless } from '../useToken';
import statisticToken, { merge as mergeToken } from './statistic';
import useResetIconStyle from './useResetIconStyle';
import genCalc from './calc';
import type AbstractCalculator from './calc/calculator';
import classNames from 'classnames';
export type OverrideTokenWithoutDerivative = ComponentTokenMap;
export type OverrideComponent = keyof OverrideTokenWithoutDerivative;
export type GlobalTokenWithComponent<C extends OverrideComponent> = GlobalToken &
ComponentTokenMap[C];
type ComponentToken<C extends OverrideComponent> = Exclude<OverrideToken[C], undefined>;
type ComponentTokenKey<C extends OverrideComponent> = keyof ComponentToken<C>;
export interface StyleInfo {
hashId: string;
prefixCls: string;
rootPrefixCls: string;
iconPrefixCls: string;
}
export type CSSUtil = {
calc: (number: any) => AbstractCalculator;
};
export type TokenWithCommonCls<T> = T & {
/** Wrap component class with `.` prefix */
componentCls: string;
/** Origin prefix which do not have `.` prefix */
prefixCls: string;
/** Wrap icon class with `.` prefix */
iconCls: string;
/** Wrap ant prefixCls class with `.` prefix */
antCls: string;
} & CSSUtil;
export type FullToken<C extends OverrideComponent> = TokenWithCommonCls<
GlobalTokenWithComponent<C>
>;
export type GenStyleFn<C extends OverrideComponent> = (
token: FullToken<C>,
info: StyleInfo,
) => CSSInterpolation;
export type GetDefaultToken<C extends OverrideComponent> =
| null
| OverrideTokenWithoutDerivative[C]
| ((token: GlobalToken) => OverrideTokenWithoutDerivative[C]);
const getDefaultComponentToken = <C extends OverrideComponent>(
component: C,
token: GlobalToken,
getDefaultToken: GetDefaultToken<C>,
) => {
if (typeof getDefaultToken === 'function') {
return getDefaultToken(mergeToken<GlobalToken>(token, token[component] ?? {}));
}
return getDefaultToken ?? {};
};
const getComponentToken = <C extends OverrideComponent>(
component: C,
token: GlobalToken,
defaultToken: OverrideTokenWithoutDerivative[C],
options?: { prefix?: boolean; deprecatedTokens?: [ComponentTokenKey<C>, ComponentTokenKey<C>][] },
) => {
const customToken = { ...(token[component] as ComponentToken<C>) };
if (options?.deprecatedTokens) {
const { deprecatedTokens } = options;
deprecatedTokens.forEach(([oldTokenKey, newTokenKey]) => {
if (process.env.NODE_ENV !== 'production') {
warning(
!customToken?.[oldTokenKey],
`The token '${String(oldTokenKey)}' of ${component} had deprecated, use '${String(
newTokenKey,
)}' instead.`,
);
}
// Should wrap with `if` clause, or there will be `undefined` in object.
if (customToken?.[oldTokenKey] || customToken?.[newTokenKey]) {
customToken[newTokenKey] ??= customToken?.[oldTokenKey];
}
});
}
const mergedToken: any = { ...defaultToken, ...customToken };
// Remove same value as global token to minimize size
Object.keys(mergedToken).forEach((key) => {
if (mergedToken[key] === token[key as keyof GlobalToken]) {
delete mergedToken[key];
}
});
if (options?.prefix && defaultToken) {
// Prefix component token with component name
Object.keys(defaultToken).forEach((key) => {
const newKey = `${component}${key.slice(0, 1).toUpperCase()}${key.slice(1)}`;
mergedToken[newKey] = mergedToken[key];
delete mergedToken[key];
});
}
return mergedToken;
};
export default function genComponentStyleHook<C extends OverrideComponent>(
componentName: C | [C, string],
styleFn: GenStyleFn<C>,
getDefaultToken?:
| null
| OverrideTokenWithoutDerivative[C]
| ((token: GlobalToken) => OverrideTokenWithoutDerivative[C]),
options: {
resetStyle?: boolean;
// Deprecated token key map [["oldTokenKey", "newTokenKey"], ["oldTokenKey", "newTokenKey"]]
deprecatedTokens?: [ComponentTokenKey<C>, ComponentTokenKey<C>][];
/**
* Only use component style in client side. Ignore in SSR.
*/
clientOnly?: boolean;
/**
* Set order of component style. Default is -999.
*/
order?: number;
} = {},
) {
const cells = (Array.isArray(componentName) ? componentName : [componentName, componentName]) as [
C,
string,
];
const [component] = cells;
const concatComponent = cells.join('-');
return (prefixCls: string): UseComponentStyleResult => {
const [theme, token, hashId, realToken, cssVar] = useToken();
const { getPrefixCls, iconPrefixCls, csp } = useContext(ConfigContext);
const rootPrefixCls = getPrefixCls();
const calculator = genCalc(cssVar ? 'css' : 'js');
// Shared config
const sharedConfig: Omit<Parameters<typeof useStyleRegister>[0], 'path'> = {
theme,
token,
hashId,
nonce: () => csp?.nonce!,
clientOnly: options.clientOnly,
// antd is always at top of styles
order: options.order || -999,
};
// Generate style for all a tags in antd component.
useStyleRegister(
{ ...sharedConfig, clientOnly: false, path: ['Shared', rootPrefixCls] },
() => [
{
// Link
'&': genLinkStyle(token),
},
],
);
// Generate style for icons
useResetIconStyle(iconPrefixCls, csp);
const wrapSSR = useStyleRegister(
{ ...sharedConfig, path: [concatComponent, prefixCls, iconPrefixCls] },
() => {
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) {
Object.keys(defaultComponentToken).forEach((key) => {
defaultComponentToken[key] = `var(${token2CSSVar(
key,
`${cssVar?.prefix}-${component}`,
)})`;
});
}
const mergedToken = mergeToken<
TokenWithCommonCls<GlobalTokenWithComponent<OverrideComponent>>
>(
proxyToken,
{
componentCls,
prefixCls,
iconCls: `.${iconPrefixCls}`,
antCls: `.${rootPrefixCls}`,
calc: calculator,
},
cssVar ? defaultComponentToken : componentToken,
);
const styleInterpolation = styleFn(mergedToken as unknown as FullToken<C>, {
hashId,
prefixCls,
rootPrefixCls,
iconPrefixCls,
});
flush(component, componentToken);
return [
options.resetStyle === false ? null : genCommonStyle(token, prefixCls),
styleInterpolation,
];
},
);
return [wrapSSR, classNames(hashId, cssVar?.key)];
};
}
export interface SubStyleComponentProps {
prefixCls: string;
}
// Get from second argument
type RestParameters<T extends any[]> = T extends [any, ...infer Rest] ? Rest : never;
export const genSubStyleComponent: <C extends OverrideComponent>(
componentName: [C, string],
...args: RestParameters<Parameters<typeof genComponentStyleHook<C>>>
) => ComponentType<SubStyleComponentProps> = (componentName, styleFn, getDefaultToken, options) => {
const useStyle = genComponentStyleHook(componentName, styleFn, getDefaultToken, {
resetStyle: false,
// Sub Style should default after root one
order: -998,
...options,
});
const StyledComponent: ComponentType<SubStyleComponentProps> = ({
prefixCls,
}: SubStyleComponentProps) => {
useStyle(prefixCls);
return null;
};
if (process.env.NODE_ENV !== 'production') {
StyledComponent.displayName = `SubStyle_${
Array.isArray(componentName) ? componentName.join('.') : componentName
}`;
}
return StyledComponent;
};
export type CSSVarRegisterProps = {
rootCls: string;
component: string;
cssVar: {
prefix?: string;
key?: string;
};
};
export const genCSSVarRegister = <C extends OverrideComponent>(
component: C,
getDefaultToken: GetDefaultToken<C>,
) => {
const CSSVarRegister: FC<CSSVarRegisterProps> = ({ rootCls, cssVar }) => {
const [, , , realToken] = useToken();
useCSSVarRegister(
{
path: [component],
prefix: cssVar?.prefix,
key: cssVar?.key!,
unitless,
ignore,
token: realToken,
scope: rootCls,
},
() => {
const defaultToken = getDefaultComponentToken(component, realToken, getDefaultToken);
return getComponentToken(component, realToken, defaultToken, {
prefix: true,
});
},
);
return null;
};
const useCSSVar = (rootCls: string) => {
const [, , , , cssVar] = useToken();
return (node: ReactElement): ReactElement =>
cssVar ? (
<>
<CSSVarRegister rootCls={rootCls} cssVar={cssVar} component={component} />
{node}
</>
) : (
node
);
};
return useCSSVar;
};

View File

@ -56,7 +56,7 @@ Raise priority through plugin:
To unify LTR and RTL styles, Ant Design uses CSS logical properties. For example, the original `margin-left` is replaced by `margin-inline-start`, so that it is the starting position spacing under both LTR and RTL. If you need to be compatible with older browsers, you can configure `transformers` through the `StyleProvider` of `@ant-design/cssinjs`:
```tsx
import { StyleProvider, legacyLogicalPropertiesTransformer } from '@ant-design/cssinjs';
import { legacyLogicalPropertiesTransformer, StyleProvider } from '@ant-design/cssinjs';
// `transformers` provides a way to transform CSS properties
export default () => (
@ -83,7 +83,7 @@ When toggled, styles will downgrade CSS logical properties:
In responsive web development, there is a need for a convenient and flexible way to achieve page adaptation and responsive design. The `px2remTransformer` transformer can quickly and accurately convert pixel units in style sheets to rem units relative to the root element (HTML tag), enabling the implementation of adaptive and responsive layouts.
```tsx
import { StyleProvider, px2remTransformer } from '@ant-design/cssinjs';
import { px2remTransformer, StyleProvider } from '@ant-design/cssinjs';
const px2rem = px2remTransformer({
rootValue: 32, // 32px = 1rem; @default 16

View File

@ -113,7 +113,7 @@
],
"dependencies": {
"@ant-design/colors": "^7.0.0",
"@ant-design/cssinjs": "^1.17.2",
"@ant-design/cssinjs": "^2.0.0-alpha.1",
"@ant-design/icons": "^5.2.6",
"@ant-design/react-slick": "~1.0.2",
"@babel/runtime": "^7.18.3",
@ -329,11 +329,11 @@
"size-limit": [
{
"path": "./dist/antd.min.js",
"limit": "401 KiB"
"limit": "405 KiB"
},
{
"path": "./dist/antd-with-locales.min.js",
"limit": "460 KiB"
"limit": "465 KiB"
}
],
"title": "Ant Design",