Skip to content
Open
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
16 changes: 14 additions & 2 deletions docs/demos/ThemeProvider/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import { Space } from 'antd';
import { useTheme } from 'antd-style';
import { createStyles, useTheme } from 'antd-style';
import { FC } from 'react';

interface AppProps {
title: string;
tokenName?: string;
}

const App: FC<AppProps> = ({ title, tokenName }) => {
const useStyle = createStyles(({ cx, cssVar }, { tokenName }: AppProps) => ({
box: {
width: 32,
height: 32,
background: { ...(cssVar as any) }[tokenName!] || 'pink',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The non-null assertion tokenName! is unsafe because tokenName is an optional property. If tokenName is undefined, this will cause a runtime error. A safer approach is to conditionally access the property only when tokenName is defined.

Suggested change
background: { ...(cssVar as any) }[tokenName!] || 'pink',
background: (tokenName && cssVar && (cssVar as any)[tokenName]) || 'pink',

borderRadius: 16,
},
}));

const App: FC<AppProps> = (props) => {
const { title, tokenName } = props;
const token = useTheme();

// @ts-ignore
const tokenColor = tokenName ? token[tokenName] : token.colorPrimary;
const { styles } = useStyle(props);

return (
<Space direction={'vertical'} size={8}>
Expand All @@ -32,6 +43,7 @@ const App: FC<AppProps> = ({ title, tokenName }) => {
borderRadius: 16,
}}
/>
<div className={styles.box} />
<code>{tokenColor || 'None'}</code>
</Space>
<div style={{ fontSize: 12, color: token.colorTextLabel, marginLeft: 8 }}>
Expand Down
4 changes: 2 additions & 2 deletions docs/demos/api/createStyles/default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* defaultShowCode: true
*/
import { SmileOutlined } from '@ant-design/icons';
import { Button, Space } from 'antd';
import { Button, Space, version } from 'antd';
import { createStyles } from 'antd-style';

const useStyles = createStyles(({ cssVar, css, cx }) => {
Expand Down Expand Up @@ -38,7 +38,7 @@ const App = () => {
const { styles } = useStyles();

return (
<div className={styles.container}>
<div className={styles.container} data-antd-ver={version}>
<Space direction={'vertical'} style={{ width: '100%' }} size={16}>
<Space>
<Button title={'功能按钮的说明'} icon={<SmileOutlined />} />
Expand Down
17 changes: 15 additions & 2 deletions src/factories/createThemeProvider/TokenContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { serializeCSS } from '@/core';
import { useAntdTheme, useThemeMode } from '@/hooks';
import { StyledThemeProvider, Theme } from '@/types';

import { useCustomToken } from '@/hooks/useCustomToken';
import type { ThemeProviderProps } from './type';

interface TokenContainerProps<T, S = Record<string, string>>
Expand All @@ -25,7 +26,7 @@ const TokenContainer: <T, S>(props: TokenContainerProps<T, S>) => ReactElement |
}) => {
const themeState = useThemeMode();
const { appearance, isDarkMode } = themeState;
const { stylish: antdStylish, ...token } = useAntdTheme();
const { stylish: antdStylish, cssVar, ...token } = useAntdTheme();

// 获取默认的自定义 token
const defaultCustomToken = useMemo(() => {
Expand All @@ -47,6 +48,13 @@ const TokenContainer: <T, S>(props: TokenContainerProps<T, S>) => ReactElement |
return { ...defaultCustomToken, ...customTokenOrFn };
}, [defaultCustomToken, customTokenOrFn, token, appearance]);

const { token: cssVariableToken, cssVarCls, hashId } = useCustomToken(customToken as any);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Casting customToken to any bypasses type checking. It would be safer to ensure that customToken's type is compatible with the CustomToken type expected by useCustomToken. Consider adding a generic constraint like T extends CustomToken where T is defined, for example in ThemeProviderProps.


const mergedCssVar = useMemo(
() => ({ ...cssVar, ...cssVariableToken }),
[cssVar, cssVariableToken],
);

// 获取 stylish
const customStylish = useMemo(() => {
if (!stylishOrGetStylish) return {};
Expand All @@ -68,12 +76,17 @@ const TokenContainer: <T, S>(props: TokenContainerProps<T, S>) => ReactElement |
const theme: Theme = {
...token,
...(customToken as any),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Casting customToken to any before spreading it into the theme object weakens type safety. The root cause seems to be that the generic T for custom tokens is not constrained. Constraining it to extend CustomToken would improve type safety here and in other places.

cssVar: mergedCssVar,
stylish: stylish as any,
...themeState,
prefixCls,
};

return <StyledThemeProvider theme={theme}>{children}</StyledThemeProvider>;
return (
<StyledThemeProvider theme={theme}>
<div className={[cssVarCls, hashId].filter(Boolean).join(' ')}>{children}</div>
</StyledThemeProvider>
);
};

export default TokenContainer;
35 changes: 35 additions & 0 deletions src/hooks/useCustomToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { CustomToken } from '@/types';
import { useCacheToken } from '@ant-design/cssinjs';
import { useToken as useInternalToken } from 'antd/lib/theme/internal';

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using useToken from antd/lib/theme/internal is risky as it's an internal Ant Design API. It can break without warning in future antd versions. If possible, it would be better to rely on public APIs to ensure long-term stability.

import React from 'react';

function useCssVarConf() {
const [theme, realToken, hashId, token, cssVar, zeroRuntime] = useInternalToken();
const themeKey = React.useId();

const mergeCssVar = {
...cssVar,
key: `antd-style-${cssVar?.key}-${themeKey.replace(/:/g, '')}`,
};

// copied from https://github.com/ant-design/ant-design/blob/4e8d79581e4ec2b4a4cf7abf2e1ee4e939ba1a90/components/theme/util/genStyleUtils.ts#L25-L27
return { theme, realToken, hashId, token, cssVar: mergeCssVar, zeroRuntime };
}

export function useCustomToken(customToken: CustomToken) {
const { theme, cssVar } = useCssVarConf();

const [token, hashId, realToken] = useCacheToken<CustomToken>(theme, [], {
salt: 'antd-style',
override: customToken || {},
getComputedToken: (_, override) => override as any,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Casting override to any weakens type safety. If the types are set up correctly, this cast should not be necessary. It would be better to adjust the types so that override can be returned directly, for instance by ensuring the override parameter's type is a full CustomToken rather than a partial one if that's the case.

cssVar,
});

return {
token,
hashId,
realToken,
cssVarCls: cssVar.key ?? '',
};
}
3 changes: 2 additions & 1 deletion src/types/theme.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MappingAlgorithm, ThemeConfig } from 'antd';
import { AliasToken } from 'antd/es/theme/interface';
import { AliasToken, GlobalToken } from 'antd/es/theme/interface';

import { BrowserPrefers, ThemeAppearance, ThemeMode } from './appearance';

Expand Down Expand Up @@ -66,6 +66,7 @@ export interface FullStylish extends AntdStylish, CustomStylish {}

export interface AntdTheme extends AntdToken {
stylish: AntdStylish;
cssVar: GlobalToken;
}

export interface FullToken extends AntdToken, CustomToken {}
Expand Down
Loading