123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- import React, { useState, useEffect, useMemo, ReactElement } from 'react';
- import axios from 'axios';
- import { Tabs, Table } from '@douyinfe/semi-ui';
- import { useIntl } from 'react-intl';
- import { Link } from 'gatsby';
- import lodash from 'lodash-es';
- interface Props {
- componentName?: string;
- isColorPalette?: boolean;
- reg?: RegExp;
- isAnimation?: boolean
- }
- interface Token {
- key: string;
- value: string;
- category: string;
- raw: string;
- }
- interface DesignToken {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- global: {
- global: {
- light: Token[];
- dark: Token[];
- };
- palette: {
- light: Token[];
- dark: Token[];
- };
- normal: Token[];
- animation: Token[];
- };
- [key: string]: Token[];
- }
- interface TokenMayWithColor extends Token {
- color?: string;
- }
- const JumpLink = ({ value, availableKeySet }: { value: string; availableKeySet: Set<string> }): ReactElement => {
- const { locale } = useIntl();
- const originValue = value;
- if (value.startsWith('var')) {
- const { length } = value;
- value = value.slice(4, length - 1);
- }
- const params = new URLSearchParams();
- params.append('token', lodash.trim(value, '$'));
- if (availableKeySet.has(value)) {
- return <Link to={`/${locale}/basic/tokens?${params.toString()}`} style={{ display: 'block' }}>{originValue}</Link>;
- } else {
- return <div>{originValue}</div>;
- }
- };
- const TokenTable = ({ tokenList, designToken, currentTab, mode = 'light' }: { mode?: 'light' | 'dark', tokenList: TokenMayWithColor[]; designToken: DesignToken; currentTab?: string; }): React.ReactElement => {
- const intl = useIntl();
- const globalTokenJumpAvailableSet = useMemo(() => {
- const global = designToken?.global;
- const set = new Set(global?.global?.light.map(token => token.key)
- .concat(global?.normal?.map(token => token.key)));
- return set;
- }, [designToken]);
- const columns = useMemo(() => [
- {
- title: intl.formatMessage({ id: 'designToken.variable' }),
- dataIndex: 'key',
- render: (text: string, record: TokenMayWithColor): React.ReactElement => {
- const { color } = record;
- if (color) {
- return (
- <div data-token={lodash.trim(text, '$')} style={{ fontWeight: 600 }}> <div style={{
- width: 16, height: 16, display: 'inline-block', borderRadius: 3, backgroundColor: color,
- transform: 'translateY(0.3rem)', marginRight: 8
- }} className={mode === 'dark' ? 'semi-always-dark' : 'semi-always-light'}
- /> {text}
- </div>
- );
- } else {
- return <div data-token={lodash.trim(text, '$')} style={{ fontWeight: 600 }}>{text}</div>;
- }
- }
- },
- {
- title: intl.formatMessage({ id: 'designToken.defaultValue' }),
- dataIndex: 'value',
- render: (text: string): React.ReactElement => <JumpLink availableKeySet={globalTokenJumpAvailableSet} value={text} />
- },
- {
- title: intl.formatMessage({ id: 'designToken.usage' }),
- dataIndex: 'comment',
- render: (text: string): React.ReactElement =>
- <div>{text || intl.formatMessage({ id: 'designToken.WIP' })}</div>
- },
- ], [intl.locale, mode]);
- if (!tokenList) {
- return null;
- }
- return <Table key={currentTab} columns={columns} dataSource={tokenList} />;
- };
- const TokenTab = ({ designToken, componentName }: { designToken: DesignToken; componentName: string }): React.ReactElement => {
- const componentDesignTokenList = useMemo(() => designToken[componentName], [designToken, componentName]);
- const tabList = useMemo(() => {
- const categorySet = new Set();
- componentDesignTokenList?.forEach(oneToken => categorySet.add(oneToken.category));
- return Array.from(categorySet).sort().map(category => ({ tab: category, itemKey: category }));
- }, [componentDesignTokenList]);
- const [currentTab, setCurrentTab] = useState(tabList[0]?.itemKey);
- const tokenList = useMemo(() =>
- designToken[componentName]?.filter((token: Token) => token.category === currentTab), [currentTab]);
- return (
- <div>
- <Tabs tabList={tabList} onChange={(key: string): void => setCurrentTab(key)}>
- <TokenTable designToken={designToken} tokenList={tokenList} currentTab={currentTab as string} />
- </Tabs>
- </div>
- );
- };
- const GlobalTokenTab = ({ designToken, isColorPalette = false, reg }: { designToken: DesignToken; isColorPalette?: boolean; reg: RegExp }): React.ReactElement => {
- const { global, palette, normal } = designToken.global;
- const [currentTab, setCurrentTab] = useState<'light' | 'dark'>('light');
- const [hasTab, setHasTab] = useState(true);
- const tokenList: TokenMayWithColor[] = useMemo(() => {
- if (!isColorPalette) {
- const modeFilteredTokenListOfGlobal: Token[] = global[currentTab].filter((token: Token) => reg.test(token.key));
- const normalTokenList: Token[] = normal.filter((token: Token) => reg.test(token.key));
- if ((modeFilteredTokenListOfGlobal.length !== 0) !== hasTab) {
- setHasTab(!hasTab);
- }
- return [...modeFilteredTokenListOfGlobal, ...normalTokenList].map((token: TokenMayWithColor): TokenMayWithColor => {
- if (token.key.startsWith('--semi-color')) {
- token.color = token.value;
- }
- return token;
- });
- } else {
- return [];
- }
- }, [currentTab]);
- return (
- <>
- {hasTab ? (
- <Tabs defaultActiveKey={'light'} tabList={[{ tab: 'Light', itemKey: 'light' }, { tab: 'Dark', itemKey: 'dark' }]} onChange={(key: typeof currentTab): void => setCurrentTab(key)}>
- <TokenTable designToken={designToken} tokenList={tokenList} mode={currentTab} />
- </Tabs>
- ) : <TokenTable designToken={designToken} tokenList={tokenList} mode={currentTab} />}
- </>
- );
- };
- const GlobalAnimationToken = ({ designToken }: { designToken: DesignToken }) => {
- const animationList = useMemo(() => designToken?.global?.animation ?? [], [designToken]);
- const tokenMap = useMemo(() => {
- const tokenMap = {};
- animationList.forEach(token => {
- const tab = token['key'].match(/--semi-transition_([\w\W]+)-/)?.[1] ?? "other";
- tokenMap[tab] = [...(tokenMap[tab] ?? []), token];
- })
- return tokenMap;
- }, [animationList]);
- return <>
- <Tabs defaultActiveKey={Object.keys(tokenMap)[0]} >
- {Object.entries(tokenMap).map(([category, tokenList]) => {
- return <Tabs.TabPane tab={category} itemKey={category} key={category}>
- <TokenTable designToken={designToken} tokenList={tokenList} />
- </Tabs.TabPane>
- })}
- </Tabs>
- </>
- }
- const DesignToken = (props: Props): React.ReactElement => {
- const [componentName, setComponentName] = useState(props.componentName?.toLowerCase());
- const [designToken, setDesignToken] = useState<DesignToken>(null);
- useEffect(() => {
- if (!componentName) {
- const componentNameFromUrl = lodash.nth(window.location.pathname.split('/'), -1);
- setComponentName(componentNameFromUrl.toLowerCase());
- }
- }, []);
- useEffect(() => {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- if (window?.__semi__?.designToken) {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- setDesignToken(window?.__semi__?.designToken);
- } else {
- (async (): Promise<void> => {
- const { data: designTokenFromServer } = await axios.get('/designToken.json');
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- window.__semi__ = {
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- ...window.__semi__,
- designToken: designTokenFromServer
- };
- setDesignToken(designTokenFromServer);
- })();
- }
- }, []);
- return (
- <div>
- {designToken && componentName && !props.isAnimation && (props.componentName === 'global' ?
- <GlobalTokenTab designToken={designToken} reg={props.reg} isColorPalette={props.isColorPalette} /> :
- <TokenTab designToken={designToken} componentName={componentName} />)}
- {
- props.isAnimation && <GlobalAnimationToken designToken={designToken} />
- }
- </div>
- );
- };
- export default DesignToken;
|