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:
二货机器人
2022-06-29 20:34:00 +08:00
committed by GitHub
parent 75c1f7e3bd
commit 66a3594e95
12 changed files with 73 additions and 40 deletions

View File

@@ -67,7 +67,7 @@ Array [
"Upload",
"message",
"notification",
"useDesignToken",
"theme",
"version",
]
`;

View File

@@ -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>,
);

View File

@@ -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;
}

View File

@@ -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 = (

View File

@@ -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';

View File

@@ -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={{

View File

@@ -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,
};

View File

@@ -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];

View File

@@ -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)

View File

@@ -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;

View File

@@ -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,
});

View File

@@ -67,7 +67,7 @@ Array [
"Upload",
"message",
"notification",
"useDesignToken",
"theme",
"version",
]
`;