1
0
代强 3 жил өмнө
parent
commit
72f726b75e
100 өөрчлөгдсөн 2325 нэмэгдсэн , 446 устгасан
  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. 1 1
      content/feedback/banner/index-en-US.md
  5. 1 1
      content/feedback/banner/index.md
  6. 1 1
      content/feedback/notification/index-en-US.md
  7. 1 1
      content/feedback/notification/index.md
  8. 1 1
      content/feedback/popconfirm/index-en-US.md
  9. 1 1
      content/feedback/popconfirm/index.md
  10. 147 2
      content/feedback/progress/index-en-US.md
  11. 151 5
      content/feedback/progress/index.md
  12. 5 4
      content/feedback/skeleton/index-en-US.md
  13. 16 17
      content/feedback/skeleton/index.md
  14. 1 1
      content/feedback/spin/index-en-US.md
  15. 1 1
      content/feedback/spin/index.md
  16. 28 1
      content/feedback/toast/index-en-US.md
  17. 30 3
      content/feedback/toast/index.md
  18. 57 56
      content/navigation/navigation/index.md
  19. 1 0
      content/order.js
  20. 1 1
      content/other/configprovider/index-en-US.md
  21. 1 1
      content/other/configprovider/index.md
  22. 1 1
      content/other/locale/index-en-US.md
  23. 1 1
      content/other/locale/index.md
  24. 56 0
      content/show/image/index-en-US.md
  25. 56 0
      content/show/image/index.md
  26. 2 2
      content/show/list/index-en-US.md
  27. 1 1
      content/show/list/index.md
  28. 1 1
      content/show/modal/index-en-US.md
  29. 228 222
      content/show/modal/index.md
  30. 1 1
      content/show/overflowlist/index-en-US.md
  31. 1 1
      content/show/overflowlist/index.md
  32. 1 1
      content/show/popover/index-en-US.md
  33. 1 1
      content/show/popover/index.md
  34. 1 1
      content/show/scrolllist/index-en-US.md
  35. 1 1
      content/show/scrolllist/index.md
  36. 1 1
      content/show/sidesheet/index-en-US.md
  37. 1 1
      content/show/sidesheet/index.md
  38. 1 1
      content/show/table/index-en-US.md
  39. 1 1
      content/show/table/index.md
  40. 18 1
      content/show/tag/index-en-US.md
  41. 18 1
      content/show/tag/index.md
  42. 1 1
      content/show/timeline/index-en-US.md
  43. 1 1
      content/show/timeline/index.md
  44. 1 1
      content/show/tooltip/index-en-US.md
  45. 1 1
      content/show/tooltip/index.md
  46. 30 0
      content/start/changelog/index-en-US.md
  47. 29 0
      content/start/changelog/index.md
  48. 1 0
      content/start/overview/index-en-US.md
  49. 1 0
      content/start/overview/index.md
  50. 1 0
      gatsby-node.js
  51. 1 1
      lerna.json
  52. 9 9
      package.json
  53. 2 2
      packages/semi-animation-react/package.json
  54. 0 12
      packages/semi-animation-styled/getBabelConfig.js
  55. 1 4
      packages/semi-animation-styled/package.json
  56. 1 1
      packages/semi-animation/package.json
  57. 1 1
      packages/semi-eslint-plugin/package.json
  58. 1 1
      packages/semi-foundation/anchor/anchor.scss
  59. 1 1
      packages/semi-foundation/autoComplete/autoComplete.scss
  60. 2 2
      packages/semi-foundation/badge/badge.scss
  61. 2 2
      packages/semi-foundation/breadcrumb/breadcrumb.scss
  62. 1 1
      packages/semi-foundation/button/button.scss
  63. 1 2
      packages/semi-foundation/calendar/foundation.ts
  64. 2 2
      packages/semi-foundation/calendar/variables.scss
  65. 1 1
      packages/semi-foundation/carousel/carousel.scss
  66. 2 1
      packages/semi-foundation/cascader/cascader.scss
  67. 2 2
      packages/semi-foundation/checkbox/checkbox.scss
  68. 6 5
      packages/semi-foundation/datePicker/foundation.ts
  69. 5 5
      packages/semi-foundation/datePicker/variables.scss
  70. 1 1
      packages/semi-foundation/dropdown/dropdown.scss
  71. 2 2
      packages/semi-foundation/form/variables.scss
  72. 1 1
      packages/semi-foundation/grid/grid.scss
  73. 10 10
      packages/semi-foundation/grid/mixin.scss
  74. 14 6
      packages/semi-foundation/gulpfile.js
  75. 11 0
      packages/semi-foundation/image/animation.scss
  76. 7 0
      packages/semi-foundation/image/constants.ts
  77. 221 0
      packages/semi-foundation/image/image.scss
  78. 64 0
      packages/semi-foundation/image/imageFoundation.ts
  79. 41 0
      packages/semi-foundation/image/previewFooterFoundation.ts
  80. 25 0
      packages/semi-foundation/image/previewFoundation.ts
  81. 260 0
      packages/semi-foundation/image/previewImageFoundation.ts
  82. 260 0
      packages/semi-foundation/image/previewInnerFoundation.ts
  83. 51 0
      packages/semi-foundation/image/rtl.scss
  84. 86 0
      packages/semi-foundation/image/utils.ts
  85. 47 0
      packages/semi-foundation/image/variables.scss
  86. 2 2
      packages/semi-foundation/input/input.scss
  87. 2 2
      packages/semi-foundation/input/textarea.scss
  88. 0 1
      packages/semi-foundation/inputNumber/inputNumber.scss
  89. 3 3
      packages/semi-foundation/inputNumber/variables.scss
  90. 1 1
      packages/semi-foundation/navigation/foundation.ts
  91. 1 1
      packages/semi-foundation/navigation/navigation.scss
  92. 2 2
      packages/semi-foundation/navigation/variables.scss
  93. 1 2
      packages/semi-foundation/package.json
  94. 1 1
      packages/semi-foundation/pagination/pagination.scss
  95. 2 1
      packages/semi-foundation/progress/constants.ts
  96. 242 0
      packages/semi-foundation/progress/generates.ts
  97. 5 4
      packages/semi-foundation/radio/radio.scss
  98. 2 2
      packages/semi-foundation/radio/variables.scss
  99. 2 1
      packages/semi-foundation/rating/rating.scss
  100. 3 2
      packages/semi-foundation/scrollList/scrollList.scss

+ 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**
 

+ 1 - 1
content/feedback/banner/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 64
+order: 65
 category: Feedback
 title:  Banner
 subTitle: Banner

+ 1 - 1
content/feedback/banner/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 64
+order: 65
 category: 反馈类
 title:  Banner 通知横幅
 icon: doc-banner

+ 1 - 1
content/feedback/notification/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 65
+order: 66
 category: Feedback
 title:  Notification
 subTitle: Notification

+ 1 - 1
content/feedback/notification/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 65
+order: 66
 category: 反馈类
 title: Notification 通知
 icon: doc-notification

+ 1 - 1
content/feedback/popconfirm/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 66
+order: 67
 category: Feedback
 title:  Popconfirm
 subTitle: Popconfirm

+ 1 - 1
content/feedback/popconfirm/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 66
+order: 67
 category: 反馈类
 title:  Popconfirm 气泡确认框
 icon: doc-popconfirm

+ 147 - 2
content/feedback/progress/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 67
+order: 68
 category: Feedback
 title: Progress
 subTitle: Progress
@@ -241,6 +241,150 @@ import { Progress } from '@douyinfe/semi-ui';
 );
 ```
 
+### Customise the progress bar color
+
+The color of a specific `percent` can be customised by setting the `stroke` property
+
+```jsx live=true
+import React, { useState } from 'react';
+import { Progress, Button } from '@douyinfe/semi-ui';
+import { IconChevronLeft, IconChevronRight } from '@douyinfe/semi-icons';
+
+() => {
+    const [percent, setPercent] = useState(10);
+    const strokeArr = [
+        { percent: 20, color: 'red' },
+        { percent: 40, color: 'orange-9' },
+        { percent: 60, color: 'light-green-8' },
+        { percent: 80, color: 'hsla(125, 50%, 46% / 1)' }
+    ];
+    return (
+        <>
+            <div>
+                <Progress
+                    percent={percent}
+                    stroke={strokeArr}
+                    showInfo
+                    type="circle"
+                    width={100}
+                    aria-label="disk usage"
+                />
+                <Progress
+                    percent={percent}
+                    stroke={strokeArr}
+                    showInfo
+                    style={{ margin: '20px 0 10px' }}
+                    aria-label="disk usage"
+                />
+            </div>
+            <Button
+                icon={<IconChevronLeft />}
+                theme="light"
+                onClick={() => {
+                    setPercent(percent - 10);
+                }}
+                disabled={percent === 0}
+            />
+            <Button
+                icon={<IconChevronRight />}
+                theme="light"
+                onClick={() => {
+                    setPercent(percent + 10);
+                }}
+                disabled={percent === 100}
+            />
+        </>
+    );
+};
+```
+
+### Auto-completion of colour intervals
+
+The gradient can be generated by setting the `strokeGradient` property to `true`, automatically fill the colour interval.
+
+```jsx live=true
+import React, { useEffect, useState } from 'react';
+import { Space, Progress, Button } from '@douyinfe/semi-ui';
+import { IconChevronLeft, IconChevronRight } from '@douyinfe/semi-icons';
+
+() => {
+    const [percent, setPercent] = useState(65);
+    const [percentInterval, setPercentInterval] = useState(0);
+    useEffect(() => {
+        setTimeout(
+            () => {
+                setPercentInterval(percentInterval > 100 ? 0 : percentInterval + 3);
+            },
+            percentInterval === 0 || percentInterval > 100 ? 1200 : 290 - (percentInterval % 50) * 3
+        );
+    }, [percentInterval]);
+    const strokeArr = [
+        { percent: 0, color: 'rgb(249, 57, 32)' },
+        { percent: 50, color: '#46259E' },
+        { percent: 100, color: 'hsla(125, 50%, 46% / 1)' },
+    ];
+    const strokeArrReverse = [
+        { percent: 0, color: 'hsla(125, 50%, 46% / 1)' },
+        { percent: 50, color: '#46259E' },
+        { percent: 100, color: 'rgb(249, 57, 32)' },
+    ];
+    return (
+        <>
+            <Space spacing={20}>
+                <div>
+                    <Progress
+                        percent={percentInterval}
+                        stroke={strokeArr}
+                        strokeGradient={true}
+                        showInfo
+                        type="circle"
+                        width={100}
+                        aria-label="file download speed"
+                    />
+                </div>
+                <div>
+                    <Progress
+                        percent={percentInterval}
+                        stroke={strokeArrReverse}
+                        strokeGradient={true}
+                        showInfo
+                        type="circle"
+                        width={100}
+                        aria-label="file download speed"
+                    />
+                </div>
+            </Space>
+            <div style={{ width: '100%', margin: '20px 0 10px' }}>
+                <Progress
+                    percent={percent}
+                    stroke={strokeArr}
+                    strokeGradient={true}
+                    showInfo
+                    size="large"
+                    aria-label="file download speed"
+                />
+            </div>
+            <Button
+                icon={<IconChevronLeft />}
+                theme="light"
+                onClick={() => {
+                    setPercent(percent - 5);
+                }}
+                disabled={percent === 0}
+            />
+            <Button
+                icon={<IconChevronRight />}
+                theme="light"
+                onClick={() => {
+                    setPercent(percent + 5);
+                }}
+                disabled={percent === 100}
+            />
+        </>
+    );
+};
+```
+
 ## API Reference
 
 | PROPERTIES | Instructions | Type | Default |
@@ -256,7 +400,8 @@ import { Progress } from '@douyinfe/semi-ui';
 | percent | percentage of progress | number |  |
 | showInfo | Whether to display the middle text in the circular progress bar, and whether to display the text on the right side of the bar-shaped progress bar | boolean | false |
 | size | size, optional `default`, `small` (only type=circle is effective), `large` (only type=line is effective) | string | 'default' |
-| stroke | Fill color of progress bar | string | 'var(--semi-color-success)' |
+| stroke | Fill color of progress bar, When of type `Array<{percent:number; color:string }>`, the `color` parameter supports the color types: `'Hex'` &#124; `'Hsl'` &#124; `'Hsla'` &#124; `'Rgb'` &#124; `'Rgba'` &#124; `'Semi Design Tokens'` | string &#124; Array<{percent:number; color:string }> | 'var(--semi-color-success)' |
+| strokeGradient | Whether to automatically generate gradient colors to fill color intervals, requires `stroke` to set at least one color interval | boolean | false |
 | strokeLinecap | round corner `round`/square corner `square` (only effective in type='circle' mode) | string | 'round' |
 | strokeWidth | When type is `line`, this property controls the height of the progress bar; when type is `circle`, this property controls the width of the progress bar | number | 4 |
 | style | style | CSSProperties |  |

+ 151 - 5
content/feedback/progress/index.md

@@ -1,12 +1,13 @@
 ---
 localeCode: zh-CN
-order: 67
+order: 68
 category: 反馈类
 title: Progress 进度条
 icon: doc-progress
 width: 60%
 brief: 用于展示用户操作的当前进度和状态,一般在操作耗时较长时使用。也可用来表示任务/对象的完成度
 ---
+
 ## 代码演示
 
 ### 如何引入
@@ -261,6 +262,150 @@ import { Progress } from '@douyinfe/semi-ui';
 );
 ```
 
+### 自定义进度条颜色
+
+可通过设置 `stroke` 属性,自定义具体 `percent` 的颜色
+
+```jsx live=true
+import React, { useState } from 'react';
+import { Progress, Button } from '@douyinfe/semi-ui';
+import { IconChevronLeft, IconChevronRight } from '@douyinfe/semi-icons';
+
+() => {
+    const [percent, setPercent] = useState(10);
+    const strokeArr = [
+        { percent: 20, color: 'red' },
+        { percent: 40, color: 'orange-9' },
+        { percent: 60, color: 'light-green-8' },
+        { percent: 80, color: 'hsla(125, 50%, 46% / 1)' }
+    ];
+    return (
+        <>
+            <div>
+                <Progress
+                    percent={percent}
+                    stroke={strokeArr}
+                    showInfo
+                    type="circle"
+                    width={100}
+                    aria-label="disk usage"
+                />
+                <Progress
+                    percent={percent}
+                    stroke={strokeArr}
+                    showInfo
+                    style={{ margin: '20px 0 10px' }}
+                    aria-label="disk usage"
+                />
+            </div>
+            <Button
+                icon={<IconChevronLeft />}
+                theme="light"
+                onClick={() => {
+                    setPercent(percent - 10);
+                }}
+                disabled={percent === 0}
+            />
+            <Button
+                icon={<IconChevronRight />}
+                theme="light"
+                onClick={() => {
+                    setPercent(percent + 10);
+                }}
+                disabled={percent === 100}
+            />
+        </>
+    );
+};
+```
+
+### 自动补齐颜色区间
+
+可通过设置 `strokeGradient` 属性,属性为 `true` 时自动补齐颜色区间,生成渐变色
+
+```jsx live=true
+import React, { useEffect, useState } from 'react';
+import { Space, Progress, Button } from '@douyinfe/semi-ui';
+import { IconChevronLeft, IconChevronRight } from '@douyinfe/semi-icons';
+
+() => {
+    const [percent, setPercent] = useState(65);
+    const [percentInterval, setPercentInterval] = useState(0);
+    useEffect(() => {
+        setTimeout(
+            () => {
+                setPercentInterval(percentInterval > 100 ? 0 : percentInterval + 3);
+            },
+            percentInterval === 0 || percentInterval > 100 ? 1200 : 290 - (percentInterval % 50) * 3
+        );
+    }, [percentInterval]);
+    const strokeArr = [
+        { percent: 0, color: 'rgb(249, 57, 32)' },
+        { percent: 50, color: '#46259E' },
+        { percent: 100, color: 'hsla(125, 50%, 46% / 1)' },
+    ];
+    const strokeArrReverse = [
+        { percent: 0, color: 'hsla(125, 50%, 46% / 1)' },
+        { percent: 50, color: '#46259E' },
+        { percent: 100, color: 'rgb(249, 57, 32)' },
+    ];
+    return (
+        <>
+            <Space spacing={20}>
+                <div>
+                    <Progress
+                        percent={percentInterval}
+                        stroke={strokeArr}
+                        strokeGradient={true}
+                        showInfo
+                        type="circle"
+                        width={100}
+                        aria-label="file download speed"
+                    />
+                </div>
+                <div>
+                    <Progress
+                        percent={percentInterval}
+                        stroke={strokeArrReverse}
+                        strokeGradient={true}
+                        showInfo
+                        type="circle"
+                        width={100}
+                        aria-label="file download speed"
+                    />
+                </div>
+            </Space>
+            <div style={{ width: '100%', margin: '20px 0 10px' }}>
+                <Progress
+                    percent={percent}
+                    stroke={strokeArr}
+                    strokeGradient={true}
+                    showInfo
+                    size="large"
+                    aria-label="file download speed"
+                />
+            </div>
+            <Button
+                icon={<IconChevronLeft />}
+                theme="light"
+                onClick={() => {
+                    setPercent(percent - 5);
+                }}
+                disabled={percent === 0}
+            />
+            <Button
+                icon={<IconChevronRight />}
+                theme="light"
+                onClick={() => {
+                    setPercent(percent + 5);
+                }}
+                disabled={percent === 100}
+            />
+        </>
+    );
+};
+```
+
 ## API 参考
 
 | 属性 | 说明 | 类型 | 默认值 |
@@ -276,7 +421,8 @@ import { Progress } from '@douyinfe/semi-ui';
 | percent | 进度百分比 | number |  |
 | showInfo | 环形进度条是否显示中间文本,条状进度条后右侧是否显示文本 | boolean | false |
 | size | 尺寸,可选`default`、`small`(仅 type=circle 生效)、`large`(仅 type=line 生效) | string | 'default' |
-| stroke | 进度条填充色 | string | 'var(--semi-color-success)' |
+| stroke | 进度条填充色,类型为 `Array<{percent:number; color:string }>` 时,`color` 参数支持颜色类型:`'Hex'` &#124; `'Hsl'` &#124; `'Hsla'` &#124; `'Rgb'` &#124; `'Rgba'` &#124; `'Semi Design Tokens'` | string &#124; Array<{percent:number; color:string }> | 'var(--semi-color-success)' |
+| strokeGradient | 是否自动生成渐变色补齐区间颜色,需要 `stroke` 设置至少一个颜色区间 | boolean | false |
 | strokeLinecap | 圆角`round`/方角`square`(仅在 type='circle'模式下生效) | string | 'round' |
 | strokeWidth | type 为`line`时,该属性控制进度条高度; type 为`circle`时,该属性控制进度条宽度 | number | 4 |
 | style | 样式 | CSSProperties |  |
@@ -303,12 +449,12 @@ import { Progress } from '@douyinfe/semi-ui';
 <Progress aria-label='Percent of file downloaded' percent={80} />
 
 // usage of aria-valuetext
-<Progress aria-label='Percent of disk usage' percent={80} aria-valuetext="Step 2: Copying files... "/> 
+<Progress aria-label='Percent of disk usage' percent={80} aria-valuetext="Step 2: Copying files... "/>
 ```
 
-
 ## 文案规范
-- 如果进度条过程复杂,或者有很长的等待时间,可以使用帮助文本来做说明。这样可以让用户知道正在发生的进度进展
+
+-   如果进度条过程复杂,或者有很长的等待时间,可以使用帮助文本来做说明。这样可以让用户知道正在发生的进度进展
 
 ## 设计变量
 

+ 5 - 4
content/feedback/skeleton/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 68
+order: 69
 category: Feedback
 title: Skeleton
 subTitle: Skeleton
@@ -10,7 +10,7 @@ brief: A placeholder preview of content before the data loaded.
 
 ## Overview
 
--   `Avatar`: Avatar placeholder, by default uses Avatar medium sizing: `width: 48px`, `height: 48px`. Supports other sizes after v1.0.
+-   `Avatar`: Avatar placeholder, by default uses Avatar medium sizing: `width: 48px`, `height: 48px`. Supports Avatar's size (after v1.0) and shape attributes (after v2.20)
 -   `Image`: Image placeholder, default size: `width: 100%`, `height: 100%`.
 -   `Title`: Title placeholder, default size: `width: 100%`, `height: 24px`.
 -   `Paragraph`: Content part placeholder, default size: `width: 100%`, `height: 16px`, `margin-bottom: 10px`.
@@ -386,13 +386,14 @@ import { Skeleton, Avatar } from '@douyinfe/semi-ui';
 
 ### Skeleton.Avatar
 
-> `Skeleton.Image`,`Skeleton.Title`,`Skeleton.Button` have same APIs with `Skeleton.Avatar`.
+> `Skeleton.Image`,`Skeleton.Title`,`Skeleton.Button` have same APIs with `Skeleton.Avatar`. `shape` only supported in `Skeleton.Avatar`  
 
 | Property | Instructions | type | Default |
 | --- | --- | --- | --- |
 | class Name | Class name | string | - |
 | size | Size of the avatar, one of `extra-extra-small`, `extra-small`, `small`, `medium`, `large`, `extra-large`, **v>=1.0** | string | `medium` |
 | style | Inline style | CSSProperties | - |
+| shape | Shape of the avatar, one of `circle`, `square` | string | `circle` |
 
 ### Skeleton.Paragraph
 
@@ -404,7 +405,7 @@ import { Skeleton, Avatar } from '@douyinfe/semi-ui';
 
 ## Content Guidelines
 
-- Unchanged fixed content directly displays fixed content, and variable content is displayed using skeleton screen
+-   Unchanged fixed content directly displays fixed content, and variable content is displayed using skeleton screen
 
 ## Design Tokens
 

+ 16 - 17
content/feedback/skeleton/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 68
+order: 69
 category: 反馈类
 title: Skeleton 骨架屏
 icon: doc-skeleton
@@ -9,7 +9,7 @@ brief: 在需要等待加载内容的位置提供的占位组件。
 
 ## 概述
 
--   `Avatar`:占位头像,默认为圆形,默认尺寸:Avatar medium: `width: 48px`,`height: 48px`。支持 Avatar 的 size 属性 (**v>=1.0**)
+-   `Avatar`:占位头像,默认为圆形,默认尺寸:Avatar medium: `width: 48px`,`height: 48px`。支持 Avatar 的 size(v1.0后支持)、shape 属性 (v2.20后支持)
 -   `Image`:占位图像,默认尺寸:`width: 100%`,`height: 100%`。
 -   `Title`:占位标题,默认尺寸:`width: 100%`, `height: 24px`。
 -   `Paragraph`:占位内容部分,默认尺寸:`width: 100%`,`height: 16px`,`margin-bottom: 10px`。
@@ -364,37 +364,36 @@ import { Skeleton, Avatar } from '@douyinfe/semi-ui';
 
 ### Skeleton
 
-| 属性        | 说明                                       | 类型       | 默认值 |
-| ----------- | ------------------------------------------ | ---------- | ------ |
-| active      | 是否展示动画效果                           | boolean    | false  |
-| className   | 类名                                       | string     | -      |
-| loading     | 为 true 时,显示占位元素。反之则显示子组件 | boolean    | true   |
-| placeholder | 加载等待时的占位元素                       | ReactNode | -      |
-| style       | 样式                                       | CSSProperties     | -      |
+| 属性        | 说明                                       | 类型          | 默认值 |
+| ----------- | ------------------------------------------ | ------------- | ------ |
+| active      | 是否展示动画效果                           | boolean       | false  |
+| className   | 类名                                       | string        | -      |
+| loading     | 为 true 时,显示占位元素。反之则显示子组件 | boolean       | true   |
+| placeholder | 加载等待时的占位元素                       | ReactNode     | -      |
+| style       | 样式                                       | CSSProperties | -      |
 
 ### Skeleton.Avatar
 
-> `Skeleton.Image`,`Skeleton.Title`,`Skeleton.Button` API 与 `Skeleton.Avatar` 相同
+> `Skeleton.Image`,`Skeleton.Title`,`Skeleton.Button` 大部分API 与 `Skeleton.Avatar` 相同。其中 shape 仅 `Skeleton.Avatar支持`
 
 | 属性 | 说明 | 类型 | 默认值 |
 | --- | --- | --- | --- |
 | className | 类名 | string | - |
 | size | 设置头像的大小,支持 `extra-extra-small`, `extra-small`、`small`、`medium`、`large`、`extra-large` **v>=1.0** | string | `medium` |
 | style | 样式 | CSSProperties | - |
+| shape | 指定头像的形状,支持 `circle`、`square` | string | `circle` |
 
 ### Skeleton.Paragraph
 
-| 属性      | 说明                 | 类型   | 默认值 |
-| --------- | -------------------- | ------ | ------ |
-| className | 类名                 | string | -      |
-| rows      | 设置段落占位图的行数 | number | 4      |
+| 属性      | 说明                 | 类型          | 默认值 |
+| --------- | -------------------- | ------------- | ------ |
+| className | 类名                 | string        | -      |
+| rows      | 设置段落占位图的行数 | number        | 4      |
 | style     | 样式                 | CSSProperties | -      |
 
 ## 文案规范
-- 不变的固定内容直接展示固定内容,可变的内容使用骨架屏展示
-
-
 
+-   不变的固定内容直接展示固定内容,可变的内容使用骨架屏展示
 
 ## 设计变量
 

+ 1 - 1
content/feedback/spin/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 69
+order: 70
 category: Feedback
 title: Spin
 subTitle: Spin

+ 1 - 1
content/feedback/spin/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 69
+order: 70
 category: 反馈类
 title: Spin 加载器
 icon: doc-spin

+ 28 - 1
content/feedback/toast/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 70
+order: 71
 category: Feedback
 title: Toast
 subTitle: Toast
@@ -237,6 +237,33 @@ function Demo() {
 render(Demo);
 ```
 
+### Update Toast Content
+
+Use unique Toast `id` to update toast content.
+
+```jsx live=true noInline=true hideInDSM
+import React, { useState } from 'react';
+import { Toast, Button } from '@douyinfe/semi-ui';
+
+function Demo() {
+    function show() {
+        const id = 'toastid';
+        Toast.info({ content: 'Update Content By Id', id });
+        setTimeout(() => {
+            Toast.success({ content: 'Id By Content Update', id });
+        }, 1000);
+    }
+
+    return (
+        <Button type="primary" onClick={show}>
+            Update Content By Id
+        </Button>
+    );
+}
+
+render(Demo);
+```
+
 ### useToast Hooks
 
 You could use `Toast.useToast` to create a `contextHolder` that could access context. Created toast will be inserted to where contextHolder is placed.

+ 30 - 3
content/feedback/toast/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 70
+order: 71
 category: 反馈类
 title: Toast 提示
 icon: doc-toast
@@ -238,6 +238,33 @@ function Demo() {
 render(Demo);
 ```
 
+### 更新消息内容
+
+可以通过唯一的 `id` 来更新内容。
+
+```jsx live=true noInline=true hideInDSM
+import React, { useState } from 'react';
+import { Toast, Button } from '@douyinfe/semi-ui';
+
+function Demo() {
+    function show() {
+        const id = 'toastid';
+        Toast.info({ content: 'Update Content By Id', id });
+        setTimeout(() => {
+            Toast.success({ content: 'Id By Content Update', id });
+        }, 1000);
+    }
+
+    return (
+        <Button type="primary" onClick={show}>
+            Update Content By Id
+        </Button>
+    );
+}
+
+render(Demo);
+```
+
 ### Hooks 用法
 
 通过 Toast.useToast 创建支持读取 context 的 contextHolder。此时的 toast 会渲染在 contextHolder 所在的节点处。
@@ -438,8 +465,8 @@ HookToast ( >= 1.2.0 ):
   - 不使用类似于「已读」类的动作,例如 OK, Got it, Dismiss, Cancel
 
 
-| ✅ 推荐用法 | ❌ 不推荐用法 |   
-| --- | --- | 
+| ✅ 推荐用法 | ❌ 不推荐用法 |
+| --- | --- |
 |  <ToastCard type='error' content={<div>Ticket transfer failed <span style={{ color: 'var(--semi-color-primary)', marginLeft: 4, cursor: 'pointer' }}>Retry</span> </div>} /> |  <ToastCard type='error' content={<div>Ticket transfer failed <span style={{ color: 'var(--semi-color-primary)', marginLeft: 4, cursor: 'pointer' }}>Dismiss</span> </div>} /> |
 
 ## 设计变量

+ 57 - 56
content/navigation/navigation/index.md

@@ -42,7 +42,7 @@ class NavApp extends React.Component {
                 bodyStyle={{ height: 320 }}
                 items={[
                     { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
-                    { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+                    { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
                     {
                         text: '任务平台',
                         icon: <IconSetting />,
@@ -82,11 +82,11 @@ class NavApp extends React.Component {
                 defaultOpenKeys={[ 'job' ]}
                 items={[
                     { itemKey: 'user', text: '用户管理' },
-                    { itemKey: 'union', text: '公会中心' },
+                    { itemKey: 'union', text: '活动管理' },
                     {
                         itemKey: 'union-management',
-                        text: '公会管理',
-                        items: ['公告设置', '公会查询', '信息录入']
+                        text: '任务管理',
+                        items: ['公告设置', '任务查询', '信息录入']
                     },
                     {
                         text: '任务平台',
@@ -123,7 +123,7 @@ class NavApp extends React.Component {
                 bodyStyle={{ height: 320 }}
                 items={[
                     { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
-                    { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+                    { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
                     {
                         text: '任务平台',
                         icon: <IconSetting />,
@@ -165,7 +165,7 @@ class NavApp extends React.Component {
                 defaultOpenKeys={['job', 'resource']}
                 items={[
                     { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
-                    { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+                    { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
                     {
                         text: '任务平台',
                         icon: <IconSetting />,
@@ -212,14 +212,14 @@ class NavApp extends React.Component {
                 onClick={data => console.log('trigger onClick: ', data)}
             >
                 <Nav.Header logo={<img src="https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/webcast_logo.svg" />} text={'Semi 运营后台'} />
-                <Nav.Item itemKey={'union'} text={'公会中心'} icon={<IconStar />} />
+                <Nav.Item itemKey={'union'} text={'活动管理'} icon={<IconStar />} />
                 <Nav.Sub itemKey={'user'} text="用户管理" icon={<IconUser />}>
-                    <Nav.Item itemKey={'golder'} text={'金主管理'} />
-                    <Nav.Item itemKey={'ban'} text={'用户封禁'} />
+                    <Nav.Item itemKey={'active'} text={'活跃用户'} />
+                    <Nav.Item itemKey={'negative'} text={'非活跃用户'} />
                 </Nav.Sub>
-                <Nav.Sub itemKey={'union-management'} text="公会管理" icon={<IconUserGroup />}>
-                    <Nav.Item itemKey={'notice'} text={'公告设置'} />
-                    <Nav.Item itemKey={'query'} text={'公会查询'} />
+                <Nav.Sub itemKey={'union-management'} text="任务管理" icon={<IconUserGroup />}>
+                    <Nav.Item itemKey={'notice'} text={'任务设置'} />
+                    <Nav.Item itemKey={'query'} text={'任务查询'} />
                     <Nav.Item itemKey={'info'} text={'信息录入'} />
                 </Nav.Sub>
                 <Nav.Footer collapseButton={true} />
@@ -234,10 +234,9 @@ class NavApp extends React.Component {
 
 Navigation 目前提供两种方向的导航:
 
--   垂直方向(默认)
--   水平方向
+-   垂直方向(默认),  `mode = "vertical"`
+-   水平方向, `mode = "horizontal"`
 
-你可以通过 `mode = "vertical"` (默认)或者 `mode = "horizontal"` 来控制。
 
 特别注意的是,有一些功能(参数)仅在 `mode = "vertical"` 时有效:
 
@@ -260,18 +259,18 @@ class NavApp extends React.Component {
                     bodyStyle={{ height: 320 }}
                     items={[
                         { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
-                        { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+                        { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
                         {
                             itemKey: 'union-management',
-                            text: '公会管理',
+                            text: '任务管理',
                             icon: <IconUserGroup />,
-                            items: ['公告设置', '公会查询', '信息录入']
+                            items: ['任务设置', '任务查询', '信息录入']
                         },
                         {
-                            text: '任务平台',
+                            text: '公告管理',
                             icon: <IconSetting />,
                             itemKey: 'job',
-                            items: ['任务管理', '用户任务查询'],
+                            items: ['推送管理', '已推送查询'],
                         },
                     ]}
                     onSelect={key => console.log(key)}
@@ -304,7 +303,7 @@ class NavApp extends React.Component {
                     mode={'horizontal'}
                     items={[
                         { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
-                        { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+                        { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
                         {
                             itemKey: 'approve-management',
                             text: '审批管理',
@@ -343,7 +342,7 @@ class NavApp extends React.Component {
                                 </Dropdown.Menu>
                             }
                         >
-                            <Avatar size="small" color='light-blue' style={{margin: 4}}>BD</Avatar>
+                            <Avatar size="small" color='light-blue' style={{ margin: 4 }}>BD</Avatar>
                             <span>Bytedancer</span>
                         </Dropdown>
                     }
@@ -367,12 +366,12 @@ class NavApp extends React.Component {
     constructor() {
         this.items = [
             { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
-            { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+            { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
             {
                 itemKey: 'union-management',
-                text: '公会管理',
+                text: '任务管理',
                 icon: <IconUserGroup />,
-                items: ['公告设置', '公会查询', '信息录入']
+                items: ['公告设置', '任务查询', '信息录入']
             },
             {
                 itemKey: 'approve-management',
@@ -419,7 +418,7 @@ class NavApp extends React.Component {
                             <Select.Option value='Korean'>한국어</Select.Option>
                             <Select.Option value='Japanese'>日本語</Select.Option>
                         </Select>
-                        <Button style={{ marginRight: 10 }}>切换至海外版</Button>
+                        <Button style={{ marginRight: 10 }}>切换至全球版</Button>
                         <Dropdown
                             position="bottomRight"
                             render={
@@ -429,7 +428,7 @@ class NavApp extends React.Component {
                                 </Dropdown.Menu>
                             }
                         >
-                            <Avatar size="small" color='light-blue' style={{margin: 4}}>BD</Avatar>
+                            <Avatar size="small" color='light-blue' style={{ margin: 4 }}>BD</Avatar>
                             <span>Bytedancer</span>
                         </Dropdown>
                     </>
@@ -480,7 +479,7 @@ class NavApp extends React.Component {
                 bodyStyle={{ height: 320 }}
                 items={[
                     { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
-                    { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+                    { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
                     {
                         text: '任务平台',
                         icon: <IconSetting />,
@@ -533,13 +532,15 @@ class NavApp extends React.Component {
                             text: '任务1',
                             icon: <IconSetting />,
                             itemKey: 'mission1',
-                            items: ['任务2',{
-                                text: '任务3拆解',
-                                icon: <IconSetting />,
-                                itemKey: 'mission3',
-                                items: ['子任务1', '子任务2'],
-                            }, ],
-                        },],
+                            items: ['任务2',
+                                {
+                                    text: '任务3拆解',
+                                    icon: <IconSetting />,
+                                    itemKey: 'mission3',
+                                    items: ['子任务1', '子任务2'],
+                                }
+                            ],
+                        }],
                     },
                 ]}
                 onSelect={key => console.log(key)}
@@ -580,12 +581,12 @@ class NavApp extends React.Component {
                 bodyStyle={{ height: 320 }}
                 items={[
                     { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
-                    { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+                    { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
                     {
                         itemKey: 'union-management',
-                        text: '公会管理',
+                        text: '任务管理',
                         icon: <IconUserGroup />,
-                        items: ['公告设置', '公会查询', '信息录入']
+                        items: ['公告设置', '任务查询', '信息录入']
                     },
                     {
                         text: '任务平台',
@@ -649,12 +650,12 @@ function NavApp (props = {}) {
 
     const items = useMemo(() => [
         { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
-        { itemKey: 'union', text: '公会中心', icon: <IconStar /> },
+        { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
         {
             itemKey: 'union-management',
-            text: '公会管理',
+            text: '任务管理',
             icon: <IconUserGroup />,
-            items: ['公告设置', '公会查询', '信息录入']
+            items: ['公告设置', '任务查询', '信息录入']
         },
         {
             text: '任务平台',
@@ -690,10 +691,10 @@ function NavApp (props = {}) {
 
 ### Nav
 
-| 属性                | 类型                                                                                                                             | 描述                                                                                                                           | 默认值     |
+| 属性                | 描述                                                                                                                             | 类型                                                                                                                           | 默认值     |
 | ------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------- |
-| bodyStyle           | 导航项列表的自定义样式                                                                             | object                                                                                                                           |           |
-| className           | 最外层元素的样式名                                                                               | boolean                                                                                                                          |           |
+| bodyStyle           | 导航项列表的自定义样式                                                                             | CSSProperties                                                                                                                           |           |
+| className           | 最外层元素的样式名                                                                               | string                                                                                                                          |           |
 | defaultIsCollapsed  | 默认是否处于收起状态,仅 `mode = "vertical"` 时有效                                                    | boolean                                                                                                                          | false     |
 | defaultOpenKeys     | 初始打开的子导航 `itemKey` 数组,仅 `mode = "vertical"` 且侧边栏处于展开状态时有效                               | string[]                                                                                                                         | []        |
 | defaultSelectedKeys | 初始选中的导航项 `itemKey` 数组                                                                   | string[]                                                                                                                         | []        |
@@ -702,7 +703,7 @@ function NavApp (props = {}) {
 | isCollapsed         | 是否处于收起状态的受控属性,仅 `mode = "vertical"` 时有效                                                 | boolean                                                                                                                          |           |
 | items               | 导航项目列表,每一项可以继续带有 items 属性。如果为 string 数组,则会取每一项作为 text 和 itemKey                         | object\| string[] \| [Item](#Nav.Item)[] \| [Sub](#Nav.Sub)[] |  |
 | limitIndent         | 解除缩进限制,可使用 level 自定义导航项缩进,水平模式只能为true >=1.27.0                                          | boolean                                                                                                                          | true      |
-| mode                | 导航类型,目前支持横向与竖直,可选值:`vertical`\                                                          | `horizontal`                                                                                                                     | string    | `vertical`           |
+| mode                | 导航类型,目前支持横向与竖直,可选值:`vertical`/| `horizontal`                                                                                                                     | string    | `vertical`           |
 | openKeys            | 受控的打开的子导航 `itemKey` 数组,配合 `onOpenChange` 回调控制子导航项展开,仅 `mode = "vertical"` 且侧边栏处于展开状态时有效 | string[]                                                                                                                         |           |
 | prefixCls           | 类名前缀                                                                                    | string                                                                                                                           | `semi`    |
 | selectedKeys        | 受控的导航项 `itemKey` 数组,配合 `onSelect` 回调控制导航项选择                                             | string[]                                                                                                                         |           |
@@ -713,10 +714,10 @@ function NavApp (props = {}) {
 | toggleIconPosition  | 带有子导航项的的父级导航项箭头位置 >=1.27.0                                                              | 'left' \| 'right'   | 'right'              |
 | tooltipHideDelay    | tooltip 隐藏的延迟,collapse 为 true 时有效,单位为 ms                                                | number                                                                                                                           | 100       |
 | tooltipShowDelay    | tooltip 显示的延迟,collapse 为 true 时有效,单位为 ms                                                | number                                                                                                                           | 0         |
-| onClick             | 点击任意导航项时触发                                                                              | ({ itemKey: string, domEvent: MouseEvent, isOpen: boolean }) => {}                                                               | () => {}  |
-| onCollapseChange    | 收起状态变化时的回调                                                                              | (isCollapsed: boolean) => {}                                                                                                     | () => {}  |
-| onOpenChange        | 切换某个子导航项目显隐状态时触发                                                                        | ({ itemKey: string, openKeys: string[], domEvent: MouseEvent, isOpen: boolean }) => {}                                           | () => {}  |
-| onSelect            | 第一次选中某个可选中导航项目时触发,其中 selectedItems 这个字段版本 >= 0.17.0 后才支持 | ({ itemKey: string, selectedKeys: string[], selectedItems: [Item](#Nav.Item)[], domEvent: MouseEvent, isOpen: boolean }) => {} | () => {}  |
+| onClick             | 点击任意导航项时触发                                                                              |<ApiType detail='({ itemKey: string, domEvent: MouseEvent, isOpen: boolean }) => void'>(itemKey, event, isOpen)=> void </ApiType>                                                                | () => {}  |
+| onCollapseChange    | 收起状态变化时的回调                                                                              |<ApiType detail='(isCollapsed: boolean) => void'>(isCollapsed)=>{} </ApiType>                                                                                                     | () => {}  |
+| onOpenChange        | 切换某个子导航项目显隐状态时触发                                                                    | <ApiType detail='({ itemKey: string, openKeys: string[], domEvent: MouseEvent, isOpen: boolean }) => void'> ({itemKey, openKeys, event, isOpen})=>{}</ApiType>                                      | () => {}  |
+| onSelect            | 第一次选中某个可选中导航项目时触发,其中 selectedItems 这个字段版本 >= 0.17.0 后才支持 | <ApiType detail='({ itemKey: string, selectedKeys: string[], selectedItems: Item[], domEvent: MouseEvent, isOpen: boolean }) => void'>(onSelectProps)=>{}</ApiType> | () => {}  |
 
 ### Nav.Item
 
@@ -765,13 +766,13 @@ function NavApp (props = {}) {
 
 ### Nav.Footer
 
-| 属性           | 描述                                                                                     | 类型                                      | 默认值 | 版本           |
-| -------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------- | ------ | -------------- |
-| children       | 子元素                                                                                   | ReactNode                                 |        |                |
-| className      | 最外层样式名                                                                             | string                                    |        |                |
-| collapseButton | 是否展示底部“收起侧边栏”按钮,mode="vertical" 且 Footer 组件的 children 参数为空才有效果 | boolean\|ReactNode                        | false  |                |
-| collapseText   | “收起”按钮的文案                                                                         | (collapsed:boolean) => string\|ReactNode |        | 0.35.0 |
-| style          | 最外层样式                                                                               | CSSProperties                                    |        |                |
+| 属性           | 描述                                                                                     | 类型                                      | 默认值 |
+| -------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------- | ------ |
+| children       | 子元素                                                                                   | ReactNode                                 |        |         
+| className      | 最外层样式名                                                                             | string                                    |        |      
+| collapseButton | 是否展示底部“收起侧边栏”按钮,mode="vertical" 且 Footer 组件的 children 参数为空才有效果 | boolean\|ReactNode                        | false  |          
+| collapseText   | “收起”按钮的文案                                                                         | (collapsed:boolean) => string\|ReactNode |        |
+| style          | 最外层样式                                                                               | CSSProperties                                    |        |  
 
 
 ## 文案规范

+ 1 - 0
content/order.js

@@ -52,6 +52,7 @@ const order = [
     'descriptions',
     'dropdown',
     'empty',
+    'image',
     'list',
     'modal',
     'overflowlist',

+ 1 - 1
content/other/configprovider/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 71
+order: 72
 category: Other
 title: ConfigProvider
 icon: doc-configprovider

+ 1 - 1
content/other/configprovider/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 71
+order: 72
 category: 其他
 title:  ConfigProvider 全局配置
 icon: doc-configprovider

+ 1 - 1
content/other/locale/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 72
+order: 73
 category: Other
 title: LocaleProvider
 subTitle: LocaleProvider

+ 1 - 1
content/other/locale/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 72
+order: 73
 category: 其他
 title:  LocaleProvider 多语言
 icon: doc-i18n

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 56 - 0
content/show/image/index-en-US.md


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 56 - 0
content/show/image/index.md


+ 2 - 2
content/show/list/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 54
+order: 55
 category: Show
 title: List
 subTitle: List
@@ -13,7 +13,7 @@ brief: Lists display a set of related contents
 
 ### How to import
 
-```jsx import 
+```jsx import
 import { List } from '@douyinfe/semi-ui';
 ```
 

+ 1 - 1
content/show/list/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 54
+order: 55
 category: 展示类
 title: List 列表
 icon: doc-list

+ 1 - 1
content/show/modal/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 55
+order: 56
 category: Show
 title:  Modal
 subTitle: Modal

+ 228 - 222
content/show/modal/index.md

@@ -1,13 +1,12 @@
 ---
 localeCode: zh-CN
-order: 55
+order: 56
 category: 展示类
-title:  Modal 模态对话框
+title: Modal 模态对话框
 icon: doc-modal
 brief: 模态对话框用于等待用户响应、告知用户重要信息或在不丢失上下文的情况下展示更多信息
 ---
 
-
 ## 代码演示
 
 ### 如何引入
@@ -22,55 +21,41 @@ import { Modal } from '@douyinfe/semi-ui';
 import React from 'react';
 import { Modal, Button } from '@douyinfe/semi-ui';
 
-class modalDemo extends React.Component {
-    constructor() {
-        super();
-        this.state = {visible: false};
-        this.showDialog = this.showDialog.bind(this);
-        this.handleOk = this.handleOk.bind(this);
-        this.handleCancel = this.handleCancel.bind(this);
-    }
-    showDialog() {
-        this.setState({
-            visible: true
-        });
-    }
-    handleOk(e) {
-        this.setState({
-            visible: false
-        });
+() => {
+    const [visible, setVisible] = useState(false);
+    const showDialog = () => {
+        setVisible(true);
+    };
+    const handleOk = () => {
+        setVisible(false);
         console.log('Ok button clicked');
-    }
-    handleAfterClose(){
-        console.log('After Close callback executed');
-    }
-    handleCancel(e) {
-        this.setState({
-            visible: false
-        });
+    };
+    const handleCancel = () => {
+        setVisible(false);
         console.log('Cancel button clicked');
-    }
-    render() {
-        return (
-            <>
-                <Button onClick={this.showDialog}>打开弹窗</Button>
-                <Modal
-                    title="基本对话框"
-                    visible={this.state.visible}
-                    onOk={this.handleOk}
-                    afterClose={this.handleAfterClose} //>=1.16.0
-                    onCancel={this.handleCancel}
-                    closeOnEsc={true}
-                >
-                    This is the content of a basic modal.
-                    <br/>
-                    More content...
-                </Modal>
-            </>
-        );
-    }
-}
+    };
+    const handleAfterClose = () => {
+        console.log('After Close callback executed');
+    };
 
+    return (
+        <>
+            <Button onClick={showDialog}>打开弹窗</Button>
+            <Modal
+                title="基本对话框"
+                visible={visible}
+                onOk={handleOk}
+                afterClose={handleAfterClose} //>=1.16.0
+                onCancel={handleCancel}
+                closeOnEsc={true}
+            >
+                This is the content of a basic modal.
+                <br />
+                More content...
+            </Modal>
+        </>
+    );
+};
 ```
 
 ### 点击遮罩层不可关闭
@@ -84,24 +69,24 @@ import { Modal, Button } from '@douyinfe/semi-ui';
 class modalDemo extends React.Component {
     constructor() {
         super();
-        this.state = {visible: false};
+        this.state = { visible: false };
         this.showDialog = this.showDialog.bind(this);
         this.handleOk = this.handleOk.bind(this);
         this.handleCancel = this.handleCancel.bind(this);
     }
     showDialog() {
         this.setState({
-            visible: true
+            visible: true,
         });
     }
     handleOk(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     handleCancel(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     render() {
@@ -128,7 +113,7 @@ class modalDemo extends React.Component {
 
 通过设置 `okText` 与 `cancelText` 属性可自定义按钮显示的文字。
 
-注意:命令式调用的 Modal 需要通过这两个属性来设置 i18 的文本,因为我们无法修改React组件树,命令式调用插入的Component无法消费到Locale相关的Context
+注意:命令式调用的 Modal 需要通过这两个属性来设置 i18 的文本,因为我们无法修改 React 组件树,命令式调用插入的 Component 无法消费到 Locale 相关的 Context
 
 ```jsx live=true
 import React from 'react';
@@ -137,24 +122,24 @@ import { Modal, Button } from '@douyinfe/semi-ui';
 class modalDemo extends React.Component {
     constructor() {
         super();
-        this.state = {visible: false};
+        this.state = { visible: false };
         this.showDialog = this.showDialog.bind(this);
         this.handleOk = this.handleOk.bind(this);
         this.handleCancel = this.handleCancel.bind(this);
     }
     showDialog() {
         this.setState({
-            visible: true
+            visible: true,
         });
     }
     handleOk(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     handleCancel(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     render() {
@@ -189,24 +174,24 @@ import { Modal, Button } from '@douyinfe/semi-ui';
 class modalDemo extends React.Component {
     constructor() {
         super();
-        this.state = {visible: false};
+        this.state = { visible: false };
         this.showDialog = this.showDialog.bind(this);
         this.handleOk = this.handleOk.bind(this);
         this.handleCancel = this.handleCancel.bind(this);
     }
     showDialog() {
         this.setState({
-            visible: true
+            visible: true,
         });
     }
     handleOk(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     handleCancel(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     render() {
@@ -218,8 +203,8 @@ class modalDemo extends React.Component {
                     visible={this.state.visible}
                     onOk={this.handleOk}
                     onCancel={this.handleCancel}
-                    okButtonProps={{size: 'small', type: 'warning'}}
-                    cancelButtonProps={{size: 'small', disabled: true}}
+                    okButtonProps={{ size: 'small', type: 'warning' }}
+                    cancelButtonProps={{ size: 'small', disabled: true }}
                 >
                     <p>This is a modal with customized button props.</p>
                     <p>More content...</p>
@@ -241,24 +226,24 @@ import { Modal, Button } from '@douyinfe/semi-ui';
 class modalDemo extends React.Component {
     constructor() {
         super();
-        this.state = {visible: false};
+        this.state = { visible: false };
         this.showDialog = this.showDialog.bind(this);
         this.handleOk = this.handleOk.bind(this);
         this.handleCancel = this.handleCancel.bind(this);
     }
     showDialog() {
         this.setState({
-            visible: true
+            visible: true,
         });
     }
     handleOk(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     handleCancel(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     render() {
@@ -287,8 +272,7 @@ class modalDemo extends React.Component {
 
 ### 自定义对话框的样式
 
-通过设置 `style` 可以自定义样式及位置如 `style.top`,也可以通过 `centered` 使对话框居中显示。
-也可以通过设置 `maskStyle` 自定义遮罩样式,及 `bodyStyle` 自定义对话框内容样式。
+通过设置 `style` 可以自定义样式及位置如 `style.top`,也可以通过 `centered` 使对话框居中显示。也可以通过设置 `maskStyle` 自定义遮罩样式,及 `bodyStyle` 自定义对话框内容样式。
 
 ```jsx live=true
 import React from 'react';
@@ -297,24 +281,24 @@ import { Modal, Button } from '@douyinfe/semi-ui';
 class modalDemo extends React.Component {
     constructor() {
         super();
-        this.state = {visible: false};
+        this.state = { visible: false };
         this.showDialog = this.showDialog.bind(this);
         this.handleOk = this.handleOk.bind(this);
         this.handleCancel = this.handleCancel.bind(this);
     }
     showDialog() {
         this.setState({
-            visible: true
+            visible: true,
         });
     }
     handleOk(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     handleCancel(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     render() {
@@ -327,15 +311,29 @@ class modalDemo extends React.Component {
                     onOk={this.handleOk}
                     onCancel={this.handleCancel}
                     centered
-                    bodyStyle={{overflow: 'auto', height: 200}}
+                    bodyStyle={{ overflow: 'auto', height: 200 }}
                 >
-                    <p style={{lineHeight: 1.8}}>Semi Design 是由互娱社区前端团队与 UED 团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。</p>
-                    <p style={{lineHeight: 1.8}}>区别于其他的设计系统而言,Semi Design 以用户中心、内容优先、设计人性化为设计理念,具有以下优势:</p>
+                    <p style={{ lineHeight: 1.8 }}>
+                        Semi Design 是由互娱社区前端团队与 UED
+                        团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的
+                        Web 应用。
+                    </p>
+                    <p style={{ lineHeight: 1.8 }}>
+                        区别于其他的设计系统而言,Semi Design 以用户中心、内容优先、设计人性化为设计理念,具有以下优势:
+                    </p>
                     <ul>
-                        <li><p>Semi Design 以内容优先进行设计。</p></li>
-                        <li><p>更容易地自定义主题。</p></li>
-                        <li><p>适用国际化场景。</p></li>
-                        <li><p>效率场景加入人性化关怀</p></li>
+                        <li>
+                            <p>Semi Design 以内容优先进行设计。</p>
+                        </li>
+                        <li>
+                            <p>更容易地自定义主题。</p>
+                        </li>
+                        <li>
+                            <p>适用国际化场景。</p>
+                        </li>
+                        <li>
+                            <p>效率场景加入人性化关怀</p>
+                        </li>
                     </ul>
                 </Modal>
             </>
@@ -356,42 +354,42 @@ import { IconVigoLogo, IconSemiLogo } from '@douyinfe/semi-icons';
 class modalDemo extends React.Component {
     constructor() {
         super();
-        this.state = {visible: false};
+        this.state = { visible: false };
         this.showDialog = this.showDialog.bind(this);
         this.handleOk = this.handleOk.bind(this);
         this.handleCancel = this.handleCancel.bind(this);
     }
     showDialog() {
         this.setState({
-            visible: true
+            visible: true,
         });
     }
     handleOk(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     handleCancel(e) {
         this.setState({
-            visible: false
+            visible: false,
         });
     }
     render() {
         const data = [
             {
-                icon: <IconSemiLogo style={{fontSize: 48}} />,
+                icon: <IconSemiLogo style={{ fontSize: 48 }} />,
                 title: 'Boost new feature adoption with Integration',
-                content: 'Sample data is prepared for you to demostrate how Integration may be useful for your team'
+                content: 'Sample data is prepared for you to demostrate how Integration may be useful for your team',
             },
             {
-                icon: <IconVigoLogo style={{fontSize: 48}} />,
+                icon: <IconVigoLogo style={{ fontSize: 48 }} />,
                 title: 'Introducing Dark Mode',
-                content: 'Sample data is prepared for you to demostrate how Integration may be useful for your team'
+                content: 'Sample data is prepared for you to demostrate how Integration may be useful for your team',
             },
             {
-                icon: <IconSemiLogo style={{fontSize: 48}} />,
+                icon: <IconSemiLogo style={{ fontSize: 48 }} />,
                 title: 'New List Component',
-                content: 'Sample data is prepared for you to demostrate how Integration may be useful for your team'
+                content: 'Sample data is prepared for you to demostrate how Integration may be useful for your team',
             },
         ];
         const btnStyle = {
@@ -399,7 +397,7 @@ class modalDemo extends React.Component {
             margin: '4px 50px',
         };
         const footer = (
-            <div style={{textAlign: 'center'}}>
+            <div style={{ textAlign: 'center' }}>
                 <Button type="primary" theme="solid" onClick={this.handleOk} style={btnStyle}>
                     Continue
                 </Button>
@@ -418,7 +416,7 @@ class modalDemo extends React.Component {
                     onCancel={this.handleCancel}
                     footer={footer}
                 >
-                    <h3 style={{textAlign: 'center', fontSize: 24, margin: 40}}>Semi Design New Features</h3>
+                    <h3 style={{ textAlign: 'center', fontSize: 24, margin: 40 }}>Semi Design New Features</h3>
                     <List
                         dataSource={data}
                         split={false}
@@ -427,12 +425,14 @@ class modalDemo extends React.Component {
                                 header={item.icon}
                                 main={
                                     <div>
-                                        <h6 style={{margin: 0, fontSize: 16}}>{item.title}</h6>
-                                        <p style={{marginTop: 4, color: 'var(--semi-color-text-1)'}}>{item.content}</p>
+                                        <h6 style={{ margin: 0, fontSize: 16 }}>{item.title}</h6>
+                                        <p style={{ marginTop: 4, color: 'var(--semi-color-text-1)' }}>
+                                            {item.content}
+                                        </p>
                                     </div>
                                 }
-                            />)
-                        }
+                            />
+                        )}
                     />
                 </Modal>
             </>
@@ -441,8 +441,7 @@ class modalDemo extends React.Component {
 }
 ```
 
-
-### 全屏Modal
+### 全屏 Modal
 
 使用 `fullScreen={true}` 可以开启全屏对话框
 
@@ -458,13 +457,7 @@ import { Modal, Button } from '@douyinfe/semi-ui';
     return (
         <>
             <Button onClick={() => setVisible(true)}>打开全屏弹窗</Button>
-            <Modal
-                title="全屏对话框标题"
-                fullScreen
-                visible={visible}
-                onOk={onClose}
-                onCancel={onClose}
-            >
+            <Modal title="全屏对话框标题" fullScreen visible={visible} onOk={onClose} onCancel={onClose}>
                 <p>This is a full screen modal</p>
                 <p>More content...</p>
             </Modal>
@@ -473,8 +466,6 @@ import { Modal, Button } from '@douyinfe/semi-ui';
 };
 ```
 
-
-
 ### 命令式调用
 
 使用 `confirm()` 可以设置一个确认框。支持各种类型的信息提示。命令式调用也可以自定义 icon , 支持 string 和 ReactNode 类型。其他 Modal 支持的 props 都可以传入。
@@ -486,54 +477,68 @@ import { IconSend } from '@douyinfe/semi-icons';
 
 ModalComponent = function(props) {
     function success() {
-        Modal.success({ 'title': 'This is a success message', 'content': 'bla bla bla...'});
+        Modal.success({ title: 'This is a success message', content: 'bla bla bla...' });
     }
 
     function info() {
-        Modal.info({ 'title': 'Here is some info', 'content': 'bla bla bla...' });
+        Modal.info({ title: 'Here is some info', content: 'bla bla bla...' });
     }
 
     function error() {
-        Modal.error({ 'title': 'Unfortunately, there is an error', 'content': 'bla bla bla...' });
+        Modal.error({ title: 'Unfortunately, there is an error', content: 'bla bla bla...' });
     }
 
     function warning() {
-        Modal.warning({ 'title': 'Warning: be cautious ahead', 'content': 'bla bla bla...' });
+        Modal.warning({ title: 'Warning: be cautious ahead', content: 'bla bla bla...' });
     }
 
     function confirm() {
-        Modal.confirm({ 'title': 'Are you sure ?', 'content': 'bla bla bla...' });
+        Modal.confirm({ title: 'Are you sure ?', content: 'bla bla bla...' });
     }
 
     function custom() {
-        Modal.info({ 'title': 'This is a custom modal', 'content': 'bla bla bla...', icon: <IconSend />, cancelButtonProps: { theme: 'borderless' }, okButtonProps: { theme: 'solid' }, });
+        Modal.info({
+            title: 'This is a custom modal',
+            content: 'bla bla bla...',
+            icon: <IconSend />,
+            cancelButtonProps: { theme: 'borderless' },
+            okButtonProps: { theme: 'solid' },
+        });
     }
 
     return (
         <div>
             <Button onClick={info}>Info</Button>
-            <br/>
-            <br/>
+            <br />
+            <br />
             <Button onClick={success}>Success</Button>
-            <br/>
-            <br/>
-            <Button onClick={error} type="danger">Error</Button>
-            <br/>
-            <br/>
-            <Button onClick={warning} type="warning">Warning</Button>
-            <br/>
-            <br/>
-            <Button onClick={confirm} type="primary">Confirm</Button>
-            <br/>
-            <br/>
+            <br />
+            <br />
+            <Button onClick={error} type="danger">
+                Error
+            </Button>
+            <br />
+            <br />
+            <Button onClick={warning} type="warning">
+                Warning
+            </Button>
+            <br />
+            <br />
+            <Button onClick={confirm} type="primary">
+                Confirm
+            </Button>
+            <br />
+            <br />
             <Button onClick={custom}>Custom</Button>
         </div>
     );
 };
 ```
 
-### Hooks用法
-通过  Modal.useModal 创建支持读取 context 的 contextHolder。
+### Hooks 用法
+
+通过 Modal.useModal 创建支持读取 context 的 contextHolder。
+
 ```jsx live=true hideInDSM
 import React from 'react';
 import { ConfigProvider, Button, Modal } from '@douyinfe/semi-ui';
@@ -541,7 +546,7 @@ import en_GB from '@douyinfe/semi-ui/lib/es/locale/source/en_GB';
 
 function Demo(props = {}) {
     const [modal, contextHolder] = Modal.useModal();
-    const config = { 'title': 'This is a success message', 'content': 'Context consumer' };
+    const config = { title: 'This is a success message', content: 'Context consumer' };
 
     return (
         <ConfigProvider locale={en_GB}>
@@ -560,50 +565,48 @@ function Demo(props = {}) {
 }
 ```
 
-
-## API参考
+## API 参考
 
 ### Modal
 
-| 属性              | 说明                             | 类型 | 默认值  |
-| ----------------- | -------------------------------- | -- | ------- |
-| afterClose         | 对话框完全关闭后的回调函数   <br/>**v1.16.0 后提供**           | () => void | 无      |
-| bodyStyle         | 对话框内容的样式                 | CSSProperties | 无      |
-| cancelButtonProps | 取消按钮的 props                 | [ButtonProps](/zh-CN/input/button#API参考) | 无      |
-| cancelText        | 取消按钮的文字                   | string | 无      |
-| centered          | 是否居中显示                     | boolean | false   |
-| className         | 可用于设置样式类名               | string | 无      |
-| closable          | 是否显示右上角的关闭按钮         | boolean | true    |
-| closeIcon            | 关闭按钮的icon  <br/>**v1.0.0 后提供**                                              | ReactNode     | <IconClose /\>    |
-| closeOnEsc              | 允许通过键盘事件Esc触发关闭  <br/>**v1.0.0 后提供**                                               | boolean | true       | 
-| confirmLoading    | 确认按钮 loading                 | boolean | false   |
-| content            | 对话框内容            | ReactNode  | 无      |
-| footer            | 对话框底部                       | ReactNode | 无      |
-| fullScreen        | 对话是否是全屏(会覆盖 width height)  <br/>**v1.18.0 后提供**                          | boolean     | false      |
-| getPopupContainer | 指定父级 DOM,弹层将会渲染至该 DOM 中,自定义需要设置 `position: relative` <br/>**v0.33.0 后提供**  | () => HTMLElement | () => document.body |     
-| hasCancel        | 是否显示取消按钮                  | boolean | true      |
-| header            | 对话框头部                       | ReactNode | 无      |
-| height            | 高度                             | number | 无      |
-| icon              | 自定义icon       <br/>**v1.1.0 后提供**                                          | ReactNode | -       |
-| keepDOM | 关闭对话框时是否销毁 <br/>**v1.0.0 后提供**  | boolean | false |
-| lazyRender | 配合 keepDOM 使用,为 true 时挂载时不会渲染对话框组件 <br/>**v1.0.0 后提供**  | boolean | true |   
-| mask              | 是否显示遮罩                     | boolean | true    |
-| maskClosable      | 是否允许通过点击遮罩来关闭对话框 | boolean | true    |
-| maskStyle         | 遮罩的样式                       | CSSProperties | 无      |
-| motion         | 动画效果开关                      | boolean    | true      |
-| okButtonProps     | 确认按钮的 props                 | [ButtonProps](/zh-CN/input/button#API参考) | 无      |
-| okText            | 确认按钮的文字                   | string | 无      |
-| okType            | 确认按钮的类型, 可选: 'primary'、'secondary'、'tertiary'、'warning'、'danger'                   | string | primary |
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| afterClose | 对话框完全关闭后的回调函数 <br/>**v1.16.0 后提供** | () => void | 无 |
+| bodyStyle | 对话框内容的样式 | CSSProperties | 无 |
+| cancelButtonProps | 取消按钮的 props | [ButtonProps](/zh-CN/input/button#API参考) | 无 |
+| cancelText | 取消按钮的文字 | string | 无 |
+| centered | 是否居中显示 | boolean | false |
+| className | 可用于设置样式类名 | string | 无 |
+| closable | 是否显示右上角的关闭按钮 | boolean | true |
+| closeIcon | 关闭按钮的 icon <br/>**v1.0.0 后提供** | ReactNode | <IconClose /\> |
+| closeOnEsc | 允许通过键盘事件 Esc 触发关闭 <br/>**v1.0.0 后提供** | boolean | true |
+| confirmLoading | 确认按钮 loading | boolean | false |
+| content | 对话框内容 | ReactNode | 无 |
+| footer | 对话框底部 | ReactNode | 无 |
+| fullScreen | 对话是否是全屏(会覆盖 width height) <br/>**v1.18.0 后提供** | boolean | false |
+| getPopupContainer | 指定父级 DOM,弹层将会渲染至该 DOM 中,自定义需要设置 `position: relative` <br/>**v0.33.0 后提供** | () => HTMLElement | () => document.body |
+| hasCancel | 是否显示取消按钮 | boolean | true |
+| header | 对话框头部 | ReactNode | 无 |
+| height | 高度 | number | 无 |
+| icon | 自定义 icon <br/>**v1.1.0 后提供** | ReactNode | - |
+| keepDOM | 关闭对话框时是否销毁 <br/>**v1.0.0 后提供** | boolean | false |
+| lazyRender | 配合 keepDOM 使用,为 true 时挂载时不会渲染对话框组件 <br/>**v1.0.0 后提供** | boolean | true |
+| mask | 是否显示遮罩 | boolean | true |
+| maskClosable | 是否允许通过点击遮罩来关闭对话框 | boolean | true |
+| maskStyle | 遮罩的样式 | CSSProperties | 无 |
+| motion | 动画效果开关 | boolean | true |
+| okButtonProps | 确认按钮的 props | [ButtonProps](/zh-CN/input/button#API参考) | 无 |
+| okText | 确认按钮的文字 | string | 无 |
+| okType | 确认按钮的类型, 可选: 'primary'、'secondary'、'tertiary'、'warning'、'danger' | string | primary |
 | preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法,不包含用户传入的组件 | boolean |  |  |
-| size | 对话框宽度尺寸,支持 `small`(448px), `medium`(684px), `large`(920px),`full-width`(100vw - 64px) <br/>**v1.0.0 后提供**  | string | 'small' |     
-| style             | 可用于设置样式                   | CSSProperties | 无      |
-| title             | 对话框的标题                     | ReactNode | 无      |
-| visible           | 对话框是否可见                   | boolean | false   |
-| width             | 宽度                             | number | 448     |
-| zIndex            | 遮罩的 z-index 值                | number | 1000    |
-| onCancel          | 取消对话框时的回调函数           | (e: any) => void \| Promise<any\>  | 无      |
-| onOk              | 点击确认按钮时的回调函数         | (e: any) => void \| Promise<any\>  | 无      |
-
+| size | 对话框宽度尺寸,支持 `small`(448px), `medium`(684px), `large`(920px),`full-width`(100vw - 64px) <br/>**v1.0.0 后提供** | string | 'small' |
+| style | 可用于设置样式 | CSSProperties | 无 |
+| title | 对话框的标题 | ReactNode | 无 |
+| visible | 对话框是否可见 | boolean | false |
+| width | 宽度 | number | 448 |
+| zIndex | 遮罩的 z-index 值 | number | 1000 |
+| onCancel | 取消对话框时的回调函数 | (e: any) => void \| Promise<any\> | 无 |
+| onOk | 点击确认按钮时的回调函数 | (e: any) => void \| Promise<any\> | 无 |
 
 ### Modal.method()
 
@@ -613,32 +616,32 @@ function Demo(props = {}) {
 -   `Modal.warning`
 -   `Modal.confirm`
 
-| 属性              | 说明                                                             | 类型              | 默认值  |
-| ----------------- | ---------------------------------------------------------------- | ----------------- | ------- |
-| bodyStyle         | 对话框内容的样式                                                 | CSSProperties            | 无      |
-| cancelButtonProps | 取消按钮的 props                                                 | [ButtonProps](/zh-CN/input/button#API参考)            | 无      |
-| cancelText        | 取消按钮的文字                                                   | string            | 无      |
-| centered          | 是否居中显示                                                     | boolean           | false   |
-| className         | 可用于设置样式类名                                               | string            | 无      |
-| closable          | 是否显示右上角的关闭按钮                                         | boolean           | true    |
-| confirmLoading    | 确认按钮 loading                                                 | boolean           | false   |
-| content            | 对话框内容                                                      | ReactNode         | 无      |
-| footer            | 对话框底部                                                       | ReactNode         | 无      |
-| header            | 对话框头部                                                       | ReactNode         | 无      |
-| height            | 高度                                                             | number            | 无      |
-| icon              | 自定义icon                                                 | ReactNode | -       |
-| mask              | 是否显示遮罩                                                     | boolean           | true    |
-| maskClosable      | 是否允许通过点击遮罩来关闭对话框                                 | boolean           | true    |
-| maskStyle         | 遮罩的样式                                                       | CSSProperties            | 无      |
-| okButtonProps     | 确认按钮的 props                                                 | [ButtonProps](/zh-CN/input/button#API参考)            | 无      |
-| okText            | 确认按钮的文字                                                   | string            | 无      |
-| okType            | 确认按钮的类型                                                   | string            | primary |
-| style             | 可用于设置样式                                                   | CSSProperties            | 无      |
-| title             | 对话框的标题                                                     | ReactNode            | 无      |
-| width             | 宽度                                                             | number            | 520     |
-| zIndex            | 遮罩的 z-index 值                                                | number            | 1000    |
-| onCancel          | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭     | (e: any) => void \| Promise<any\>          | 无      |
-| onOk              | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | (e: any) => void \| Promise<any\>          | 无      |
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| bodyStyle | 对话框内容的样式 | CSSProperties | 无 |
+| cancelButtonProps | 取消按钮的 props | [ButtonProps](/zh-CN/input/button#API参考) | 无 |
+| cancelText | 取消按钮的文字 | string | 无 |
+| centered | 是否居中显示 | boolean | false |
+| className | 可用于设置样式类名 | string | 无 |
+| closable | 是否显示右上角的关闭按钮 | boolean | true |
+| confirmLoading | 确认按钮 loading | boolean | false |
+| content | 对话框内容 | ReactNode | 无 |
+| footer | 对话框底部 | ReactNode | 无 |
+| header | 对话框头部 | ReactNode | 无 |
+| height | 高度 | number | 无 |
+| icon | 自定义 icon | ReactNode | - |
+| mask | 是否显示遮罩 | boolean | true |
+| maskClosable | 是否允许通过点击遮罩来关闭对话框 | boolean | true |
+| maskStyle | 遮罩的样式 | CSSProperties | 无 |
+| okButtonProps | 确认按钮的 props | [ButtonProps](/zh-CN/input/button#API参考) | 无 |
+| okText | 确认按钮的文字 | string | 无 |
+| okType | 确认按钮的类型 | string | primary |
+| style | 可用于设置样式 | CSSProperties | 无 |
+| title | 对话框的标题 | ReactNode | 无 |
+| width | 宽度 | number | 520 |
+| zIndex | 遮罩的 z-index 值 | number | 1000 |
+| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | (e: any) => void \| Promise<any\> | 无 |
+| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | (e: any) => void \| Promise<any\> | 无 |
 
 以上函数调用后,会返回一个引用,可以通过该引用更新和关闭弹窗。
 
@@ -653,58 +656,61 @@ modal.update({
 modal.destroy();
 ```
 
--   `Modal.destroyAll` **v>=0.37.0**  
+-   `Modal.destroyAll` **v>=0.37.0**
 
 使用 Modal.destroyAll() 可以销毁命令式及以上`.info()`等创建的弹窗。
 
 -   `Modal.useModal` **v>=1.2.0**  
-当你需要使用 Context 时,可以通过 Modal.useModal 创建一个 contextHolder 插入相应的节点中。此时通过 hooks 创建的 Modal 将会得到 contextHolder 所在位置的所有上下文。创建的 modal 对象拥有与 [Modal.method](#Modal.method()) 相同的创建通知方法。
+    当你需要使用 Context 时,可以通过 Modal.useModal 创建一个 contextHolder 插入相应的节点中。此时通过 hooks 创建的 Modal 将会得到 contextHolder 所在位置的所有上下文。创建的 modal 对象拥有与 [Modal.method](<#Modal.method()>) 相同的创建通知方法。
 
 ## Accessibility
 
 ### ARIA
+
 WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/alertdialog/
-- role 设置为 `dialog`
-- aria-modal 设置为 true
-- aria-labelledby 对应 Modal header
-- aria-describedby 对应 Modal body
+
+-   role 设置为 `dialog`
+-   aria-modal 设置为 true
+-   aria-labelledby 对应 Modal header
+-   aria-describedby 对应 Modal body
 
 ### 键盘和焦点
-- Modal 在弹出时自动获得焦点,关闭时焦点自动回归到打开前元素。
-- 键盘用户可以使用 `Tab` 键和 `Shift + Tab`,将焦点在 Modal 内移动,包括 Modal 自带的关闭按钮和确定取消按钮,此时 Modal 背后元素不可被 tab 聚焦。
-- Modal 打开时默认聚焦到取消按钮, 可通过在 cancelButtonProps 或 okButtonProps 传入 autoFocus 来控制该行为。
-- 可通过在 Modal 内容中需要聚焦的表单元素上添加 autoFocus 来让 Modal 打开时自动聚焦到该元素 (需同时设置 cancelButtonProps 的 autoFocus 为 false)。
-- 修改 closeOnEsc 默认值为 true,允许用户通过键盘直接关闭 Modal 带来更好的体验
 
+-   Modal 在弹出时自动获得焦点,关闭时焦点自动回归到打开前元素。
+-   键盘用户可以使用 `Tab` 键和 `Shift + Tab`,将焦点在 Modal 内移动,包括 Modal 自带的关闭按钮和确定取消按钮,此时 Modal 背后元素不可被 tab 聚焦。
+-   Modal 打开时默认聚焦到取消按钮, 可通过在 cancelButtonProps 或 okButtonProps 传入 autoFocus 来控制该行为。
+-   可通过在 Modal 内容中需要聚焦的表单元素上添加 autoFocus 来让 Modal 打开时自动聚焦到该元素 (需同时设置 cancelButtonProps 的 autoFocus 为 false)。
+-   修改 closeOnEsc 默认值为 true,允许用户通过键盘直接关闭 Modal 带来更好的体验
 
 ## 文案规范
 
-- 命令式 Modal 与 默认 Modal 两种模态对话框的标题使用 动词 + 名词 的格式,无论是陈述句还是问句
+-   命令式 Modal 与 默认 Modal 两种模态对话框的标题使用 动词 + 名词 的格式,无论是陈述句还是问句
 
-| ✅ 推荐用法 | ❌ 不推荐用法 |   
-| --- | --- | 
-| Edit ticket | Edit |
+| ✅ 推荐用法   | ❌ 不推荐用法                         |
+| ------------- | ------------------------------------- |
+| Edit ticket   | Edit                                  |
 | Delete form? | Are you sure you want to delete form? |
 
-- 两种模态对话框的操作按钮在保证标题描述清楚的前提下,只需要使用标题内的动词即可
+-   两种模态对话框的操作按钮在保证标题描述清楚的前提下,只需要使用标题内的动词即可
 
-| ✅ 推荐用法 | ❌ 不推荐用法 |   
-| --- | --- | 
-| Edit | Edit ticket |
+| ✅ 推荐用法 | ❌ 不推荐用法 |
+| ----------- | ------------- |
+| Edit        | Edit ticket   |
 
-- 命令式 Modal 的正文规范
-  - 对标题进行具体的解释说明,不要重复标题的信息
-  - 确保用户知道在必要时如何采取行动
+-   命令式 Modal 的正文规范
+    -   对标题进行具体的解释说明,不要重复标题的信息
+    -   确保用户知道在必要时如何采取行动
 
 ## 设计变量
+
 <DesignToken/>
 
 ## FAQ
 
--   为什么使用 LocaleProvider 后, Modal.confirm 确认、取消按钮的文本没有国际化?
-    Modal 使用 Portal 将浮层节点插入到 DOM 树中。但这个操作仅能改变节点在 DOM 树中的位置,无法改变节点在 React 节点树中的位置,LocalProvider是基于 Contenxt 机制传递的,必须是从属的 React 子结点才可消费到 Local 相关 Contenxt。因此命令式的 Modal 的内置文本无法自动适配国际化。
-    你可以通过 `okText` 和 `cancelText` 这两个属性来根据 Locale 重新设置 i18 的文本。   
-    在1.2版本之后,你也可以通过 Modal.useModal 方法来返回 modal 实体以及 contextHolder 节点。将 contextHolder 插入到你需要获取 context 位置,即可使 Modal 获取到对应的 Context,如 ConfigProvider 或者 LocaleProvider 的配置。
+-   **为什么使用 LocaleProvider 后, Modal.confirm 确认、取消按钮的文本没有国际化?**  
+    Modal 使用 Portal 将浮层节点插入到 DOM 树中。但这个操作仅能改变节点在 DOM 树中的位置,无法改变节点在 React 节点树中的位置,LocalProvider 是基于 Contenxt 机制传递的,必须是从属的 React 子结点才可消费到 Local 相关 Contenxt。因此命令式的 Modal 的内置文本无法自动适配国际化。  
+    你可以通过 `okText` 和 `cancelText` 这两个属性来根据 Locale 重新设置 i18 的文本。  
+    在 1.2 版本之后,你也可以通过 Modal.useModal 方法来返回 modal 实体以及 contextHolder 节点。将 contextHolder 插入到你需要获取 context 位置,即可使 Modal 获取到对应的 Context,如 ConfigProvider 或者 LocaleProvider 的配置。
 
 <!-- ## 相关物料
 ```material

+ 1 - 1
content/show/overflowlist/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 56
+order: 57
 category: Show
 title: OverflowList
 subTitle: OverflowList

+ 1 - 1
content/show/overflowlist/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 56
+order: 57
 category: 展示类
 title: OverflowList 折叠列表
 icon: doc-overflowList

+ 1 - 1
content/show/popover/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 57
+order: 58
 category: Show
 title: Popover
 subTitle: Popover

+ 1 - 1
content/show/popover/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 57
+order: 58
 category: 展示类
 title: Popover 气泡卡片
 icon: doc-popover

+ 1 - 1
content/show/scrolllist/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 58
+order: 59
 category: Show
 title:  ScrollList
 subTitle: ScrollList

+ 1 - 1
content/show/scrolllist/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 58
+order: 59
 category: 展示类
 title: ScrollList 滚动列表
 icon: doc-scrolllist

+ 1 - 1
content/show/sidesheet/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 59
+order: 60
 category: Show
 title: SideSheet
 subTitle: SideSheet

+ 1 - 1
content/show/sidesheet/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 59
+order: 60
 category: 展示类
 title: SideSheet 滑动侧边栏
 icon: doc-sidesheet

+ 1 - 1
content/show/table/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 60
+order: 61
 category: Show
 title: Table
 subTitle: Table

+ 1 - 1
content/show/table/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 60
+order: 61
 category: 展示类
 title:  Table 表格
 icon: doc-table

+ 18 - 1
content/show/tag/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 61
+order: 62
 category: Show
 title: Tag
 subTitle: Tag
@@ -57,6 +57,22 @@ import { Tag } from '@douyinfe/semi-ui';
 );
 ```
 
+### Shape
+
+Supports two Shape: `square`(default)、`circle`。
+
+```jsx live=true
+import React from 'react';
+import { Tag, Space } from '@douyinfe/semi-ui';
+
+() => (
+    <Space>
+        <Tag size="small" shape='circle'> small circle tag </Tag>
+        <Tag size="large" shape='circle'> large circle tag </Tag>
+    </Space>
+);
+```
+
 ### Color
 
 Tag supports 16 colors including whites from Semi's palette: `amber`, `blue`, `cyan`, `green`, `grey`, `indigo`, `light-blue`, `light-green`, `lime`, `orange`, `pink`, `purple`, `red`, `teal`, `violet`, `yellow`, `white`. You can also customize color through `style`.
@@ -294,6 +310,7 @@ class TagGroupCloseableDemo extends React.Component {
 | className | Class name | string |  |  |
 | closable | Toggle whether the tag can be closed | boolean | false |  |
 | color | Color of tags, one of `amber`、 `blue`、 `cyan`、 `green`、 `grey`、 `indigo`、 `light-blue`、 `light-green`、 `lime`、 `orange`、 `pink`、 `purple`、 `red`、 `teal`、 `violet`、 `yellow`、 `white` | string | `grey` |  |
+| shape | Shape of tag, one of `square`、 `circle` | string | `square` | 2.20.0 |
 | size | Size, one of `small`, `large` | string | `small` |  |
 | style | Inline style | object |  |  |
 | type | Style type, one of `ghost`, `solid`, `light` | string | `light` |  |

+ 18 - 1
content/show/tag/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 61
+order: 62
 category: 展示类
 title: Tag 标签
 icon: doc-tag
@@ -53,6 +53,22 @@ import { Tag, Space } from '@douyinfe/semi-ui';
 );
 ```
 
+### 形状
+
+默认定义了两种形状:`square`(默认)、`circle`。
+
+```jsx live=true
+import React from 'react';
+import { Tag, Space } from '@douyinfe/semi-ui';
+
+() => (
+    <Space>
+        <Tag size="small" shape='circle'> small circle tag </Tag>
+        <Tag size="large" shape='circle'> large circle tag </Tag>
+    </Space>
+);
+```
+
 ### 颜色
 
 标签支持默认色板的 16 种颜色和白色,包括:`amber`、 `blue`、 `cyan`、 `green`、 `grey`、 `indigo`、 `light-blue`、 `light-green`、 `lime`、 `orange`、 `pink`、 `purple`、 `red`、 `teal`、 `violet`、 `yellow`、 `white`,也可以通过 style 来自定义颜色样式。
@@ -282,6 +298,7 @@ class TagGroupCloseableDemo extends React.Component {
 | className | 类名 | string |     | |
 | closable | 标签是否可以关闭 | boolean  |  false   | |
 | color  | 标签的颜色,可选 `amber`、 `blue`、 `cyan`、 `green`、 `grey`、 `indigo`、 `light-blue`、 `light-green`、 `lime`、 `orange`、 `pink`、 `purple`、 `red`、 `teal`、 `violet`、 `yellow`、 `white` | string  | `grey`| |
+| shape | 标签的形状,可选 `square`、 `circle` | string | `square` | 2.20.0 |
 | size | 标签的尺寸,可选 `small`、 `large` | string | `small` | |
 | style | 样式 | CSSProperties |     | |
 | type  | 标签的样式类型,可选 `ghost`、 `solid`、 `light` | string  | `light`     | |

+ 1 - 1
content/show/timeline/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 62
+order: 63
 category: Show
 title:  Timeline
 subTitle: Timeline

+ 1 - 1
content/show/timeline/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 62
+order: 63
 category: 展示类
 title: Timeline 时间轴
 icon: doc-timeline

+ 1 - 1
content/show/tooltip/index-en-US.md

@@ -1,6 +1,6 @@
 ---
 localeCode: en-US
-order: 63
+order: 64
 category: Show
 title: Tooltip
 subTitle: Tooltip

+ 1 - 1
content/show/tooltip/index.md

@@ -1,6 +1,6 @@
 ---
 localeCode: zh-CN
-order: 63
+order: 64
 category: 展示类
 title: Tooltip 工具提示
 icon: doc-tooltip

+ 30 - 0
content/start/changelog/index-en-US.md

@@ -16,6 +16,36 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
 
 ---
 
+#### 🎉 2.20.0-beta.1 (2022-09-20)
+- 【Fix】
+    - Fix the issue that `@douyinfe/semi-foundation` Image relative file not export
+
+#### 🎉 2.20.0-beta.0 (2022-09-19)
+
+- 【New Component】
+    - Add Image Component [#344](https://github.com/DouyinFE/semi-design/issues/344)
+- 【Feat】
+    - Tag add shape property, supports `square`、`circle` [#89](https://github.com/DouyinFE/semi-design/issues/89)
+    - Progress supports automatically filling gradient colors according to progress, and automatically switching colors according to progress presets [#1092](https://github.com/DouyinFE/semi-design/issues/1092) [@uiuing](https://github.com/uiuing)
+    - Toast supports dynamic modification of content by ID [#1035](https://github.com/DouyinFE/semi-design/issues/1035) [@gwsbhqt](https://github.com/gwsbhqt)
+    - Skeleton.Avatar supports shape property [#1117](https://github.com/DouyinFE/semi-design/issues/1117) [@MuxinFeng](https://github.com/MuxinFeng)
+- 【Chore】
+    - Remove the ts source code in the package product of @douyinfe/semi-ui, leaving only the lib and dist directories
+    - Modify the writing of some components scss, convert division into multiplication and math.div syntax, to avoid the problem of repeatedly throwing warnings when compiling higher versions of sass
+  
+#### 🎉 2.19.0 (2022-09-09)
+- 【Fix】
+    - fix the issue that the keyboard focus style is blocked after the Anchor spacing can trigger the selection
+    - fix the issue that the Select onblur event was not triggered when the panel was closed
+    - fix Anchor spacing in front of anchor text cannot trigger selection [@edc-hui](https://github.com/edc-hui)
+    - fix the problem that the location of event rendering did not change after Calendar weekStartsOn was changed
+    - fix the problem that Input onEnterPress event does not take effect when using Input in DropDown
+    - fix the problem that tagGroup pollutes incoming tagList data  [#1107 ](https://github.com/DouyinFE/semi-design/issues/1107)
+    - fix DatePicker time will be set 8:00 when switch month and type is dateTime [@rojer95](https://github.com/rojer95)
+- 【Chore】
+    - Remove corejs deps in semi-foundation semi-ui.
+
+
 #### 🎉 2.19.0-beta.0 (2022-09-05)
 - 【Feat】
   - Popconfirm's onOk and onCancel support Promise type return value and close asynchronously [#1056](https://github.com/DouyinFE/semi-design/issues/1056)

+ 29 - 0
content/start/changelog/index.md

@@ -16,6 +16,35 @@ Semi 版本号遵循 **Semver** 规范(主版本号-次版本号-修订版本
 
 ---
 
+#### 🎉 2.20.0-beta.1 (2022-09-20)
+- 【Fix】
+    - 修复 @douyinfe/semi-foundation Image相关未导出的问题
+
+#### 🎉 2.20.0-beta.0 (2022-09-19)
+
+- 【New Component】
+    - 新增 Image 组件 [#344](https://github.com/DouyinFE/semi-design/issues/344)
+- 【Feat】
+    - Tag 新增 shape 选择,可选 square、circle [#89](https://github.com/DouyinFE/semi-design/issues/89)
+    - Progress 支持根据进度自动填充渐变颜色,根据进度预设自动切换颜色 [#1092](https://github.com/DouyinFE/semi-design/issues/1092) [@uiuing](https://github.com/uiuing)
+    - Toast 支持新增通过 ID 动态修改内容 [#1035](https://github.com/DouyinFE/semi-design/issues/1035) [@gwsbhqt](https://github.com/gwsbhqt)
+    - Skeleton.Avatar 支持 shape属性 [#1117](https://github.com/DouyinFE/semi-design/issues/1117) [@MuxinFeng](https://github.com/MuxinFeng)
+- 【Chore】
+    -  移除 @douyinfe/semi-ui 发包产物中的ts源码,仅保留 lib、dist目录
+    -  修改部分组件 scss 的写法,将除法转换为乘法及math.div语法,避免 sass 高版本编译时重复抛出 warning 的问题 
+
+#### 🎉 2.19.0 (2022-09-09)
+- 【Fix】
+    - 修复当 Datepicker 的 type 为 dateTime 时,切换年月会导致时间部分被重置为8点 [#1078](https://github.com/DouyinFE/semi-design/issues/1078) [@rojer95](https://github.com/rojer95)
+    - 修复锚点组件的子节点单行文本前面的空白处,点击不能选中的问题 [#512](https://github.com/DouyinFE/semi-design/issues/512) [@edc-hui](https://github.com/edc-hui)
+    - 修复面板关闭时, Select onblur事件未被触发问题 [#1110](https://github.com/DouyinFE/semi-design/issues/1110)
+    - 修复 Calendar weekStartsOn 改变后,事件渲染的位置没有跟着改变问题 [#1101](https://github.com/DouyinFE/semi-design/issues/1101)
+    - 修复在 DropDown 中使用 Input,Input onEnterPress 事件不生效问题 [#1102](https://github.com/DouyinFE/semi-design/issues/1102)
+    - 修复 TagGroup 污染传入 tagList 数据问题  [#1107](https://github.com/DouyinFE/semi-design/issues/1107)
+    - 修复 Anchor 间距可触发选中后,键盘聚焦样式被遮挡问题 
+- 【Chore】
+    - 移除 semi-foundation 、semi-ui中的 corejs 依赖 [#1095](https://github.com/DouyinFE/semi-design/issues/1095)
+
 #### 🎉 2.19.0-beta.0 (2022-09-05)
 - 【Feat】
   - Popconfirm onOk、onCancel支持 Promise 类型返回值,异步关闭 [#1056 ](https://github.com/DouyinFE/semi-design/issues/1056)

+ 1 - 0
content/start/overview/index-en-US.md

@@ -66,6 +66,7 @@ Collapsible,
 Descriptions,
 Dropdown,
 Empty,
+Image,
 List,
 Modal,
 OverflowList,

+ 1 - 0
content/start/overview/index.md

@@ -67,6 +67,7 @@ Collapsible 折叠,
 Descriptions 描述列表,
 Dropdown 下拉框,
 Empty 空状态,
+Image 图片,
 List 列表,
 Modal 模态对话框,
 OverflowList 折叠列表,

+ 1 - 0
gatsby-node.js

@@ -182,6 +182,7 @@ exports.onCreateWebpackConfig = ({ stage, rules, loaders, plugins, actions }) =>
             "DSM_URL":JSON.stringify(process.env['DSM_URL']),
             'process.env.SEMI_SITE_HEADER':JSON.stringify(process.env.SEMI_SITE_HEADER),
             'process.env.SEMI_SITE_BANNER':JSON.stringify(process.env.SEMI_SITE_BANNER),
+            'process.env.D2C_URL': JSON.stringify(process.env.D2C_URL),
         })],
     });
 };

+ 1 - 1
lerna.json

@@ -1,5 +1,5 @@
 {
     "useWorkspaces": true,
     "npmClient": "yarn",
-    "version": "2.19.0-beta.0"
+    "version": "2.20.0-beta.1"
 }

+ 9 - 9
package.json

@@ -15,7 +15,7 @@
         "develop": "npm run pre-develop && gatsby clean && lerna run build:lib --scope @douyinfe/semi-webpack-plugin --scope eslint-plugin-semi-design && gatsby develop -H 0.0.0.0 --port=3666 --verbose",
         "scripts:changelog": "node scripts/changelog.js",
         "start": "npm run story",
-        "pre-story": "lerna exec --scope=@douyinfe/semi-ui --scope=@douyinfe/semi-foundation -- rm -rf ./lib && lerna run build:lib --scope @douyinfe/semi-webpack-plugin --scope eslint-plugin-semi-design",
+        "pre-story": "lerna exec --scope=@douyinfe/semi-ui --scope=@douyinfe/semi-foundation -- rimraf ./lib && lerna run build:lib --scope @douyinfe/semi-webpack-plugin --scope eslint-plugin-semi-design",
         "story": "npm run pre-story && start-storybook -c ./.storybook/js/ -p 6006",
         "story:ts": "npm run pre-story && && start-storybook -c ./.storybook/ts/ -p 6007",
         "story:ani": "npm run pre-story && && start-storybook -c ./.storybook/animation/react -p 6008",
@@ -34,7 +34,7 @@
         "build:js": "lerna run build:js",
         "build:css": "lerna run build:css",
         "build-storybook": "build-storybook  -c ./.storybook/js/",
-        "build:gatsbydoc": "lerna run build:lib --scope @douyinfe/semi-webpack-plugin --scope eslint-plugin-semi-design && cross-env NODE_ENV=production node --max_old_space_size=16384 ./node_modules/gatsby/cli.js build --prefix-paths --verbose && rm -rf build && mv public build",
+        "build:gatsbydoc": "lerna run build:lib --scope @douyinfe/semi-webpack-plugin --scope eslint-plugin-semi-design && cross-env NODE_ENV=production node --max_old_space_size=16384 ./node_modules/gatsby/cli.js build --prefix-paths --verbose && rimraf build && mv public build",
         "build:icon": "lerna run build:icon --scope='@douyinfe/semi-{icons,illustrations}'",
         "cypress:coverage": "npx wait-on http://127.0.0.1:6006 && ./node_modules/.bin/cypress run",
         "postcypress:coverage": "yarn coverage:merge",
@@ -45,7 +45,7 @@
         "@douyinfe/semi-site-banner": "^0.0.2",
         "@douyinfe/semi-site-doc-style": "0.0.1",
         "@douyinfe/semi-site-header": "^0.0.10",
-        "@douyinfe/semi-site-markdown-blocks": "^0.0.7",
+        "@douyinfe/semi-site-markdown-blocks": "^0.0.8",
         "@mdx-js/react": "^1.6.22",
         "@svgr/core": "^5.5.0",
         "@types/react-window": "^1.8.5",
@@ -88,7 +88,7 @@
         "react-sortable-hoc": "^2.0.0",
         "react-virtualized": "^9.22.3",
         "reset-css": "^5.0.1",
-        "sass": "1.32.13",
+        "sass": "1.54.9",
         "typeface-inconsolata": "0.0.72",
         "typeface-inter": "^3.18.1",
         "unist-util-remove": "^1.0.3",
@@ -113,6 +113,7 @@
         "@babel/types": "^7.15.4",
         "@commitlint/cli": "^9.1.2",
         "@commitlint/config-conventional": "^7.6.0",
+        "@cypress/code-coverage": "^3.9.12",
         "@octokit/rest": "^18.12.0",
         "@shopify/jest-dom-mocks": "^2.11.7",
         "@storybook/addon-a11y": "^6.4.10",
@@ -150,6 +151,8 @@
         "chromatic": "^6.0.6",
         "crypto": "^1.0.1",
         "css-loader": "^3.6.0",
+        "cypress": "9.5.2",
+        "cypress-plugin-tab": "^1.0.5",
         "enzyme": "^3.11.0",
         "enzyme-adapter-react-16": "^1.15.6",
         "enzyme-to-json": "^3.6.2",
@@ -184,7 +187,7 @@
         "react-dnd-cjs": "^9.5.1",
         "react-storybook-addon-props-combinations": "^1.1.0",
         "rimraf": "^2.7.1",
-        "sass-loader": "^7.3.1",
+        "sass-loader": "^10.1.1",
         "semver": "^7.3.5",
         "sha1": "^1.1.1",
         "sinon": "^6.3.5",
@@ -198,10 +201,7 @@
         "webpack": "^4.46.0",
         "webpack-cli": "^3.3.12",
         "webpack-dev-server": "^3.11.2",
-        "webpackbar": "^5.0.0-3",
-        "cypress": "9.5.2",
-        "cypress-plugin-tab": "^1.0.5",
-        "@cypress/code-coverage": "^3.9.12"
+        "webpackbar": "^5.0.0-3"
     },
     "husky": {
         "hooks": {

+ 2 - 2
packages/semi-animation-react/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation-react",
-  "version": "2.19.0-beta.0",
+  "version": "2.20.0-beta.1",
   "description": "motion library for semi-ui-react",
   "keywords": [
     "motion",
@@ -26,7 +26,7 @@
   },
   "dependencies": {
     "@douyinfe/semi-animation": "2.12.0",
-    "@douyinfe/semi-animation-styled": "2.19.0-beta.0",
+    "@douyinfe/semi-animation-styled": "2.20.0-beta.1",
     "classnames": "^2.2.6"
   },
   "devDependencies": {

+ 0 - 12
packages/semi-animation-styled/getBabelConfig.js

@@ -18,18 +18,6 @@ module.exports = ({ isESM }) => {
             ],
         ],
         plugins: [
-            [
-                '@babel/plugin-transform-runtime',
-                {
-                    corejs: 3
-                },
-            ],
-            [
-                '@babel/plugin-proposal-decorators',
-                {
-                    legacy: true,
-                },
-            ],
         ]
     };
 };

+ 1 - 4
packages/semi-animation-styled/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation-styled",
-  "version": "2.19.0-beta.0",
+  "version": "2.20.0-beta.1",
   "description": "semi styled animation",
   "keywords": [
     "semi",
@@ -29,9 +29,6 @@
     "build:lib": "node scripts/compileLib",
     "prepublishOnly": "npm run build:lib"
   },
-  "dependencies": {
-    "@babel/runtime-corejs3": "^7.15.4"
-  },
   "devDependencies": {
     "@babel/plugin-proposal-decorators": "^7.15.8",
     "@babel/plugin-transform-runtime": "^7.15.8",

+ 1 - 1
packages/semi-animation/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation",
-  "version": "2.19.0-beta.0",
+  "version": "2.20.0-beta.1",
   "description": "animation base library for semi-ui",
   "keywords": [
     "animation",

+ 1 - 1
packages/semi-eslint-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-semi-design",
-  "version": "2.19.0-beta.0",
+  "version": "2.20.0-beta.1",
   "description": "semi ui eslint plugin",
   "keywords": [
     "semi",

+ 1 - 1
packages/semi-foundation/anchor/anchor.scss

@@ -1,5 +1,5 @@
-@import './variables.scss';
 @import "./animation.scss";
+@import './variables.scss';
 
 $module: #{$prefix}-anchor;
 

+ 1 - 1
packages/semi-foundation/autoComplete/autoComplete.scss

@@ -1,5 +1,5 @@
-@import "./variables.scss";
 @import "./animation.scss";
+@import "./variables.scss";
 @import "./option.scss";
 
 $module: #{$prefix}-autocomplete;

+ 2 - 2
packages/semi-foundation/badge/badge.scss

@@ -20,7 +20,7 @@ $module: #{$prefix}-badge;
         box-sizing: border-box;
         height: $height-badge_count;
         min-width: $height-badge_count;
-        border-radius: $height-badge_count / 2;
+        border-radius: $height-badge_count * 0.5;
         padding: $spacing-badge_count-paddingY $spacing-badge_count-paddingX;
         background-color: $color-badge_default-bg-default;
         border: $width-badge-border $color-badge_default-border-default solid;
@@ -162,7 +162,7 @@ $module: #{$prefix}-badge;
             color: $color-badge_warning_inverted-text-default;
         }
     }
-    
+
 }
 
 @import "./rtl.scss";

+ 2 - 2
packages/semi-foundation/breadcrumb/breadcrumb.scss

@@ -1,8 +1,8 @@
 //@import '../theme/variables.scss';
 // @import '../theme/mixin.scss';
 // @import '../theme/_font.scss';
-@import './variables.scss';
 @import "./animation.scss";
+@import './variables.scss';
 
 $module: #{$prefix}-breadcrumb;
 
@@ -120,4 +120,4 @@ $module: #{$prefix}-breadcrumb;
 
 }
 
-@import './rtl.scss';
+@import './rtl.scss';

+ 1 - 1
packages/semi-foundation/button/button.scss

@@ -1,5 +1,5 @@
-@import './variables.scss';
 @import './animation.scss';
+@import './variables.scss';
 @import './splitButtonGroup.scss';
 
 $module: #{$prefix}-button;

+ 1 - 2
packages/semi-foundation/calendar/foundation.ts

@@ -32,9 +32,8 @@ import {
     parseRangeAllDayEvent,
     DateObj,
     checkWeekend,
-    weeekStartsOnEnum
 } from './eventUtil';
-
+import type { weeekStartsOnEnum } from './eventUtil';
 
 export { weeekStartsOnEnum };
 export interface EventObject {

+ 2 - 2
packages/semi-foundation/calendar/variables.scss

@@ -17,7 +17,7 @@ $width-calendar_day-minWidth: 70px;
 $height-calendar_day_grid: 60px;
 $width-calendar_day_grid: 130px;
 $width-calendar_day_grid-minWidth: 130px;
-$height-calendar_time_grid: $height-calendar_day_grid / 2;
+$height-calendar_time_grid: $height-calendar_day_grid * 0.5;
 $height-calendar_allDay: 26px;
 $height-calendar_allDay-minHeight: 26px;
 $width-calendar_currCircle: 8px;
@@ -29,7 +29,7 @@ $height-calendar_month_day: 24px;
 $height-calendar_month_week_skeletion: 100%;
 $height-calendar_month_grid_wrapper: calc(100% - 27px);
 $width-calendar_card: 220px;
-$height-calendar_day_gridTime: $height-calendar_day_grid / 2;
+$height-calendar_day_gridTime: $height-calendar_day_grid * 0.5;
 $width-calendar_today_date: 24px;
 $height-calendar_today_date: 24px;
 $height-calendar_body_li: 24px;

+ 1 - 1
packages/semi-foundation/carousel/carousel.scss

@@ -1,5 +1,5 @@
-@import './variables.scss';
 @import './animation.scss';
+@import './variables.scss';
 
 $module: #{$prefix}-carousel;
 

+ 2 - 1
packages/semi-foundation/cascader/cascader.scss

@@ -1,5 +1,6 @@
-@import './variables.scss';
 @import "./animation.scss";
+@import './variables.scss';
+
 
 $module: #{$prefix}-cascader;
 

+ 2 - 2
packages/semi-foundation/checkbox/checkbox.scss

@@ -1,7 +1,7 @@
 //@import '../theme/variables.scss';
 // @import '../theme/mixin.scss';
-@import "./variables.scss";
 @import "./animation.scss";
+@import "./variables.scss";
 @import "./mixin.scss";
 
 $module: #{$prefix}-checkbox;
@@ -412,4 +412,4 @@ $module: #{$prefix}-checkbox;
     }
 }
 
-@import "./rtl.scss";
+@import "./rtl.scss";

+ 6 - 5
packages/semi-foundation/datePicker/foundation.ts

@@ -12,13 +12,14 @@ import { getDefaultFormatTokenByType } from './_utils/getDefaultFormatToken';
 import { strings } from './constants';
 import { strings as inputStrings } from '../input/constants';
 
-import { Type, DateInputFoundationProps, InsetInputValue } from './inputFoundation';
-import { MonthsGridFoundationProps } from './monthsGridFoundation';
-import { WeekStartNumber } from './_utils/getMonthTable';
-import { ArrayElement, Motion } from '../utils/type';
 import getInsetInputFormatToken from './_utils/getInsetInputFormatToken';
 import getInsetInputValueFromInsetInputStr from './_utils/getInsetInputValueFromInsetInputStr';
 
+import type { ArrayElement, Motion } from '../utils/type';
+import type { Type, DateInputFoundationProps, InsetInputValue } from './inputFoundation';
+import type { MonthsGridFoundationProps } from './monthsGridFoundation';
+import type { WeekStartNumber } from './_utils/getMonthTable';
+
 export type ValidateStatus = ArrayElement<typeof strings.STATUS>;
 export type InputSize = ArrayElement<typeof strings.SIZE_SET>;
 export type Position = ArrayElement<typeof strings.POSITION_SET>;
@@ -956,7 +957,7 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
             if (insetInput) {
                 const insetInputFormatToken = getInsetInputFormatToken({ format, type });
                 const insetInputStr = this._isMultiple() ? this.formatMultipleDates(dates, undefined, insetInputFormatToken) : this.formatDates(dates, insetInputFormatToken);
-                insetInputValue = getInsetInputValueFromInsetInputStr({ inputValue: insetInputStr, type, rangeSeparator  });
+                insetInputValue = getInsetInputValueFromInsetInputStr({ inputValue: insetInputStr, type, rangeSeparator });
             }
             const isRangeTypeAndInputIncomplete = this._isRangeType() && !this._isRangeValueComplete(dates);
             /**

+ 5 - 5
packages/semi-foundation/datePicker/variables.scss

@@ -36,7 +36,7 @@ $height-datepicker_month_grid_yearType_insetInput: 317px;
 $height-datepicker_month_grid_timeType_insetInput: 317px;
 
 // Spacing
-$spacing-datepicker_day-marginX: ($width-datepicker_day - $width-datepicker_day_main) / 2; // 日期格子水平外边距
+$spacing-datepicker_day-marginX: ($width-datepicker_day - $width-datepicker_day_main) * 0.5; // 日期格子水平外边距
 $spacing-datepicker_yam_header-paddingX: $spacing-base; // 年月选择 header 水平内边距
 $spacing-datepicker_yam_header-paddingY: $spacing-base-tight; // 年月选择 header 垂直内边距
 $spacing-datepicker_scrolllist_header-padding: $spacing-base; // 时间选择 header 内边距
@@ -182,7 +182,7 @@ $height-datepicker_insetInput_compact: 26px;
 $fontSize-datepicker_insetInput_compact-fontSize: 12px;
 
 $spacing-datepicker_switch_compact-padding: 6px;
-$spacing-datepicker_day_compact-margin: ($width-datepicker_day_compact - $width-datepicker_day_main_compact) / 2;
+$spacing-datepicker_day_compact-margin: ($width-datepicker_day_compact - $width-datepicker_day_main_compact) * 0.5;
 $spacing-datepicker_weeks_compact-padding: 10px;
 $spacing-datepicker_weeks_compact-paddingTop: $spacing-tight - 2px;
 $spacing-datepicker_weekday_compact-paddingLeft: 10px;
@@ -234,12 +234,12 @@ $height-datepicker_date_time_panel_compact: $height-datepicker_date_panel_compac
 $height-datepicker_presetPanel_left_and_right_except_content_compact: 20px + $spacing-datepicker_quick_control_header_compact-paddingTop + $spacing-datepicker_quick_control_content_compact-marginTop; // compact,除去content以外的高度,默认48px
 
 $width-datepicker_presetPanel_left_and_right_two_col_button: ($width-datepicker_presetPanel_left_and_right_content - $spacing-datepicker_quick_control_item-margin) * 0.5; // 左右方位快捷选择面板,固定两列,按钮宽度
-$width-datepicker_presetPanel_top_and_bottom_three_col_button: ($width-datepicker_presetPanel_top_and_bottom_content_date - $spacing-datepicker_quick_control_item-margin * 2) / 3; // 上下方位快捷选择面板,固定三列,按钮宽度
+$width-datepicker_presetPanel_top_and_bottom_three_col_button: ($width-datepicker_presetPanel_top_and_bottom_content_date - $spacing-datepicker_quick_control_item-margin * 2) * 0.333; // 上下方位快捷选择面板,固定三列,按钮宽度
 $width-datepicker_presetPanel_top_and_bottom_five_col_button: ($width-datepicker_presetPanel_top_and_bottom_content_range - $spacing-datepicker_quick_control_item-margin * 4) * 0.2; // 上下方位快捷选择面板,固定五列,按钮宽度
 $width-datepicker_presetPanel_top_and_bottom_two_col_button:($width-datepicker_presetPanel_top_and_bottom_content_month - $spacing-datepicker_quick_control_item-margin) * 0.5; // 上下方位快捷选择面板,固定两列,按钮宽度
 
 // compact
-$width-datepicker_presetPanel_top_and_bottom_three_col_button_compact: ($width-datepicker_presetPanel_top_and_bottom_content_date_compact - $spacing-datepicker_quick_control_item-margin * 2) / 3; // 上下方位快捷选择面板,固定三列,按钮宽度
+$width-datepicker_presetPanel_top_and_bottom_three_col_button_compact: ($width-datepicker_presetPanel_top_and_bottom_content_date_compact - $spacing-datepicker_quick_control_item-margin * 2) * 0.333; // 上下方位快捷选择面板,固定三列,按钮宽度
 $width-datepicker_presetPanel_top_and_bottom_five_col_button_compact: ($width-datepicker_presetPanel_top_and_bottom_content_range_compact - $spacing-datepicker_quick_control_item-margin * 4) * 0.2; // 上下方位快捷选择面板,固定五列,按钮宽度
 $width-datepicker_presetPanel_top_and_bottom_two_col_button_compact: ($width-datepicker_presetPanel_top_and_bottom_content_month_compact - $spacing-datepicker_quick_control_item-margin) * 0.5; // 上下方位快捷选择面板,固定两列,按钮宽度
 
@@ -250,4 +250,4 @@ $height-datepicker_preset_panel_inset_input: $height-datepicker_month_max + $spa
 
 // insetinput compact
 $height-datepicker_inset_input_compact: 28px + $spacing-datepicker_insetInput_wrapper_compact-paddingY; // compact,insetInput高度, 默认36px
-$height-datepicker_preset_panel_inset_input_compact: $height-datepicker_month_max_compact + $width-datepicker_nav_compact + $spacing-datepicker_insetInput_wrapper_compact-paddingY * 2 + $height-datepicker_inset_input_compact; // inset_input下,非month面板渲染最大高度,默认296px
+$height-datepicker_preset_panel_inset_input_compact: $height-datepicker_month_max_compact + $width-datepicker_nav_compact + $spacing-datepicker_insetInput_wrapper_compact-paddingY * 2 + $height-datepicker_inset_input_compact; // inset_input下,非month面板渲染最大高度,默认296px

+ 1 - 1
packages/semi-foundation/dropdown/dropdown.scss

@@ -1,6 +1,6 @@
 //@import '../theme/variables.scss';
-@import './variables.scss';
 @import './animation.scss';
+@import './variables.scss';
 
 $module: #{$prefix}-dropdown;
 

+ 2 - 2
packages/semi-foundation/form/variables.scss

@@ -1,5 +1,5 @@
-$spacing-form_label-paddingTop: ($height-control-default - 20px) / 2; // 水平布局表单标题顶部内边距
-$spacing-form_label_small-paddingTop: ($height-control-default - 24px) / 2; // 水平布局 小尺寸表单标题顶部内边距
+$spacing-form_label-paddingTop: ($height-control-default - 20px) * 0.5; // 水平布局表单标题顶部内边距
+$spacing-form_label_small-paddingTop: ($height-control-default - 24px) * 0.5; // 水平布局 小尺寸表单标题顶部内边距
 
 $spacing-form_field_horizontal-paddingRight: $spacing-base; // 水平布局表单标题右侧内边距
 $spacing-form_field_group_horizontal-paddingRight: $spacing-base; // 水平布局表单组标题右侧内边距

+ 1 - 1
packages/semi-foundation/grid/grid.scss

@@ -118,4 +118,4 @@ $module: #{$prefix};
     @include make-grid(-xxl);
 }
 
-@import "./rtl.scss";
+@import "./rtl.scss";

+ 10 - 10
packages/semi-foundation/grid/mixin.scss

@@ -1,6 +1,6 @@
+@use "sass:math";
 // mixins for clearfix
 // ------------------------
-
 //TODO 提出到公共mixin
 @mixin clearfix() {
     zoom: 1;
@@ -21,8 +21,8 @@
 @mixin make-row($gutter: $width-grid_gutter) {
     position: relative;
     height: auto;
-    margin-right: ($gutter / -2);
-    margin-left: ($gutter / -2);
+    margin-right: ($gutter * -0.5);
+    margin-left: ($gutter * -0.5);
     @include clearfix();
 }
 
@@ -35,8 +35,8 @@
         #{$item} {
             position: relative;
             min-height: 1px;
-            padding-right: ($width-grid_gutter / 2);
-            padding-left: ($width-grid_gutter / 2);
+            padding-right: ($width-grid_gutter * 0.5);
+            padding-left: ($width-grid_gutter * 0.5);
         }
     }
 }
@@ -59,19 +59,19 @@
         .#{$module}-col#{$class}-#{$i} {
             display: block;
             box-sizing: border-box;
-            width: percentage(($i / $width-grid_columns));
+            width: percentage(math.div($i , $width-grid_columns));
         }
 
         .#{$module}-col#{$class}-push-#{$i} {
-            left: percentage(($i / $width-grid_columns));
+            left: percentage(math.div($i , $width-grid_columns));
         }
 
         .#{$module}-col#{$class}-pull-#{$i} {
-            right: percentage(($i / $width-grid_columns));
+            right: percentage(math.div($i , $width-grid_columns));
         }
 
         .#{$module}-col#{$class}-offset-#{$i} {
-            margin-left: percentage(($i / $width-grid_columns));
+            margin-left: percentage(math.div($i , $width-grid_columns));
         }
 
         .#{$module}-col#{$class}-order-#{$i} {
@@ -101,7 +101,7 @@
         .#{$module}-col#{$class}-offset-#{$i} {
             .#{$prefix}-rtl & {
                 margin-left: auto;
-                margin-right: percentage(($i / $width-grid_columns));
+                margin-right: percentage(math.div($i , $width-grid_columns));
             }
         }
     }

+ 14 - 6
packages/semi-foundation/gulpfile.js

@@ -34,7 +34,7 @@ gulp.task('compileTSForCJS', function compileTSForCJS() {
 });
 
 const excludeScss = [
-    '!**/button/splitButtonGroup.scss', 
+    '!**/button/splitButtonGroup.scss',
     '!**/steps/bacisSteps.scss',
     '!**/steps/fillSteps.scss',
     '!**/steps/navSteps.scss',
@@ -49,8 +49,16 @@ gulp.task('compileScss', function compileScss() {
             function (chunk, enc, cb) {
                 const rootPath = path.join(__dirname, '../../');
                 const scssVarStr = `@import "${rootPath}/packages/semi-theme-default/scss/index.scss";\n`;
-                const scssBuffer = Buffer.from(scssVarStr);
-                chunk.contents = Buffer.concat([scssBuffer, chunk.contents]);
+                let scssRaw = chunk.contents.toString('utf-8');
+                if (scssRaw.startsWith("@use")) {
+                    const scssRawSplit = scssRaw.split("\n");
+                    const codeStartIndex = scssRawSplit.findIndex(item => !item.startsWith("@use"));
+                    scssRawSplit.splice(codeStartIndex, 0, scssVarStr);
+                    scssRaw = scssRawSplit.join("\n");
+                } else {
+                    scssRaw = `${scssVarStr}\n${scssRaw}`;
+                }
+                chunk.contents = Buffer.from(scssRaw, 'utf-8');
                 cb(null, chunk);
             }
         ))
@@ -67,11 +75,11 @@ gulp.task('moveScss', function moveScss() {
         .pipe(gulp.dest('lib/cjs'));
 });
 
-gulp.task('compileLib', 
+gulp.task('compileLib',
     gulp.series(
         [
-            'cleanLib', 'compileScss', 
-            'moveScss', 
+            'cleanLib', 'compileScss',
+            'moveScss',
             gulp.parallel('compileTSForESM', 'compileTSForCJS'),
         ]
     )

+ 11 - 0
packages/semi-foundation/image/animation.scss

@@ -0,0 +1,11 @@
+$transform_rotate-image_preview_spinner: var(--semi-transform_rotate-clockwise360deg); // 预览图像加载spin旋转角度
+$transform_scale3d-image_preview_image_img: 1, 1, 1; // 预览图片放大
+$transform_rotate-image_preview_image_img: var(--semi-transform-rotate-none); // 预览图片旋转角度
+
+$transition_duration-image_preview_image_img: 0.3s; // 预览图像动画持续时间
+$transition_function-image_preview_image_img: cubic-bezier(0.215, 0.61, 0.355, 1); // 预览图片动画函数
+$transition_delay-image_preview_image_img: 0s; // 预览图片延迟时间
+
+$transition_duration-image_preview: 500ms; // 预览图片透明度变化动画时间
+
+$transform_rotate-image_preview_icon_rtl: var(--semi-transform_rotate-clockwise180deg); // rtl模式下向前/向后切换图片按钮旋转角度

+ 7 - 0
packages/semi-foundation/image/constants.ts

@@ -0,0 +1,7 @@
+import { BASE_CLASS_PREFIX } from "../base/constants";
+
+const cssClasses = {
+    PREFIX: `${BASE_CLASS_PREFIX}-image`,
+} as const;
+
+export { cssClasses };

+ 221 - 0
packages/semi-foundation/image/image.scss

@@ -0,0 +1,221 @@
+@import "./animation.scss";
+@import "./variables.scss";
+
+$module: #{$prefix}-image;
+
+.#{$module} {
+
+    border-radius: $radius-image;
+    position: relative;
+    display: inline-block;
+    overflow: hidden;
+
+    &-img {
+        vertical-align: middle;
+        border-radius: inherit;
+
+        &-preview {
+            cursor: zoom-in;
+        }
+
+        &-error {
+            opacity: 0;
+        }
+    }
+
+    &-overlay {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+    }
+}
+
+.#{$module}-status {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border-radius: $radius-image;
+    background-color: $color-image_status-bg;
+    color: $color-image_status;
+}
+
+.#{$module}-preview {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: $z-image_preview;
+    background-color: var(--semi-color-overlay-bg);
+    transition: opacity $transition_duration-image_preview;
+    overflow: hidden;
+
+    &-popup {
+        position: absolute;
+    }
+
+    .#{$module}-preview-hide {
+        opacity: 0;
+    }
+
+    &-icon {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        width: $width-image_preview_icon;
+        height: $height-image_preview_icon;
+        border-radius: 50%;
+        position: absolute;
+        top: 50%;
+        transform: translateY(-50%);
+        background: $color-image_preview_icon-bg;
+        cursor: pointer;
+        color: $color-image_preview_icon;
+    }
+
+    &-prev {
+        left: $spacing-image_preview_icon-x;
+    }
+
+    &-next {
+        right: $spacing-image_preview_icon-x;
+    }
+
+    &-header {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        font-weight: normal;
+        @include font-size-regular;
+        color: $color-image_preview_header;
+        height: $height-image_preview_header;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: $spacing-image_preview_header-paddingY $spacing-image_preview_header-paddingX;
+        z-index: $z-image_preview_header;
+
+        &-title {
+            flex: 1;
+        }
+
+        &-close {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            cursor: pointer;
+            width: $width-image_preview_header_close;
+            height: $height-image_preview_header_close;
+            border-radius: 50%;
+
+            &:hover {
+                background-color: $color-image_header_close-bg;
+            }
+        }
+    }
+
+    &-footer {
+        display: flex;
+        align-items: center;
+        padding: $spacing-image_preview_footer-paddingY $spacing-image_preview_footer-paddingX;
+        background: $color-image_preview_footer-bg;
+        border-radius: $radius-image_preview_footer;
+        height: $height-image_preview_footer;
+
+        &-wrapper {
+            position: absolute;
+            left: 50%;
+            bottom: 16px;
+            transform: translateX(-50%);
+        }
+
+        &-page {
+            color: $color-image_preview_footer_icon;
+            @include font-size-header-6;
+            margin: $spacing-image_preview_footer_page-marginY $spacing-image_preview_footer_page-marginX;
+        }
+
+        .#{$prefix}-icon {
+            color: $color-image_preview_footer_icon;
+            cursor: pointer;
+        }
+
+        &-gap {
+            margin-left: $spacing-image_preview_footer_gap-marginLeft;
+        }
+    
+        .#{$prefix}-slider {
+            width: $width-image_preview_footer_slider;
+            padding: $spacing-image_preview_footer_slider-paddingY $spacing-image_preview_footer_slider-paddingX;
+    
+            .#{$prefix}-slider-rail {
+                color: $color-image_preview_footer_slider_rail;
+                height: $height-image_preview_footer_slider;
+            }
+    
+            .#{$prefix}-slider-track {
+                height: $height-image_preview_footer_slider;
+            }
+    
+            .#{$prefix}-slider-handle {
+                width: $width-image_preview_footer_slider_handle;
+                height: $height-image_preview_footer_slider_handle;
+                margin-top: $spacing-image_preview_footer_slider_handle-marginTop;
+                box-sizing: border-box;
+            }
+        }
+
+        .#{$prefix}-divider {
+            background: $color-image-preview_divider-bg;
+            margin: $spacing-image_preview_footer_divider-marginY $spacing-image_preview_footer_divider-marginX;
+        }
+        
+        .#{$module}-preview-footer-disabled {
+            color: $color-image_preview_disabled;
+            cursor: default;
+            pointer-events: none;
+        }
+
+    }
+
+    &-image {
+        position: relative;
+        height: 100%;
+
+        &-img {
+            position: absolute;
+            transform: scale3d($transform_scale3d-image_preview_image_img) $transform_rotate-image_preview_image_img;
+            transition: transform $transition_duration-image_preview_image_img  $transition_delay-image_preview_image_img;
+            z-index: 0;
+        }
+
+        &-spin {
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+
+            .#{$prefix}-spin-wrapper {
+                color: $color-image_preview_image_spin;
+            }
+        }
+    }
+    
+    @keyframes spinner {
+        to {
+            transform: $transform_rotate-image_preview_spinner;
+        }
+    }
+}
+
+// Remove the default border of img when src is empty or src is invalid
+img[src=""], img:not([src]) {
+    opacity: 0;
+}
+
+@import "./rtl.scss";

+ 64 - 0
packages/semi-foundation/image/imageFoundation.ts

@@ -0,0 +1,64 @@
+import BaseFoundation, { DefaultAdapter } from "../base/foundation";
+import { isObject } from "lodash";
+
+
+export interface ImageAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
+    getIsInGroup: () => boolean;
+}
+
+
+export default class ImageFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<ImageAdapter<P, S>, P, S> {
+    constructor(adapter: ImageAdapter<P, S>) {
+        super({ ...adapter });
+    }
+
+    handleClick = (e: any) => {
+        const { imageID, preview } = this.getProps();
+        // if preview = false, then it cannot preview
+        if (!preview) {
+            return;
+        }
+        // if image in group, then use group's Preview components
+        if (this._adapter.getIsInGroup()) {
+            const { setCurrentIndex, handleVisibleChange } = this._adapter.getContexts();
+            setCurrentIndex(imageID);
+            handleVisibleChange(true);
+        } else {
+            // image isn't in group, then use it's own Preview components
+            this.handlePreviewVisibleChange(true);
+        }
+    }
+
+    handleLoaded = (e: any) => {
+        const { onLoad } = this.getProps();
+        onLoad && onLoad(e);
+        this.setState ({
+            loadStatus: "success",
+        } as any);
+    }
+
+    handleError = (e: any) => {
+        const { onError } = this.getProps();
+        onError && onError(e);
+        this.setState ({
+            loadStatus: "error",
+        } as any);
+    }
+
+    handlePreviewVisibleChange = (newVisible: boolean) => {
+        const { preview } = this.getProps();
+        if (isObject(preview)) {
+            const { onVisibleChange } = preview as any;
+            onVisibleChange && onVisibleChange(newVisible);
+            if (!("visible" in this.getProps())) {
+                this.setState({
+                    previewVisible: newVisible,
+                } as any);
+            }
+        } else {
+            this.setState({
+                previewVisible: newVisible,
+            } as any);
+        }
+    }
+}

+ 41 - 0
packages/semi-foundation/image/previewFooterFoundation.ts

@@ -0,0 +1,41 @@
+import BaseFoundation, { DefaultAdapter } from "../base/foundation";
+
+export interface PreviewFooterAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
+    setStartMouseOffset: (time: number) => void;
+}
+
+export default class PreviewFooterFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<PreviewFooterAdapter<P, S>, P, S> {
+    
+    changeSliderValue = (type: string): void => {
+        const { zoom, step, min, max } = this.getProps();
+        let newValue = type === "plus" ? zoom + step : zoom - step;
+        if (newValue > max) {
+            newValue = max;
+        } else if (newValue < min) {
+            newValue = min;
+        }
+        this.handleValueChange(newValue);
+    };
+
+    handleValueChange = (value: number): void => {
+        const { onZoomIn, onZoomOut, zoom } = this.getProps();
+        if (value > zoom) {
+            onZoomIn(value / 100);
+        } else {
+            onZoomOut(value / 100);
+        }
+        this._adapter.setStartMouseOffset(value);
+    };
+
+    handleRatioClick = (): void => {
+        const { ratio, onAdjustRatio } = this.getProps();
+        const type = ratio === "adaptation" ? "realSize" : "adaptation";
+        onAdjustRatio(type);
+    }
+
+    handleRotate = (direction: string): void => {
+        const { onRotate } = this.getProps();
+        onRotate && onRotate(direction);
+    }
+    
+}

+ 25 - 0
packages/semi-foundation/image/previewFoundation.ts

@@ -0,0 +1,25 @@
+import BaseFoundation, { DefaultAdapter } from "../base/foundation";
+
+export default class PreviewFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<Partial<DefaultAdapter>> {
+    
+    handleVisibleChange = (newVisible : boolean) => {
+        const { visible, onVisibleChange } = this.getProps();
+        if (!(visible in this.getProps())) {
+            this.setState({
+                visible: newVisible,
+            });
+        }
+        onVisibleChange && onVisibleChange(newVisible);
+    };
+
+    handleCurrentIndexChange = (index: number) => {
+        const { currentIndex, onChange } = this.getProps();
+        if (!(currentIndex in this.getProps())) {
+            this.setState({
+                currentIndex: index,
+            } as any);
+        }
+        onChange && onChange(index);
+    };
+    
+}

+ 260 - 0
packages/semi-foundation/image/previewImageFoundation.ts

@@ -0,0 +1,260 @@
+import BaseFoundation, { DefaultAdapter } from "../base/foundation";
+import { handlePrevent } from "../utils/a11y";
+import { throttle, isUndefined } from "lodash";
+
+export interface PreviewImageAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
+    getOriginImageSize: () => { originImageWidth: number; originImageHeight: number; }; 
+    setOriginImageSize: (size: { originImageWidth: number; originImageHeight: number; }) => void;
+    getContainerRef: () => any;
+    getImageRef: () => any;
+    getMouseMove: () => boolean;
+    setStartMouseMove: (move: boolean) => void;
+    getMouseOffset: () => { x: number; y: number };
+    setStartMouseOffset: (offset: { x: number; y: number }) => void;
+    setLoading: (loading: boolean) => void;
+}
+
+export interface DragDirection {
+    canDragVertical: boolean;
+    canDragHorizontal: boolean;
+}
+
+export interface ExtremeBounds {
+    left: number;
+    top: number;
+}
+
+export interface ImageOffset {
+    x: number;
+    y: number;
+}
+
+export default class PreviewImageFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<PreviewImageAdapter<P, S>, P, S> {
+    constructor(adapter: PreviewImageAdapter<P, S>) {
+        super({ ...adapter });
+    }
+
+    _isImageVertical = (): boolean => this.getProp("rotation") % 180 !== 0;
+
+    _getImageBounds = (): DOMRect => {
+        const imageRef = this._adapter.getImageRef();
+        return imageRef?.getBoundingClientRect();
+    };
+
+    _getContainerBounds = (): DOMRect => {
+        const containerRef = this._adapter.getContainerRef();
+        return containerRef?.current?.getBoundingClientRect();
+    }
+
+    _getOffset = (e: any): ImageOffset => {
+        const { left, top } = this._getImageBounds();
+        return {
+            x: e.clientX - left,
+            y: e.clientY - top,
+        };
+    }
+
+    setLoading = (loading: boolean) => {
+        this._adapter.setLoading(loading);
+    }
+
+    handleWindowResize = (): void => {
+        const { setRatio } = this.getProps();
+        const { ratio } = this.getProps();
+        const { originImageWidth, originImageHeight } = this._adapter.getOriginImageSize();
+        if (originImageWidth && originImageHeight) {
+            if (ratio !== "adaptation") {
+                setRatio("adaptation");
+            } else {
+                this.handleResizeImage();
+            } 
+        }
+    };
+
+    handleLoad = (e: any): void => {
+        if (e.target) {
+            const { width: w, height: h } = e.target as any;
+            this._adapter.setOriginImageSize({ originImageWidth: w, originImageHeight: h });
+            this.setState({
+                loading: false,
+            } as any);
+            this.handleResizeImage();
+        }
+        const { src, onLoad } = this.getProps();
+        onLoad && onLoad(src);
+    }
+
+    handleError = (e: any): void => {
+        const { onError, src } = this.getProps();
+        this.setState({
+            loading: false,
+        } as any);
+        onError && onError(src);
+    }
+
+    handleResizeImage = () => {
+        const horizontal = !this._isImageVertical();
+        const { originImageWidth, originImageHeight } = this._adapter.getOriginImageSize();
+        const imgWidth = horizontal ? originImageWidth : originImageHeight;
+        const imgHeight = horizontal ? originImageHeight : originImageWidth;
+        const { onZoom } = this.getProps();
+        const containerRef = this._adapter.getContainerRef();
+        if (containerRef && containerRef.current) {
+            const { width: containerWidth, height: containerHeight } = this._getContainerBounds();
+            const reservedWidth = containerWidth - 80;
+            const reservedHeight = containerHeight - 80;
+            const _zoom = Number(
+                Math.min(reservedWidth / imgWidth, reservedHeight / imgHeight).toFixed(2)
+            );
+            onZoom(_zoom);
+        }
+    }
+
+    handleRightClickImage = (e: any) => {
+        const { disableDownload } = this.getProps();
+        if (disableDownload) {
+            e.preventDefault();
+            e.stopPropagation();
+            return false;
+        } else {
+            return true;
+        }
+    };
+
+    handleWheel = (e: React.WheelEvent<HTMLImageElement>) => {
+        this.onWheel(e);
+        handlePrevent(e);
+    }
+
+    onWheel = throttle((e: React.WheelEvent<HTMLImageElement>): void => {
+        const { onZoom, zoomStep, maxZoom, minZoom } = this.getProps();
+        const { currZoom } = this.getStates();
+        let _zoom:number;
+        if (e.deltaY < 0) {
+            /* zoom in */
+            if (currZoom + zoomStep <= maxZoom) {
+                _zoom = Number((currZoom + zoomStep).toFixed(2));
+            }
+        } else if (e.deltaY > 0) {
+            /* zoom out */
+            if (currZoom - zoomStep >= minZoom) {
+                _zoom = Number((currZoom - zoomStep).toFixed(2));
+            }
+        }
+        if (!isUndefined(_zoom)) {
+            onZoom(_zoom);
+        }
+    }, 50);
+
+    calcCanDragDirection = (): DragDirection => {
+        const { width, height } = this.getStates();
+        const { rotation } = this.getProps();
+        const { width: containerWidth, height: containerHeight } =this._getContainerBounds();
+        let canDragHorizontal = width > containerWidth;
+        let canDragVertical = height > containerHeight;
+        if (this._isImageVertical()) {
+            canDragHorizontal = height > containerWidth;
+            canDragVertical = width > containerHeight;
+        }
+        return {
+            canDragVertical,
+            canDragHorizontal,
+        };
+    };
+
+    handleZoomChange = (newZoom: number, e: any): void => {
+        const imageRef = this._adapter.getImageRef();
+        const { originImageWidth, originImageHeight } = this._adapter.getOriginImageSize();
+        const { canDragVertical, canDragHorizontal } = this.calcCanDragDirection();
+        const canDrag = canDragVertical || canDragHorizontal;
+        const { width: containerWidth, height: containerHeight } = this._getContainerBounds();
+        const newWidth = Math.floor(originImageWidth * newZoom);
+        const newHeight = Math.floor(originImageHeight * newZoom);
+
+        // debugger;
+        let _offset;
+        const horizontal = !this._isImageVertical();
+        let newTop = 0;
+        let newLeft = 0;
+        if (horizontal) {
+            _offset = {
+                x: 0.5 * (containerWidth - newWidth),
+                y: 0.5 * (containerHeight - newHeight),
+            };
+           
+            newLeft = _offset.x;
+            newTop= _offset.y;
+        } else {
+            _offset = {
+                x: 0.5 * (containerWidth - newHeight),
+                y: 0.5 * (containerHeight - newWidth),
+            };
+            newLeft = _offset.x - (newWidth - newHeight) / 2;
+            newTop = _offset.y + (newWidth - newHeight) / 2;
+        }
+        
+        this.setState({
+            width: newWidth,
+            height: newHeight,
+            offset: _offset,
+            left: newLeft,
+            top: newTop,
+            currZoom: newZoom,
+        } as any);
+        imageRef && (imageRef.style.cursor = canDrag ? "grab" : "default");
+    };
+
+    calcExtremeBounds = (): ExtremeBounds => {
+        const { width, height } = this.getStates(); 
+        const { width: containerWidth, height: containerHeight } = this._getContainerBounds();
+        let extremeLeft = containerWidth - width;
+        let extremeTop = containerHeight - height;
+        if (this._isImageVertical()) {
+            extremeLeft = containerWidth - height;
+            extremeTop = containerHeight - width;
+        }
+        return {
+            left: extremeLeft,
+            top: extremeTop,
+        };
+    };
+
+    handleMoveImage = (e: any): void => {
+        const { offset, width, height, left, top } = this.getStates();
+        const { rotation } = this.getProps();
+        const startMouseMove = this._adapter.getMouseMove();
+        const startMouseOffset = this._adapter.getMouseOffset();
+        const { canDragVertical, canDragHorizontal } = this.calcCanDragDirection();
+        if (startMouseMove && (canDragVertical || canDragHorizontal)) {
+            const { pageX, pageY } = e;
+            const { left: extremeLeft, top: extremeTop } = this.calcExtremeBounds();
+            let newX = canDragHorizontal ? pageX - startMouseOffset.x : offset.x;
+            let newY = canDragVertical ? pageY - startMouseOffset.y : offset.y;
+            if (canDragHorizontal) {
+                newX = newX > 0 ? 0 : newX < extremeLeft ? extremeLeft : newX;
+            }
+            if (canDragVertical) {
+                newY = newY > 0 ? 0 : newY < extremeTop ? extremeTop : newY;
+
+            }
+            const _offset = {
+                x: newX,
+                y: newY,
+            };
+            this.setState({
+                offset: _offset,
+                left: this._isImageVertical() ? _offset.x - (width - height) / 2 : _offset.x,
+                top: this._isImageVertical() ? _offset.y + (width - height) / 2 : _offset.y,
+            } as any);
+        }
+    };
+
+    handleImageMouseDown = (e: any): void => {
+        this._adapter.setStartMouseOffset(this._getOffset(e));
+        this._adapter.setStartMouseMove(true);
+    };
+
+    handleImageMouseUp = (): void => {
+        this._adapter.setStartMouseMove(false);
+    };
+}

+ 260 - 0
packages/semi-foundation/image/previewInnerFoundation.ts

@@ -0,0 +1,260 @@
+import BaseFoundation, { DefaultAdapter } from "../base/foundation";
+import KeyCode from "../utils/keyCode";
+import { getPreloadImagArr, downloadImage, isTargetEmit } from "./utils";
+
+export interface PreviewInnerAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
+    getIsInGroup: () => boolean;
+    notifyChange: (index: number) => void;
+    notifyZoom: (zoom: number, increase: boolean) => void;
+    notifyClose: () => void;
+    notifyVisibleChange: (visible: boolean) => void;
+    notifyRatioChange: (type: string) => void;
+    notifyRotateChange: (angle: number) => void;
+    notifyDownload: (src: string, index: number) => void;
+    registerKeyDownListener: () => void;
+    unregisterKeyDownListener: () => void;
+    getMouseActiveTime: () => number;
+    getStopTiming: () => boolean;
+    setStopTiming: (value: boolean) => void;
+    getStartMouseDown: () => {x: number, y: number};
+    setStartMouseDown: (x: number, y: number) => void;
+    setMouseActiveTime: (time: number) => void;
+}
+
+const NOT_CLOSE_TARGETS = ["icon", "footer"];
+const STOP_CLOSE_TARGET = ["icon", "footer", "header"];
+
+export default class PreviewInnerFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<PreviewInnerAdapter<P, S>, P, S> {
+    constructor(adapter: PreviewInnerAdapter<P, S>) {
+        super({ ...adapter });
+    }
+
+    beforeShow() {
+        this._adapter.registerKeyDownListener();
+    }
+
+    afterHide() {
+        this._adapter.unregisterKeyDownListener();
+    }
+
+    handleRatio(type: string) {
+        this.setState({
+            ratio: type,
+        } as any);
+    }
+
+    handleViewVisibleChange = () => {
+        const nowTime = new Date().getTime();
+        const mouseActiveTime = this._adapter.getMouseActiveTime();
+        const stopTiming = this._adapter.getStopTiming();
+        const { viewerVisibleDelay } = this.getProps();
+        const { viewerVisible } = this.getStates();
+        if (nowTime - mouseActiveTime > viewerVisibleDelay && !stopTiming) {
+            viewerVisible && this.setState({
+                viewerVisible: false,
+            } as any);
+        }
+    }
+
+    handleMouseMoveEvent = (e: any, event: string) => {
+        const isTarget = isTargetEmit(e.nativeEvent, STOP_CLOSE_TARGET);
+        if (isTarget && event === "over") {
+            this._adapter.setStopTiming(true);
+        } else if (isTarget && event === "out") {
+            this._adapter.setStopTiming(false);
+        }
+    }
+
+    handleMouseMove = (e: any) => {
+        this._adapter.setMouseActiveTime(new Date().getTime());
+        this.setState({
+            viewerVisible: true,
+        } as any);
+    }
+
+    handleMouseUp = (e: any) => {
+        const { maskClosable } = this.getProps();
+        let couldClose = !isTargetEmit(e.nativeEvent, NOT_CLOSE_TARGETS);
+        const { clientX, clientY } = e;
+        const { x, y } = this._adapter.getStartMouseDown();
+        if (clientX !== x || y !== clientY) {
+            couldClose = false;
+        }
+        if (couldClose && maskClosable) {
+            this.handlePreviewClose();
+        }
+    }
+
+    handleMouseDown = (e: any) => {
+        const { clientX, clientY } = e;
+        this._adapter.setStartMouseDown(clientX, clientY);
+    }
+
+    handleKeyDown = (e: any) => {
+        const { closeOnEsc } = this.getProps();
+        if (closeOnEsc && e.keyCode === KeyCode.ESC) {
+            e.stopPropagation();
+            this._adapter.notifyVisibleChange(false);
+            this._adapter.notifyClose();
+            return;
+        }
+    }
+
+    handleSwitchImage = (direction: string) => {
+        const step = direction === "prev" ? -1 : 1;
+        const { imgSrc, currentIndex: currentIndexInState } = this.getStates();
+        const srcLength = imgSrc.length;
+        const newIndex = (currentIndexInState + step + srcLength) % srcLength;
+        if ("currentIndex" in this.getProps()) {
+            if (this._adapter.getIsInGroup()) {
+                const setCurrentIndex = this._adapter.getContext("setCurrentIndex");
+                setCurrentIndex(newIndex);
+            }
+        } else {
+            this.setState({
+                currentIndex: newIndex,
+            } as any);
+        }
+        this._adapter.notifyChange(newIndex);
+        this.setState({
+            direction,
+            rotation: 0,
+        } as any);
+        this._adapter.notifyRotateChange(0);
+    }  
+
+    handleDownload = () => {
+        const { currentIndex, imgSrc } = this.getStates();
+        const downloadSrc = imgSrc[currentIndex];
+        const downloadName = downloadSrc.slice(downloadSrc.lastIndexOf("/") + 1);
+        downloadImage(downloadSrc, downloadName);
+        this._adapter.notifyDownload(downloadSrc, currentIndex);
+    }
+
+    handlePreviewClose = () => {
+        this._adapter.notifyVisibleChange(false);
+        this._adapter.notifyClose();
+    }
+
+    handleAdjustRatio = (type: string) => {
+        this.setState({
+            ratio: type,
+        } as any);
+        this._adapter.notifyRatioChange(type);
+    }
+
+    handleRotateImage = (direction: string) => {
+        const { rotation } = this.getStates();
+        const newRotation = rotation + (direction === "left" ? 90 : (-90));
+        this.setState({
+            rotation: newRotation,
+        } as any);
+        this._adapter.notifyRotateChange(newRotation);
+    }
+
+    handleZoomImage = (newZoom: number) => {
+        const { zoom } = this.getStates();
+        this._adapter.notifyZoom(newZoom, newZoom > zoom);
+        this.setState({
+            zoom: newZoom,
+        } as any);
+    }
+
+    // 当 visible 改变之后,预览组件完成首张图片加载后,启动预加载
+    // 如: 1,2,3,4,5,6,7,8张图片, 点击第 4 张图片,preLoadGap 为 2
+    // 当 visible 从 false 变为 true ,首先加载第 4 张图片,当第 4 张图片加载完成后,
+    // 再按照 5,3,6,2的顺序预先加载这几张图片
+    // When visible changes, the preview component finishes loading the first image and starts preloading
+    // Such as: 1, 2, 3, 4, 5, 6, 7, 8 pictures, click the 4th picture, preLoadGap is 2
+    // When visible changes from false to true , load the 4th image first, when the 4th image is loaded,
+    // Preload these pictures in the order of 5, 3, 6, 2
+    preloadGapImage = () => {
+        const { preLoad, preLoadGap, infinite, currentIndex } = this.getProps();
+
+        const { imgSrc }= this.getStates();
+        if (!preLoad || typeof preLoadGap !== "number" || preLoadGap < 1){
+            return;
+        }
+
+        const preloadImages = getPreloadImagArr(imgSrc, currentIndex, preLoadGap, infinite);
+        const Img = new Image();
+        let index = 0;
+        function callback(e: any){
+            index++;
+            if (index < preloadImages.length) {
+                Img.src = preloadImages[index];
+            }
+        }
+        Img.onload = (e) => {
+            this.setLoadSuccessStatus(Img.src);
+            callback(e);
+        };
+        Img.onerror = callback;
+        Img.src = preloadImages[0];  
+    }
+
+    // 在切换左右图片时,当被切换图片完成加载后,根据方向决定下一个预加载的图片
+    // 如: 1,2,3,4,5,6,7,8张图片
+    // 当 preLoadGap 为 2, 从第 5 张图片进行切换
+    // - 如果向 右 切换到第 6 张,则第 6 张图片加载动作结束后(无论加载成功 or 失败),会预先加载第 8 张;
+    // - 如果向 左 切换到第 4 张,则第 4 张图片加载动作结束后(无论加载成功 or 失败),会预先加载第 2 张;
+    // When switching the left and right pictures, when the switched picture is loaded, the next preloaded picture is determined according to the direction
+    // Such as: 1, 2, 3, 4, 5, 6, 7, 8 pictures
+    // When preLoadGap is 2, switch from the 5th picture
+    // - If you switch to the 6th image(direction is next), the 8th image will be preloaded after the 6th image is loaded (whether it succeeds or fails to load);
+    // - If you switch to the 4th image(direction is prev), the second image will be preloaded after the 4th image is loaded (whether it succeeds or fails to load);
+    preloadSingleImage = () => {
+        const { preLoad, preLoadGap, infinite } = this.getProps();
+        const { imgSrc, currentIndex, direction, imgLoadStatus } = this.getStates();
+        if (!preLoad || typeof preLoadGap !== "number" || preLoadGap < 1){
+            return;
+        }
+        // 根据方向决定preload那个index
+        // Determine the index of preload according to the direction
+        let preloadIndex = currentIndex + (direction === "prev" ? -1 : 1) * preLoadGap;
+        if (preloadIndex < 0 || preloadIndex >= imgSrc.length) {
+            if (infinite) {
+                preloadIndex = (preloadIndex + imgSrc.length) % imgSrc.length;
+            } else {
+                return;
+            }
+        }
+        // 如果图片没有加载成功过,则进行预加载
+        // If the image has not been loaded successfully, preload it
+        if (!imgLoadStatus[preloadIndex]) {
+            const Img = new Image();
+            Img.onload = (e) => {
+                this.setLoadSuccessStatus(imgSrc[preloadIndex]);
+            };
+            Img.src = imgSrc[preloadIndex];
+        }
+    }
+
+    setLoadSuccessStatus = (src: string) => {
+        const { imgLoadStatus } = this.getStates();
+        const status = { ...imgLoadStatus };
+        status[src] = true;
+        this.setState({
+            imgLoadStatus: status,
+        } as any);
+    }
+
+    onImageLoad = (src: string) => {
+        const { preloadAfterVisibleChange } = this.getStates();
+        this.setLoadSuccessStatus(src);
+        // 当 preview 中当前加载的图片加载完成后,
+        // 如果是在visible change之后的第一次加载,则启动加载该currentIndex左右preloadGap范围的图片
+        // 如果是非第一次加载,是在左右切换图片,则根据方向预先加载单张图片
+        // When the currently loaded image in Preview is loaded,
+        // - It is the first load after visible change, start loading the images in the preloadGap range around the currentIndex
+        // - It is not the first load, the image is switched left and right, and a single image is preloaded according to the direction
+        if (preloadAfterVisibleChange) {
+            this.preloadGapImage();
+            this.setState({
+                preloadAfterVisibleChange: false,
+            } as any);
+        } else {
+            this.preloadSingleImage();
+        }
+    }   
+}

+ 51 - 0
packages/semi-foundation/image/rtl.scss

@@ -0,0 +1,51 @@
+@import "./variables.scss";
+@import "./animation.scss";
+
+$module: #{$prefix}-image;
+
+.#{$prefix}-rtl,
+.#{$prefix}-portal-rtl {
+
+    .#{$module}-preview {
+
+        direction: rtl;
+
+        &-group {
+            direction: rtl;
+        }
+        
+        &-prev {
+            right: $spacing-image_preview_icon-x;
+            left: auto;
+            transform: $transform_rotate-image_preview_icon-rtl;
+        }
+    
+        &-next {
+            left: $spacing-image_preview_icon-x;
+            right: auto;
+            transform: $transform_rotate-image_preview_icon-rtl;
+        }
+
+        &-footer {
+
+            &-page {
+                display: flex;
+                direction: rtl;
+            }
+
+            &-gap {
+                margin-right: $spacing-image_preview_footer_gap-marginLeft;
+                margin-left: $spacing-image_preview_footer_gap_rtl-marginLeft;
+            }
+
+            .#{$prefix}-icon-chevron_left {
+                transform: $transform_rotate-image_preview_icon_rtl;
+            }
+
+            .#{$prefix}-icon-chevron_right {
+                transform: $transform_rotate-image_preview_icon_rtl;
+            }
+        }
+
+    }
+}

+ 86 - 0
packages/semi-foundation/image/utils.ts

@@ -0,0 +1,86 @@
+export const isTargetEmit = (event, targetClasses): boolean => {
+    // e.path is the event-triggered bubbling path, which stores each node through which bubbling passes.
+    // e.path.length-4 is to remove elements above the root node, such as body, html, document, window
+    const isTarget = event?.path?.slice(0, event.path.length - 4).some((node): boolean => {
+        if (node.className && typeof node.className === "string") {
+            return targetClasses.some(c => node.className.includes(c));
+        }
+        return false;
+    });
+    return isTarget;
+};
+
+export const downloadImage = (src: string, filename: string): void => {
+    const image = new Image();
+    image.src = src;
+    image.crossOrigin = "anonymous";
+    image.onload = (e): void => {
+        const eleLink = document.createElement("a");
+        eleLink.download = filename;
+        eleLink.style.display = "none";
+        eleLink.download = filename;
+        eleLink.href = src;
+        const canvas = document.createElement("canvas");
+        canvas.width = image.width;
+        canvas.height = image.height;
+        const context = canvas.getContext("2d");
+        context.drawImage(image, 0, 0, image.width, image.height);
+        eleLink.href = canvas.toDataURL("image/jpeg");
+        document.body.appendChild(eleLink);
+        eleLink.click();
+        document.body.removeChild(eleLink);
+    };
+};
+
+export const crossMerge = (leftArr = [], rightArr = []) => {
+    let newArr = [];
+    const leftLen = leftArr.length;
+    const rightLen = rightArr.length;
+    const crossLength = leftLen <= rightLen ? leftLen : rightLen;
+    (new Array(crossLength).fill(0)).forEach((item, index) => {
+        newArr.push(rightArr[index]);
+        newArr.push(leftArr[index]);
+    });
+    if (leftLen > rightLen) {
+        newArr = newArr.concat(leftArr.slice(rightLen, leftLen));
+    } else if (leftLen < rightLen) {
+        newArr = newArr.concat(rightArr.slice(leftLen, rightLen));
+    }
+    return newArr;
+};
+
+export const getPreloadImagArr = (imgSrc: string[], currentIndex: number, preLoadGap: number, infinite: boolean) => {
+    const beginIndex = currentIndex - preLoadGap;
+    const endIndex = currentIndex + preLoadGap;
+    const srcLength = imgSrc.length;
+    let leftArr = [];
+    let rightArr = [];
+    if ( preLoadGap >= Math.floor(srcLength / 2)) {
+        if (infinite) {
+            leftArr = imgSrc.concat(imgSrc).slice(beginIndex + srcLength < 0 ? 0 : beginIndex + srcLength, currentIndex + srcLength);
+            rightArr = imgSrc.concat(imgSrc).slice(currentIndex + 1, endIndex + 1 < 2 * srcLength ? endIndex + 1 : 2 * srcLength);
+        } else {
+            leftArr = imgSrc.slice(0, currentIndex);
+            rightArr = imgSrc.slice(currentIndex + 1, srcLength);
+        }
+    } else {
+        if (infinite) {
+            leftArr = imgSrc.concat(imgSrc).slice(beginIndex + srcLength, currentIndex + srcLength);
+            rightArr = imgSrc.concat(imgSrc).slice(currentIndex + 1, endIndex + 1);
+        } else {
+            if (beginIndex >= 0 && endIndex < srcLength) {
+                leftArr = imgSrc.slice(beginIndex, currentIndex);
+                rightArr = imgSrc.slice(currentIndex + 1, endIndex + 1);
+            } else if (beginIndex < 0) {
+                leftArr = imgSrc.slice(0, currentIndex);
+                rightArr = imgSrc.slice(currentIndex + 1, 2 * preLoadGap + 1);
+            } else {
+                rightArr = imgSrc.slice(currentIndex + 1, srcLength);
+                leftArr = imgSrc.slice(srcLength - 2 * preLoadGap - 1, currentIndex);
+            }
+        }
+    }
+    const result = crossMerge(leftArr.reverse(), rightArr);
+    const duplicateResult = Array.from(new Set(result));
+    return duplicateResult;
+};

+ 47 - 0
packages/semi-foundation/image/variables.scss

@@ -0,0 +1,47 @@
+$spacing-image_mask_info_text-marginTop: 8px; // 图像预览遮罩文字上外边距
+$spacing-image_preview_icon-x: 24px; // 图像预览中部左右调节icon与页面距离
+$spacing-image_preview_header-paddingY: 0; // 图像预览header部分上下内边距
+$spacing-image_preview_header-paddingX: 24px; // 图像预览header部分左右内边距
+$spacing-image_preview_footer-paddingY: 0; // 图像预览footer操作区部分上下内边距
+$spacing-image_preview_footer-paddingX: 16px; // 图像预览footer操作区部分左右内边距
+$spacing-image_preview_footer_page-marginY: 0; // 图像预览footer操作区图像页数据上下内边距
+$spacing-image_preview_footer_page-marginX: 12px; // 图像预览footer操作区图像页数据左右内边距
+$spacing-image_preview_footer_slider-paddingY: 0; // 图像预览footer操作区slider上下内边距
+$spacing-image_preview_footer_slider-paddingX: 16px; // 图像预览footer操作区slider左右内边距
+$spacing-image_preview_footer_slider_handle-marginTop: 8px; // 图像预览footer操作区slider的滑块上外边距
+$spacing-image_preview_footer_divider-marginY: 0;  // 图像预览footer操作区slider的分割线上下外边距
+$spacing-image_preview_footer_divider-marginX: 16px; // 图像预览footer操作区slider的分割线左右外边距
+$spacing-image_preview_footer_gap-marginLeft: 16px; // 图像预览footer操作区icon的左外边距
+$spacing-image_preview_footer_gap_rtl-marginLeft: 0; // 图像预览footer操作区在rtl模式下icon的左外边距
+
+$width-image_preview_footer_slider: 132px; // 图像预览footer操作区slider宽度
+$width-image_preview_footer_slider_handle: 16px; // 图像预览footer操作区滑块宽度
+$width-image_preview_icon: 40px; // 图像预览footer操作区icon宽度
+$width-image_preview_header_close: 30px; // 图像预览header部分的关闭热区宽度
+
+$height-image_preview_header: 60px; // 图像预览header部分高度
+$height-image_preview_footer: 48px; // 图像预览footer部分高度
+$height-image_preview_footer_slider: 2px; // 图像预览footer中slider高度
+$height-image_preview_footer_slider_handle: 16px; // 图像预览footer中slider的滑块高度
+$height-image_preview_icon: 40px; // 图像预览footer操作区icon高度
+$height-image_preview_header_close: 30px; // 图像预览header部分的关闭热区高度
+
+$radius-image: var(--semi-border-radius-small); // 图像圆角
+$radius-image_preview_footer: 6px; // 图像预览footer操作区圆角
+
+$color-image_mask-bg: var(--semi-color-overlay-bg); // 图像蒙层背景色
+$color-image_mask_info_text: var(--semi-color-white); // 图像蒙层文字颜色
+$color-image_status-bg: var(--semi-color-fill-0); // 图像加载失败背景颜色
+$color-image_status: var(--semi-color-disabled-text); // 图像状态加载失败 icon 颜色
+$color-image_preview-bg: var(--semi-color-overlay-bg); // 图像预览背景色
+$color-image_preview_icon: var(--semi-color-white); // 图像预览中部左右icon背景色
+$color-image_preview_header: var(--semi-color-white); // 图像预览header文字颜色
+$color-image_preview_footer_icon: var(--semi-color-white); // 图像预览footer中icon颜色
+$color-image_preview_footer_slider_rail: var(--semi-color-white); // 图像预览footer中slider滑轨颜色 
+// 以下几个颜色在明暗主题下一致,所以没有采用变量写法
+$color-image_preview_disabled: rgba(249, 249, 249, 0.35); // 图像预览禁用颜色
+$color-image_preview_icon-bg: rgba(0, 0, 0, 0.75); //图像预览中部icon背景色
+$color-image_header_close-bg: rgba(0, 0, 0, 0.75); //图像预览header的关闭icon的hover背景色
+$color-image_preview_footer-bg: rgba(0, 0, 0, 0.75); // 图像预览footer部分背景色
+$color-image-preview_divider-bg: rgba(255, 255, 255, .5); // 图像预览footer中的分割线背景色
+$color-image_preview_image_spin: #ccc; // 图像预览的加载状态颜色

+ 2 - 2
packages/semi-foundation/input/input.scss

@@ -1,6 +1,6 @@
 //@import '../theme/variables.scss';
-@import './variables.scss';
 @import "./animation.scss";
+@import './variables.scss';
 
 $module: #{$prefix}-input;
 
@@ -457,7 +457,7 @@ $module: #{$prefix}-input;
     background-color: transparent;
     box-sizing: border-box;
 
-    &[type="password"]::-ms-reveal, 
+    &[type="password"]::-ms-reveal,
     &[type="password"]::-ms-clear {
         display: none;
     }

+ 2 - 2
packages/semi-foundation/input/textarea.scss

@@ -1,5 +1,5 @@
-@import "./variables.scss";
 @import "./animation.scss";
+@import "./variables.scss";
 
 $module: #{$prefix}-input;
 
@@ -144,7 +144,7 @@ $module: #{$prefix}-input;
     cursor: text;
     box-sizing: border-box;
     color: $color-input_default-text-default;
-    
+
     &:hover {
         border-color: $color-textarea-border-hover;
     }

+ 0 - 1
packages/semi-foundation/inputNumber/inputNumber.scss

@@ -1,5 +1,4 @@
 @import "./animation.scss";
-@import '../input/variables.scss';
 @import './variables.scss';
 
 $module: #{$prefix}-input-number;

+ 3 - 3
packages/semi-foundation/inputNumber/variables.scss

@@ -1,9 +1,9 @@
 $height-inputNumber_button_default: $height-control-default; // 数字输入框高度 - 默认
 $height-inputNumber_button_large: $height-control-large; // 数字输入框高度 - 大
 $height-inputNumber_button_small: $height-control-small; // 数字输入框高度 - 小
-$height-inputNumber_button_inner_default: $height-input_default; // 隐藏步进器的数字输入框高度 - 默认
-$height-inputNumber_button_inner_large: $height-input_large; // 隐藏步进器的数字输入框高度 - 大
-$height-inputNumber_button_inner_small: $height-input_small; // 隐藏步进器的数字输入框高度 - 小
+$height-inputNumber_button_inner_default: $height-control-default - 2px; // 隐藏步进器的数字输入框高度 - 默认
+$height-inputNumber_button_inner_large: $height-control-large - 2px; // 隐藏步进器的数字输入框高度 - 大
+$height-inputNumber_button_inner_small: $height-control-small - 2px; // 隐藏步进器的数字输入框高度 - 小
 
 $width-inputNumber_button: 14px; // 步进器按钮宽度
 $radius-inputNumber: var(--semi-border-radius-small);

+ 1 - 1
packages/semi-foundation/navigation/foundation.ts

@@ -188,7 +188,7 @@ export default class NavigationFoundation<P = Record<string, any>, S = Record<st
         return item;
     }
 
-    getShouldOpenKeys(itemKeysMap: ItemKey2ParentKeysMap = {}, selectedKeys: string | number[] = []) {
+    getShouldOpenKeys(itemKeysMap: ItemKey2ParentKeysMap = {}, selectedKeys: string | number[]= []) {
         const willOpenKeySet = new Set();
 
         if (Array.isArray(selectedKeys) && selectedKeys.length) {

+ 1 - 1
packages/semi-foundation/navigation/navigation.scss

@@ -1,7 +1,7 @@
 //@import '../theme/variables.scss';
+@import "./animation.scss";
 @import "./variables.scss";
 @import "./mixin.scss";
-@import "./animation.scss";
 
 $module: #{$prefix}-navigation;
 

+ 2 - 2
packages/semi-foundation/navigation/variables.scss

@@ -46,9 +46,9 @@ $spacing-navigation_dropdown_item_nav_sub_title-paddingY: $spacing-tight; // 导
 $spacing-navigation_dropdown_item_nav_item-marginTop: 0; // 导航栏下拉菜单项顶部外边距
 $spacing-navigation_dropdown_item_nav_item-marginBottom: 0; // 导航栏下拉菜单项底部外边距
 $spacing-navigation_vertical_nav_item_last-marginBottom: 0; // 侧边导航栏下拉最后一个菜单项底部外边距
-$spacing-navigation_vertical_nav_header-paddingLeft:  ($width-navigation_container_collapsed - $spacing-base-tight - $width-navigation_border - $height-navigation_header_logo_collapsed) / 2; // 侧边导航栏 header 左侧内边距
+$spacing-navigation_vertical_nav_header-paddingLeft:  ($width-navigation_container_collapsed - $spacing-base-tight - $width-navigation_border - $height-navigation_header_logo_collapsed) * 0.5; // 侧边导航栏 header 左侧内边距
 $spacing-navigation_vertical_nav_header-paddingRight: $spacing-tight; // 侧边导航栏 header 右侧内边距
-$spacing-navigation_vertical_nav_header_collapsed-paddingLeft: ($width-navigation_container_collapsed - $spacing-base-tight - $width-navigation_border - $height-navigation_header_logo_collapsed) / 2; // 侧边导航栏收起后 header 左侧内边距
+$spacing-navigation_vertical_nav_header_collapsed-paddingLeft: ($width-navigation_container_collapsed - $spacing-base-tight - $width-navigation_border - $height-navigation_header_logo_collapsed) * 0.5; // 侧边导航栏收起后 header 左侧内边距
 $spacing-navigation_vertical_nav_header_collapsed-paddingRight: 0; // 侧边导航栏收起后 header 右侧内边距
 $spacing-navigation_vertical_footer-paddingLeft: $spacing-tight; // 侧边导航栏 footer 左侧内边距
 $spacing-navigation_vertical_footer-paddingRight: $spacing-tight; // 侧边导航栏 footer 右侧内边距

+ 1 - 2
packages/semi-foundation/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-foundation",
-    "version": "2.19.0-beta.0",
+    "version": "2.20.0-beta.1",
     "description": "",
     "scripts": {
         "build:lib": "node ./scripts/compileLib.js",
@@ -36,7 +36,6 @@
         "gulp-sass": "^5.0.0",
         "gulp-typescript": "^6.0.0-alpha.1",
         "merge2": "^1.4.1",
-        "sass": "1.45.0",
         "through2": "^4.0.2"
     }
 }

+ 1 - 1
packages/semi-foundation/pagination/pagination.scss

@@ -1,6 +1,6 @@
 //@import '../theme/variables.scss';
-@import "./variables.scss";
 @import "./animation.scss";
+@import "./variables.scss";
 
 $module: #{$prefix}-page;
 

+ 2 - 1
packages/semi-foundation/progress/constants.ts

@@ -7,12 +7,13 @@ const cssClasses = {
 const strings = {
     types: ['line', 'circle'],
     DEFAULT_TYPE: 'line',
+    STROKE_DEFAULT: 'var(--semi-color-success)',
     strokeLineCap: ['square', 'round'],
     DEFAULT_LINECAP: 'round',
     sizes: ['default', 'small', 'large'],
     DEFAULT_SIZE: 'default',
     directions: ['vertical', 'horizontal'],
-    DEFAULT_DIRECTION: 'horizontal'
+    DEFAULT_DIRECTION: 'horizontal',
 };
 
 const numbers = {};

+ 242 - 0
packages/semi-foundation/progress/generates.ts

@@ -0,0 +1,242 @@
+import { strings } from './constants';
+
+// type ColorType = 'Hex' | 'Hsl' | 'Hsla' | 'Rgb' | 'Rgba' | 'Semi Design Tokens';
+
+type Generate = {
+    startColor: string;
+    endColor: string;
+    size: number;
+};
+
+type StrokeSet = { percent: number; color: string };
+
+type StrokeArr = Array<StrokeSet>;
+
+function generateColor(s: StrokeArr, percent: number, gradient: boolean): string | undefined {
+    try {
+        const gradientColorArr = generate(s, percent, gradient);
+        if (gradientColorArr.length !== 0) return gradientColorArr;
+    } catch (e) {
+        return undefined;
+    }
+    return undefined;
+}
+
+function generate(s: StrokeArr, percent: number, gradient: boolean): string | undefined {
+    s.sort((a, b) => a.percent - b.percent);
+    if (s[0].percent > percent) {
+        return strings.STROKE_DEFAULT;
+    }
+    const endS = s[s.length - 1];
+    if (endS.percent < percent) {
+        return formatToHex(endS.color);
+    }
+    for (const [index, item] of s.entries()) {
+        if (item.percent === percent) {
+            return formatToHex(item.color);
+        }
+        if (percent > item.percent) continue;
+        const oldItem = s[index - 1];
+        if (!gradient) {
+            return formatToHex(oldItem.color);
+        }
+        return generateGradients(
+            {
+                startColor: formatToHex(oldItem.color),
+                endColor: formatToHex(item.color),
+                size: item.percent - oldItem.percent - 1,
+            },
+            percent - oldItem.percent - 1
+        ) as string;
+    }
+    return undefined;
+}
+
+function generateGradients(g: Generate, index: number | undefined): Array<string> | string {
+    const { startColor, endColor, size } = g;
+    const sA = startColor.split('');
+    const eA = endColor.split('');
+    const rC = [parseInt(`${sA[1]}${sA[2]}`, 16), parseInt(`${eA[1]}${eA[2]}`, 16)];
+    const gC = [parseInt(`${sA[3]}${sA[4]}`, 16), parseInt(`${eA[3]}${eA[4]}`, 16)];
+    const bC = [parseInt(`${sA[5]}${sA[6]}`, 16), parseInt(`${eA[5]}${eA[6]}`, 16)];
+    const aC = [parseInt(`${sA[7]}${sA[8]}`, 16), parseInt(`${eA[7]}${eA[8]}`, 16)];
+    const rStep = (rC[0] - rC[1]) / (size + 1);
+    const gStep = (gC[0] - gC[1]) / (size + 1);
+    const bStep = (bC[0] - bC[1]) / (size + 1);
+    const aStep = (aC[0] - aC[1]) / (size + 1);
+    function tHex(i: number) {
+        const rS = Math.round(rC[0] - rStep * (i + 1)).toString(16);
+        const gS = Math.round(gC[0] - gStep * (i + 1)).toString(16);
+        const bS = Math.round(bC[0] - bStep * (i + 1)).toString(16);
+        const h = `${padTwo(rS)}${padTwo(gS)}${padTwo(bS)}`;
+        const t = Math.floor(aStep * (i + 1) + aC[1]).toString(16);
+        return toHex.Hex(`#${h}`, t);
+    }
+    function padTwo(s: string) {
+        if (s.length === 1) {
+            return `0${s}`;
+        }
+        if (s.length === 0) {
+            return '00';
+        }
+        return s;
+    }
+    if (typeof index === 'undefined') {
+        const gradientColorArr = [startColor];
+        for (let i = 0; i < size; i += 1) {
+            gradientColorArr.push(tHex(i));
+        }
+        return gradientColorArr;
+    }
+    return tHex(index);
+}
+
+// Resolve the colour type contained within `ColorType` to Hex
+function formatToHex(color: string): string | undefined {
+    color = color.trim().toLowerCase();
+    // Hex
+    if (REG_S.hex.test(color)) {
+        return toHex.Hex(color, undefined);
+    }
+    // Hsl or Hsla
+    if (REG_S.hslA.test(color)) {
+        return toHex.Hex(toHex.HslA(color), undefined);
+    }
+    // Rgb or Rgba
+    if (REG_S.rgbA.test(color)) {
+        return toHex.Hex(toHex.RgbA(color), undefined);
+    }
+    // Semi Design Tokens
+    if (REG_S.semiDesignTokens.test(color)) {
+        if (SEMI_DESIGN_TOKENS.ALONG.indexOf(color) !== -1) {
+            return toHex.SemiDesignToken(color);
+        }
+        if (SEMI_DESIGN_TOKENS.SEQUENCE.indexOf(color) !== -1) {
+            return toHex.SemiDesignToken(`${color}-5`);
+        }
+        return toHex.SemiDesignToken(`${color}`);
+    }
+    return undefined;
+}
+
+const toHex = {
+    Hex(color: string, transparency: string | undefined): string {
+        color = color.replace('#', '');
+        if (color.length === 8) return `#${color}`;
+        if (color.length === 6) return `#${color}${transparency || 'ff'}`;
+        if (color.length === 3) {
+            color = color
+                .split('')
+                .map(c => c + c)
+                .join('');
+        }
+        return `#${color}${transparency || 'ff'}`;
+    },
+    SemiDesignToken(color: string): string | undefined {
+        // ! Only produces effects when used, the conditions for running need to occur after the real DOM is rendered
+        if (typeof window === 'undefined') {
+            return undefined;
+        }
+        const variable = getComputedStyle(document.body).getPropertyValue(`--semi-${color}`);
+        if (variable === '') return undefined;
+        const rgba = `rgba(${variable}, 1)`;
+        return toHex.RgbA(rgba);
+    },
+    HslA(color: string): string {
+        const hsla = REG_S.hslA.exec(color);
+        const h = parseInt(hsla[2]);
+        const s = parseInt(hsla[3]) / 100;
+        const l = parseInt(hsla[4]) / 100;
+        const a = hsla[5];
+        const c = (1 - Math.abs(2 * l - 1)) * s,
+            x = c * (1 - Math.abs(((h / 60) % 2) - 1)),
+            m = l - c / 2;
+        let r: string | number = 0,
+            g: string | number = 0,
+            b: string | number = 0;
+        if (0 <= h && h < 60) {
+            r = c;
+            g = x;
+            b = 0;
+        } else if (60 <= h && h < 120) {
+            r = x;
+            g = c;
+            b = 0;
+        } else if (120 <= h && h < 180) {
+            r = 0;
+            g = c;
+            b = x;
+        } else if (180 <= h && h < 240) {
+            r = 0;
+            g = x;
+            b = c;
+        } else if (240 <= h && h < 300) {
+            r = x;
+            g = 0;
+            b = c;
+        } else if (300 <= h && h < 360) {
+            r = c;
+            g = 0;
+            b = x;
+        }
+        r = Math.round((r + m) * 255).toString(16);
+        g = Math.round((g + m) * 255).toString(16);
+        b = Math.round((b + m) * 255).toString(16);
+        return toHex.utils.pAL(r, g, b, a);
+    },
+    RgbA(color: string): string {
+        const rgba = REG_S.rgbA.exec(color);
+        const r = parseInt(rgba[2], 10).toString(16),
+            g = parseInt(rgba[3], 10).toString(16),
+            b = parseInt(rgba[4], 10).toString(16),
+            a = rgba[5];
+        return toHex.utils.pAL(r, g, b, a);
+    },
+    utils: {
+        pAL(r: string, g: string, b: string, a: string) {
+            if (r.length == 1) r = '0' + r;
+            if (g.length == 1) g = '0' + g;
+            if (b.length == 1) b = '0' + b;
+            if (typeof a !== 'undefined') {
+                a = Math.round(parseInt(a) * 255).toString(16);
+                if (a.length == 1) a = '0' + a;
+                return '#' + r + g + b + a;
+            }
+            return '#' + r + g + b;
+        },
+    },
+};
+
+const REG_S = {
+    hex: /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,
+    hslA: /(hsl)a?\(\s*?(\d+),?\s*?(\d+)%,?\s*?(\d+)%,?\s*?\/?(\s*?[\d.]+)?\s*?\)/,
+    rgbA: /(rgb)a?\(\s*?(\d+),?\s*?(\d+),?\s*?(\d+),?\s*?\/?(\s*?[\d.]+)?\s*?\)/,
+    semiDesignTokens: /(\w+)?-?(\w+)-?(\d)?/,
+};
+
+// From src/components/palette.js
+const SEMI_DESIGN_TOKENS = {
+    // No sequence
+    ALONG:["black", "white"],
+    // Sequence: 0-9
+    SEQUENCE:[
+        "amber",
+        "blue",
+        "cyan",
+        "green",
+        "grey",
+        "indigo",
+        "light-blue",
+        "light-green",
+        "lime",
+        "orange",
+        "pink",
+        "purple",
+        "red",
+        "teal",
+        "violet",
+        "yellow"
+    ]
+};
+
+export { generateColor, StrokeArr };

+ 5 - 4
packages/semi-foundation/radio/radio.scss

@@ -1,5 +1,6 @@
-@import "./variables.scss";
 @import "./animation.scss";
+@import "./variables.scss";
+
 
 $module: #{$prefix}-radio;
 $inner-width: $width-icon-medium;
@@ -182,7 +183,7 @@ $inner-width: $width-icon-medium;
             .#{$module}-inner-checked .#{$module}-inner-display {
                 border-color: $color-radio_cardRadioGroup_checked_disabled-border-default;
             }
-            
+
             &:hover {
                 .#{$module}-inner-checked .#{$module}-inner-display {
                     border-color: $color-radio_cardRadioGroup_checked_disabled-border-hover;
@@ -228,7 +229,7 @@ $inner-width: $width-icon-medium;
             background: $color-radio_default-bg-default;
             transition: background-color $transition_duration-radio-bg $transition_function-radio-bg $transition_delay-radio-bg,
             border $transition_duration-radio-border $transition_function-radio-border $transition_delay-radio-border;
-        
+
             .#{$prefix}-icon {
                 width: 100%;
                 height: 100%;
@@ -426,4 +427,4 @@ $inner-width: $width-icon-medium;
     }
 }
 
-@import "./rtl.scss";
+@import "./rtl.scss";

+ 2 - 2
packages/semi-foundation/radio/variables.scss

@@ -67,7 +67,7 @@ $width-radio_inner: $width-icon-medium; // 单选按钮宽度
 $spacing-radio_addon-paddingLeft: $spacing-tight; //单选标题到单选按钮左侧边距
 $spacing-radio_addon-marginLeft: $width-radio_inner; //单选标题左侧整体外边距
 $spacing-radio_addon_buttonRadio_large-paddingX: $spacing-loose; // 大尺寸按钮式单选按钮水平内边距
-$spacing-radio_addon_buttonRadio_large-paddingY: $spacing-base-tight / 2; // 大尺寸按钮式单选按钮垂直内边距
+$spacing-radio_addon_buttonRadio_large-paddingY: $spacing-base-tight * 0.5; // 大尺寸按钮式单选按钮垂直内边距
 $spacing-radio_addon_buttonRadio_small-paddingX: $spacing-base; // 小尺寸按钮式单选按钮水平内边距
 $spacing-radio_addon_buttonRadio_small-paddingY: $spacing-super-tight; // 中尺寸按钮式单选按钮垂直内边距
 $spacing-radio_addon_buttonRadio_middle-paddingX: $spacing-base; // 中尺寸按钮式单选按钮水平内边距
@@ -102,4 +102,4 @@ $font-radio_cardRadioGroup_addon-fontWeight: $font-weight-bold; // 卡片式单
 $font-radio_cardRadioGroup_addon-lineHeight: 20px; // 卡片式单选组标题行高
 $font-radio_cardRadioGroup_extra-size: $font-size-regular; // 卡片式单选组副标题字体大小
 $font-radio_cardRadioGroup_extra-fontWeight: normal; // 卡片式单选组副标题字重
-$font-radio_cardRadioGroup_extra-lineHeight: 20px; // 卡片式单选组副标题行高
+$font-radio_cardRadioGroup_extra-lineHeight: 20px; // 卡片式单选组副标题行高

+ 2 - 1
packages/semi-foundation/rating/rating.scss

@@ -1,5 +1,6 @@
-@import './variables.scss';
 @import "./animation.scss";
+@import './variables.scss';
+
 
 $module: #{$prefix}-rating;
 

+ 3 - 2
packages/semi-foundation/scrollList/scrollList.scss

@@ -1,7 +1,8 @@
 //@import '../theme/variables.scss';
 // @import '../theme/_font.scss';
-@import './variables.scss';
 @import './animation.scss';
+@import './variables.scss';
+
 
 $module: #{$prefix}-scrolllist;
 
@@ -44,7 +45,7 @@ $module: #{$prefix}-scrolllist;
                     content: '';
                     display: block;
                     width: 100%;
-                    height: ($height-scrollList - $height-scrollList_item) / 2;
+                    height: ($height-scrollList - $height-scrollList_item) * 0.5;
                 }
             }
         }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно