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 }): 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 {originValue}; } else { return
{originValue}
; } }; 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 (
{text}
); } else { return
{text}
; } } }, { title: intl.formatMessage({ id: 'designToken.defaultValue' }), dataIndex: 'value', render: (text: string): React.ReactElement => }, { title: intl.formatMessage({ id: 'designToken.usage' }), dataIndex: 'comment', render: (text: string): React.ReactElement =>
{text || intl.formatMessage({ id: 'designToken.WIP' })}
}, ], [intl.locale, mode]); if (!tokenList) { return null; } return ; }; 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 (
setCurrentTab(key)}>
); }; 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 ? ( setCurrentTab(key)}> ) : } ); }; 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 <> {Object.entries(tokenMap).map(([category, tokenList]) => { return })} } const DesignToken = (props: Props): React.ReactElement => { const [componentName, setComponentName] = useState(props.componentName?.toLowerCase()); const [designToken, setDesignToken] = useState(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 => { 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 (
{designToken && componentName && !props.isAnimation && (props.componentName === 'global' ? : )} { props.isAnimation && }
); }; export default DesignToken;