mirror of
https://github.com/ant-design/ant-design.git
synced 2026-02-09 02:49:18 +08:00
refactor: Provide top theme export and theme adjuster (#36302)
* chore: change theme mode * refactor: export theme * refactor: top of theme * chore: map of token * chore: fix lint * chore: rename * test: fix test case
This commit is contained in:
@@ -67,7 +67,7 @@ Array [
|
||||
"Upload",
|
||||
"message",
|
||||
"notification",
|
||||
"useDesignToken",
|
||||
"theme",
|
||||
"version",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -5,9 +5,11 @@ import ConfigProvider from '..';
|
||||
import { InputNumber } from '../..';
|
||||
import { render } from '../../../tests/utils';
|
||||
import { useToken } from '../../theme';
|
||||
import darkDerivative from '../../theme/themes/dark';
|
||||
import theme from '../../theme/export';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
|
||||
const { darkAlgorithm } = theme;
|
||||
|
||||
let mockCanUseDom = true;
|
||||
|
||||
jest.mock('rc-util/lib/Dom/canUseDom', () => () => mockCanUseDom);
|
||||
@@ -55,7 +57,7 @@ describe('ConfigProvider.Theme', () => {
|
||||
errorSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('derivative should work', () => {
|
||||
it('algorithm should work', () => {
|
||||
let tokenRef: any;
|
||||
const Demo = () => {
|
||||
const [, token] = useToken();
|
||||
@@ -63,7 +65,7 @@ describe('ConfigProvider.Theme', () => {
|
||||
return null;
|
||||
};
|
||||
render(
|
||||
<ConfigProvider theme={{ derivative: darkDerivative }}>
|
||||
<ConfigProvider theme={{ algorithm: darkAlgorithm }}>
|
||||
<Demo />
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ export type DirectionType = 'ltr' | 'rtl' | undefined;
|
||||
export interface ThemeConfig {
|
||||
token?: Partial<SeedToken>;
|
||||
override?: OverrideToken;
|
||||
derivative?: (token: SeedToken) => MapToken;
|
||||
algorithm?: (token: SeedToken) => MapToken;
|
||||
hashed?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Theme as V5Theme } from '@ant-design/cssinjs';
|
||||
import IconContext from '@ant-design/icons/lib/components/Context';
|
||||
import { FormProvider as RcFormProvider } from 'rc-field-form';
|
||||
import type { ValidateMessages } from 'rc-field-form/lib/interface';
|
||||
@@ -248,17 +249,20 @@ const ProviderChildren: React.FC<ProviderChildrenProps> = props => {
|
||||
}
|
||||
|
||||
// ================================ Dynamic theme ================================
|
||||
const memoTheme = React.useMemo(
|
||||
() => ({
|
||||
...mergedTheme,
|
||||
const memoTheme = React.useMemo(() => {
|
||||
const { algorithm, token, ...rest } = mergedTheme || {};
|
||||
const themeObj = algorithm ? new V5Theme(algorithm) : undefined;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
theme: themeObj,
|
||||
|
||||
token: {
|
||||
...defaultSeedToken,
|
||||
...mergedTheme?.token,
|
||||
...token,
|
||||
},
|
||||
}),
|
||||
[mergedTheme],
|
||||
);
|
||||
};
|
||||
}, [mergedTheme]);
|
||||
|
||||
if (theme) {
|
||||
childNode = (
|
||||
|
||||
@@ -148,7 +148,7 @@ export { default as Tabs } from './tabs';
|
||||
export type { TabPaneProps, TabsProps } from './tabs';
|
||||
export { default as Tag } from './tag';
|
||||
export type { TagProps, TagType } from './tag';
|
||||
export { useDesignToken } from './theme/export';
|
||||
export { default as theme } from './theme/export';
|
||||
export { default as TimePicker } from './time-picker';
|
||||
export type { TimePickerProps, TimeRangePickerProps } from './time-picker';
|
||||
export { default as Timeline } from './timeline';
|
||||
|
||||
@@ -2,11 +2,13 @@ import { Theme } from '@ant-design/cssinjs';
|
||||
import * as React from 'react';
|
||||
import { render, renderHook } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import { useDesignToken } from '../export';
|
||||
import theme from '../export';
|
||||
|
||||
const { useToken } = theme;
|
||||
|
||||
describe('Theme', () => {
|
||||
it('useTheme', () => {
|
||||
const result = renderHook(() => useDesignToken());
|
||||
const result = renderHook(() => useToken());
|
||||
|
||||
expect(result.current.theme instanceof Theme).toBeTruthy();
|
||||
expect(result.current.hashId).toBeFalsy();
|
||||
@@ -19,12 +21,12 @@ describe('Theme', () => {
|
||||
|
||||
it('ConfigProvider with seed', () => {
|
||||
const Demo = React.forwardRef((_, ref: any) => {
|
||||
const themeObj = useDesignToken();
|
||||
const themeObj = useToken();
|
||||
ref.current = themeObj;
|
||||
return null;
|
||||
});
|
||||
|
||||
const themeRef = React.createRef<ReturnType<typeof useDesignToken>>();
|
||||
const themeRef = React.createRef<ReturnType<typeof useToken>>();
|
||||
render(
|
||||
<ConfigProvider
|
||||
theme={{
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { useToken } from '.';
|
||||
import { useToken as useInternalToken } from '.';
|
||||
import defaultAlgorithm from './themes/default';
|
||||
import darkAlgorithm from './themes/dark';
|
||||
|
||||
// ZombieJ: We export as object to user but array in internal.
|
||||
// This is used to minimize the bundle size for antd package but safe to refactor as object also.
|
||||
// Please do not export internal `useToken` directly to avoid something export unexpected.
|
||||
/**
|
||||
* Get current context Design Token. Will be different if you using nest theme config.
|
||||
*/
|
||||
export function useDesignToken() {
|
||||
const [theme, token, hashId] = useToken();
|
||||
/** Get current context Design Token. Will be different if you using nest theme config. */
|
||||
function useToken() {
|
||||
const [theme, token, hashId] = useInternalToken();
|
||||
|
||||
return { theme, token, hashId };
|
||||
}
|
||||
|
||||
export default {
|
||||
useToken,
|
||||
defaultAlgorithm,
|
||||
darkAlgorithm,
|
||||
};
|
||||
|
||||
@@ -20,6 +20,8 @@ import genComponentStyleHook from './util/genComponentStyleHook';
|
||||
import getArrowStyle from './util/placementArrow';
|
||||
import statisticToken, { merge as mergeToken, statistic } from './util/statistic';
|
||||
|
||||
const defaultTheme = new Theme(defaultDerivative);
|
||||
|
||||
export {
|
||||
resetComponent,
|
||||
resetIcon,
|
||||
@@ -49,7 +51,7 @@ export type {
|
||||
// ================================ Context =================================
|
||||
export const DesignTokenContext = React.createContext<{
|
||||
token: Partial<SeedToken>;
|
||||
derivative?: (token: SeedToken) => MapToken;
|
||||
theme?: Theme<SeedToken, MapToken>;
|
||||
override?: OverrideToken;
|
||||
hashed?: string | boolean;
|
||||
}>({
|
||||
@@ -63,19 +65,14 @@ const saltPrefix =
|
||||
process.env.NODE_ENV === 'production' ? version : `${version}-${new Date().getHours()}`;
|
||||
|
||||
export function useToken(): [Theme<SeedToken, MapToken>, GlobalToken, string] {
|
||||
const {
|
||||
token: rootDesignToken,
|
||||
override,
|
||||
derivative = defaultDerivative,
|
||||
hashed,
|
||||
} = React.useContext(DesignTokenContext);
|
||||
|
||||
const theme = React.useMemo(() => new Theme(derivative), [derivative]);
|
||||
const { token: rootDesignToken, override, hashed, theme } = React.useContext(DesignTokenContext);
|
||||
|
||||
const salt = `${saltPrefix}-${hashed || ''}`;
|
||||
|
||||
const mergedTheme = theme || defaultTheme;
|
||||
|
||||
const [token, hashId] = useCacheToken<GlobalToken, SeedToken>(
|
||||
theme,
|
||||
mergedTheme,
|
||||
[defaultSeedToken, rootDesignToken],
|
||||
{
|
||||
salt,
|
||||
@@ -84,7 +81,7 @@ export function useToken(): [Theme<SeedToken, MapToken>, GlobalToken, string] {
|
||||
},
|
||||
);
|
||||
|
||||
return [theme, token, hashed ? hashId : ''];
|
||||
return [mergedTheme, token, hashed ? hashId : ''];
|
||||
}
|
||||
|
||||
export type UseComponentStyleResult = [(node: React.ReactNode) => React.ReactElement, string];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable react/no-array-index-key */
|
||||
import type { TableProps } from 'antd';
|
||||
import { Alert, Col, Row, Select, Space, Table, useDesignToken } from 'antd';
|
||||
import { Alert, Col, Row, Select, Space, Table, theme } from 'antd';
|
||||
import * as React from 'react';
|
||||
import { statistic } from '../../../../../components/theme';
|
||||
|
||||
@@ -85,7 +85,7 @@ export default () => {
|
||||
);
|
||||
|
||||
// Full token
|
||||
const { token } = useDesignToken();
|
||||
const { token } = theme.useToken();
|
||||
const tokenList = React.useMemo(
|
||||
() =>
|
||||
Object.keys(token)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BugOutlined, EyeOutlined } from '@ant-design/icons';
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import { Button, Checkbox, Drawer, Form, Input, InputNumber, Space } from 'antd';
|
||||
import { Button, Select, Checkbox, Drawer, Form, Input, InputNumber, Space } from 'antd';
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
@@ -83,6 +83,22 @@ export default function DynamicTheme({
|
||||
onFinish={onFinish}
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item name="theme" label="Theme">
|
||||
<Select
|
||||
dropdownStyle={{ zIndex: 9999999999 }}
|
||||
options={[
|
||||
{
|
||||
label: 'Default',
|
||||
value: 'default',
|
||||
},
|
||||
{
|
||||
label: 'Dark',
|
||||
value: 'dark',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{keys.map((key: keyof typeof defaultToken) => {
|
||||
if (PresetColors.includes(key as any)) {
|
||||
return null;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { presetDarkPalettes, presetPalettes } from '@ant-design/colors';
|
||||
import { createCache, StyleProvider } from '@ant-design/cssinjs';
|
||||
import { setTwoToneColor } from '@ant-design/icons';
|
||||
import { ConfigProvider } from 'antd';
|
||||
import { ConfigProvider, theme as antdTheme } from 'antd';
|
||||
import zhCN from 'antd/lib/locale/zh_CN';
|
||||
import { browserHistory } from 'bisheng/router';
|
||||
import classNames from 'classnames';
|
||||
@@ -90,6 +90,7 @@ export default class Layout extends React.Component {
|
||||
setTheme: this.setTheme,
|
||||
direction: 'ltr',
|
||||
setIframeTheme: this.setIframeTheme,
|
||||
v5theme: 'default',
|
||||
designToken: defaultSeedToken,
|
||||
hashedStyle: true,
|
||||
};
|
||||
@@ -242,6 +243,7 @@ export default class Layout extends React.Component {
|
||||
theme,
|
||||
setTheme,
|
||||
setIframeTheme,
|
||||
v5theme,
|
||||
designToken,
|
||||
hashedStyle,
|
||||
} = this.state;
|
||||
@@ -289,6 +291,8 @@ export default class Layout extends React.Component {
|
||||
theme={{
|
||||
token: designToken,
|
||||
hashed: hashedStyle,
|
||||
algorithm:
|
||||
v5theme === 'dark' ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
|
||||
}}
|
||||
>
|
||||
<Header {...restProps} changeDirection={this.changeDirection} />
|
||||
@@ -297,13 +301,15 @@ export default class Layout extends React.Component {
|
||||
<DynamicTheme
|
||||
componentName={this.props.params?.children?.replace('-cn', '')}
|
||||
defaultToken={{
|
||||
theme: v5theme,
|
||||
...designToken,
|
||||
hashed: hashedStyle,
|
||||
}}
|
||||
onChangeTheme={newToken => {
|
||||
console.log('Change Theme:', newToken);
|
||||
const { hashed, ...restToken } = newToken;
|
||||
const { hashed, theme, ...restToken } = newToken;
|
||||
this.setState({
|
||||
v5theme: theme,
|
||||
designToken: restToken,
|
||||
hashedStyle: hashed,
|
||||
});
|
||||
|
||||
@@ -67,7 +67,7 @@ Array [
|
||||
"Upload",
|
||||
"message",
|
||||
"notification",
|
||||
"useDesignToken",
|
||||
"theme",
|
||||
"version",
|
||||
]
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user