فهرست منبع

Merge pull request #1121 from DouyinFE/tokenTableAddAnimation

Token table add animation
代强 3 سال پیش
والد
کامیت
84c4711fd2
5فایلهای تغییر یافته به همراه89 افزوده شده و 22 حذف شده
  1. 2 2
      .vscode/settings.json
  2. 5 1
      content/basic/tokens/index-en-US.md
  3. 4 1
      content/basic/tokens/index.md
  4. 28 4
      scripts/designToken.js
  5. 50 14
      src/components/DesignToken/index.tsx

+ 2 - 2
.vscode/settings.json

@@ -17,7 +17,7 @@
         "editor.defaultFormatter": "michelemelluso.code-beautifier"
     },
     "[typescriptreact]": {
-        "editor.defaultFormatter": "dbaeumer.vscode-eslint"
+        "editor.defaultFormatter": "vscode.typescript-language-features"
     },
     "typescript.updateImportsOnFileMove.enabled": "always",
     "files.autoSave": "onFocusChange",
@@ -44,4 +44,4 @@
         "backtop",
         "Splited"
     ]
-}
+}

+ 5 - 1
content/basic/tokens/index-en-US.md

@@ -197,6 +197,10 @@ It is used to describe the sequence of interface elements
 
 <DesignToken componentName='global' reg={/z-/}/>
 
+## Animation
+
+<DesignToken componentName="global" isAnimation={true} />
+
 ## Variables not yet supported
 
 Currently, Semi does not support global variables in the following categories. If you have related requirements, you can give feedback through issue and describe your expected needs in detail.
@@ -210,4 +214,4 @@ Currently, Semi does not support global variables in the following categories. I
 **Media query**
 
 ## Customization
-If you need to customize the global variable style, please go to [Semi DSM](https://semi.design/dsm), make your own theme and publish it
+If you need to customize the global variable style, please go to [Semi DSM](https://semi.design/dsm), make your own theme and publish it

+ 4 - 1
content/basic/tokens/index.md

@@ -201,6 +201,10 @@ brief: Semi Design Tokens
 
 <DesignToken componentName='global' reg={/z-/}/>
 
+## 动画
+
+<DesignToken componentName="global" isAnimation={true} />
+
 ## 尚未支持的变量
 目前,Semi 尚未支持以下类别的全局变量,如果你有相关需求,可以通过issue进行反馈,详细描述你的预期需求,我们会在评估后进行处理
 
@@ -208,7 +212,6 @@ brief: Semi Design Tokens
 
 **字间距 letter spacing**
 
-**时长 duration**
 
 **媒体查询 media query**
 

+ 28 - 4
scripts/designToken.js

@@ -1,12 +1,13 @@
 const fs = require('fs');
 const path = require('path');
 const lodash = require('lodash');
+const { merge } = require("lodash");
 
 
 const isComment = codeLine => lodash.startsWith(codeLine, '//') || lodash.startsWith(codeLine, '/*');
 const getTokenCategory = codeLine => {
-    const categorySet = new Set(['color', 'width', 'height', 'spacing', 'radius', 'font', 'motion']);
-    const firstWord = lodash.get(codeLine.match(/\$([\w\W]+?)-/), 1, { toLowerCase: () => null }).toLowerCase();
+    const categorySet = new Set(['color', 'width', 'height', 'spacing', 'radius', 'font', 'motion', "animation", "transition"]);
+    const firstWord = lodash.get(codeLine.match(/\$([\w\W]+?)[-_]/), 1, { toLowerCase: () => null }).toLowerCase();
     if (firstWord) {
         return categorySet.has(firstWord) ? firstWord : 'other';
     } else {
@@ -15,12 +16,17 @@ const getTokenCategory = codeLine => {
 };
 const codeLineSplit = codeLine => {
     const [key, value, comment] = codeLine.split(/:|\/\/|\/\*/).map(code => code.trim()).filter(code => code);
-    return { key, value: lodash.trimEnd(value, ';'), comment:comment && comment.replace("ignore-semi-css-trans", ""), category: getTokenCategory(codeLine), raw: codeLine };
+    let category = getTokenCategory(codeLine);
+    if (category==='transition'){
+        category = "animation";
+    }
+    return { key, value: lodash.trimEnd(value, ';'), comment:comment && comment.replace("ignore-semi-css-trans", ""), category: category, raw: codeLine };
 };
 
 const getGlobalDesignToken = () => {
     const globalScssContentArray = fs.readFileSync(path.join(__dirname, '../packages/semi-theme-default/scss/global.scss'), { encoding: 'utf-8' }).split('\n');
     const paletteScssContentArray = fs.readFileSync(path.join(__dirname, '../packages/semi-theme-default/scss/_palette.scss'), { encoding: 'utf-8' }).split('\n');
+    const animationScssContentArray = fs.readFileSync(path.join(__dirname, '../packages/semi-theme-default/scss/animation.scss'), { encoding: 'utf-8' }).split('\n');
     const normalContentArray = fs.readFileSync(path.join(__dirname, '../packages/semi-theme-default/scss/variables.scss'), { encoding: 'utf-8' }).split('\n');
     const getLightAndDarkScss = scssFileContentArray => {
         const contentArray = scssFileContentArray.map(codeLine => codeLine.trim())
@@ -79,12 +85,23 @@ const getGlobalDesignToken = () => {
     const normalContent = normalContentArray.map(codeLine => codeLine.trim())
         .filter(codeLine => codeLine && !isComment(codeLine))
         .map(codeLine => codeLineSplit(codeLine));
-    return { global: globalScssContent, palette: paletteContent, normal: normalContent };
+
+    const animationContent = animationScssContentArray.map(codeLine => codeLine.trim())
+        .filter(codeLine => codeLine && !isComment(codeLine))
+        .filter(codeLine => !codeLine.startsWith('body'))
+        .filter(codeLine => !codeLine.startsWith('}'))
+        .filter(codeLine => !codeLine.startsWith('@'))
+        .map(codeLine=>codeLineSplit(codeLine)).map(token=>{
+            token.category="animation";
+            return token;
+        });
+    return { global: globalScssContent, palette: paletteContent, normal: normalContent, animation:animationContent };
 };
 
 // 官网组件 design token 注入
 async function main() {
     const componentVariablesMap = {};
+    const animationVariablesMap = {};
     const semiUIDir = path.join(__dirname, '../packages/semi-foundation');
     fs.readdirSync(semiUIDir).map(dirname => {
         const variableSCSSPath = path.join(semiUIDir, dirname, 'variables.scss');
@@ -93,7 +110,14 @@ async function main() {
             const scssCodeLineList = raw.split('\n').filter(codeLine => codeLine && !isComment(codeLine));
             componentVariablesMap[dirname.toLowerCase()] = scssCodeLineList.map(codeLine => codeLineSplit(codeLine));
         }
+        const animationSCSSPath = path.join(semiUIDir, dirname, 'animation.scss');
+        if (fs.existsSync(animationSCSSPath)){
+            const raw = fs.readFileSync(animationSCSSPath, { encoding: 'utf-8' });
+            const scssCodeLineList = raw.split('\n').filter(codeLine => codeLine && !isComment(codeLine));
+            animationVariablesMap[dirname.toLowerCase()] = scssCodeLineList.map(codeLine => codeLineSplit(codeLine));
+        }
     });
+    merge(componentVariablesMap, animationVariablesMap);
     componentVariablesMap.global = getGlobalDesignToken();
     const [_, __, savePath] = process.argv;
     fs.writeFileSync(savePath || './designToken.json', JSON.stringify(componentVariablesMap));

+ 50 - 14
src/components/DesignToken/index.tsx

@@ -4,20 +4,21 @@ import { Tabs, Table } from '@douyinfe/semi-ui';
 import { useIntl } from 'react-intl';
 import { Link } from 'gatsby';
 import lodash from 'lodash-es';
-interface Props{
+interface Props {
     componentName?: string;
     isColorPalette?: boolean;
     reg?: RegExp;
+    isAnimation?: boolean
 }
 
-interface Token{
+interface Token {
     key: string;
     value: string;
     category: string;
     raw: string;
 }
 
-interface DesignToken{
+interface DesignToken {
     // eslint-disable-next-line @typescript-eslint/ban-ts-comment
     // @ts-ignore
     global: {
@@ -30,18 +31,19 @@ interface DesignToken{
             dark: Token[];
         };
         normal: Token[];
+        animation: Token[];
     };
     [key: string]: Token[];
 
 }
 
 
-interface TokenMayWithColor extends Token{
+interface TokenMayWithColor extends Token {
     color?: string;
 }
 
 
-const JumpLink = ({ value, availableKeySet }: {value: string;availableKeySet: Set<string>}): ReactElement => {
+const JumpLink = ({ value, availableKeySet }: { value: string; availableKeySet: Set<string> }): ReactElement => {
     const { locale } = useIntl();
     const originValue = value;
     if (value.startsWith('var')) {
@@ -57,7 +59,7 @@ const JumpLink = ({ value, availableKeySet }: {value: string;availableKeySet: Se
     }
 };
 
-const TokenTable = ({ tokenList, designToken, currentTab,mode='light' }: {mode?:'light'|'dark',tokenList: TokenMayWithColor[];designToken: DesignToken; currentTab?: string; }): React.ReactElement => {
+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;
@@ -76,7 +78,7 @@ const TokenTable = ({ tokenList, designToken, currentTab,mode='light' }: {mode?:
                         <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'}
+                        }} className={mode === 'dark' ? 'semi-always-dark' : 'semi-always-light'}
                         /> {text}
                         </div>
                     );
@@ -98,14 +100,14 @@ const TokenTable = ({ tokenList, designToken, currentTab,mode='light' }: {mode?:
                 <div>{text || intl.formatMessage({ id: 'designToken.WIP' })}</div>
         },
 
-    ], [intl.locale,mode]);
+    ], [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 TokenTab = ({ designToken, componentName }: { designToken: DesignToken; componentName: string }): React.ReactElement => {
     const componentDesignTokenList = useMemo(() => designToken[componentName], [designToken, componentName]);
     const tabList = useMemo(() => {
         const categorySet = new Set();
@@ -126,9 +128,9 @@ const TokenTab = ({ designToken, componentName }: {designToken: DesignToken;comp
     );
 };
 
-const GlobalTokenTab = ({ designToken, isColorPalette = false, reg }: {designToken: DesignToken;isColorPalette?: boolean;reg: RegExp}): React.ReactElement => {
+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 [currentTab, setCurrentTab] = useState<'light' | 'dark'>('light');
     const [hasTab, setHasTab] = useState(true);
     const tokenList: TokenMayWithColor[] = useMemo(() => {
         if (!isColorPalette) {
@@ -151,14 +153,44 @@ const GlobalTokenTab = ({ designToken, isColorPalette = false, reg }: {designTok
     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 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());
@@ -195,9 +227,13 @@ const DesignToken = (props: Props): React.ReactElement => {
 
     return (
         <div>
-            {designToken && componentName && (props.componentName === 'global' ?
+            {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>
     );
 };