Jelajahi Sumber

chore: solve conflict

zhangyumei.0319 3 tahun lalu
induk
melakukan
7a0e534df5
100 mengubah file dengan 3227 tambahan dan 459 penghapusan
  1. 1 1
      content/feedback/banner/index-en-US.md
  2. 1 1
      content/feedback/banner/index.md
  3. 1 1
      content/feedback/notification/index-en-US.md
  4. 1 1
      content/feedback/notification/index.md
  5. 1 1
      content/feedback/popconfirm/index-en-US.md
  6. 1 1
      content/feedback/popconfirm/index.md
  7. 147 2
      content/feedback/progress/index-en-US.md
  8. 151 5
      content/feedback/progress/index.md
  9. 5 4
      content/feedback/skeleton/index-en-US.md
  10. 16 17
      content/feedback/skeleton/index.md
  11. 1 1
      content/feedback/spin/index-en-US.md
  12. 1 1
      content/feedback/spin/index.md
  13. 28 1
      content/feedback/toast/index-en-US.md
  14. 30 3
      content/feedback/toast/index.md
  15. 57 56
      content/navigation/navigation/index.md
  16. 1 0
      content/order.js
  17. 1 1
      content/other/configprovider/index-en-US.md
  18. 1 1
      content/other/configprovider/index.md
  19. 1 1
      content/other/locale/index-en-US.md
  20. 1 1
      content/other/locale/index.md
  21. 1 1
      content/show/dropdown/index-en-US.md
  22. 509 0
      content/show/image/index-en-US.md
  23. 509 0
      content/show/image/index.md
  24. 2 2
      content/show/list/index-en-US.md
  25. 1 1
      content/show/list/index.md
  26. 1 1
      content/show/modal/index-en-US.md
  27. 224 218
      content/show/modal/index.md
  28. 1 1
      content/show/overflowlist/index-en-US.md
  29. 1 1
      content/show/overflowlist/index.md
  30. 1 1
      content/show/popover/index-en-US.md
  31. 1 1
      content/show/popover/index.md
  32. 1 1
      content/show/scrolllist/index-en-US.md
  33. 1 1
      content/show/scrolllist/index.md
  34. 1 1
      content/show/sidesheet/index-en-US.md
  35. 1 1
      content/show/sidesheet/index.md
  36. 1 1
      content/show/table/index-en-US.md
  37. 1 1
      content/show/table/index.md
  38. 18 1
      content/show/tag/index-en-US.md
  39. 18 1
      content/show/tag/index.md
  40. 1 1
      content/show/timeline/index-en-US.md
  41. 1 1
      content/show/timeline/index.md
  42. 1 1
      content/show/tooltip/index-en-US.md
  43. 1 1
      content/show/tooltip/index.md
  44. 18 1
      content/start/changelog/index-en-US.md
  45. 25 8
      content/start/changelog/index.md
  46. 1 0
      content/start/overview/index-en-US.md
  47. 1 0
      content/start/overview/index.md
  48. 1 1
      lerna.json
  49. 4 4
      package.json
  50. 2 2
      packages/semi-animation-react/package.json
  51. 0 12
      packages/semi-animation-styled/getBabelConfig.js
  52. 1 4
      packages/semi-animation-styled/package.json
  53. 1 1
      packages/semi-animation/package.json
  54. 1 1
      packages/semi-eslint-plugin/package.json
  55. 1 1
      packages/semi-foundation/anchor/anchor.scss
  56. 1 1
      packages/semi-foundation/autoComplete/autoComplete.scss
  57. 2 2
      packages/semi-foundation/badge/badge.scss
  58. 2 2
      packages/semi-foundation/breadcrumb/breadcrumb.scss
  59. 1 1
      packages/semi-foundation/button/button.scss
  60. 1 2
      packages/semi-foundation/calendar/foundation.ts
  61. 2 2
      packages/semi-foundation/calendar/variables.scss
  62. 1 1
      packages/semi-foundation/carousel/carousel.scss
  63. 2 1
      packages/semi-foundation/cascader/cascader.scss
  64. 2 2
      packages/semi-foundation/checkbox/checkbox.scss
  65. 6 5
      packages/semi-foundation/datePicker/foundation.ts
  66. 6 6
      packages/semi-foundation/datePicker/monthsGridFoundation.ts
  67. 5 5
      packages/semi-foundation/datePicker/variables.scss
  68. 2 2
      packages/semi-foundation/datePicker/yearAndMonthFoundation.ts
  69. 1 1
      packages/semi-foundation/dropdown/dropdown.scss
  70. 14 0
      packages/semi-foundation/form/form.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. 224 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. 264 0
      packages/semi-foundation/image/previewInnerFoundation.ts
  83. 51 0
      packages/semi-foundation/image/rtl.scss
  84. 88 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. 2 9
      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

+ 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

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

@@ -427,7 +427,7 @@ function DropdownEvents() {
 | children | Child elements wrapped by the drop layer | ReactNode |  |  |
 | clickToHide | Whether to close the drop-down layer automatically when clicking on the drop-down layer | boolean |  | **0.24.0** |
 | contentClassName | Drop-down menu root element class name | string |  |  |
-| getPopupContainer | Specifies the parent DOM, and the bullet layer will be rendered to the DOM, you need to set 'position: relative` | function():HTMLElement | () = > document.body |
+| getPopupContainer | Specifies the parent DOM, and the bullet layer will be rendered to the DOM, you need to set 'position: relative` | function():HTMLElement | () => document.body |
 | mouseEnterDelay | After the mouse is moved into the Trigger, the display time is delayed, in milliseconds (only effective when the trigger is hover/focus) | number | 50 |  |
 | mouseLeaveDelay | The time for the delay to disappear after the mouse moves out of the pop-up layer, in milliseconds (only effective when the trigger is hover/focus) | number | 50 |  |
 | menu | Menu content config | Array<DropdownMenuItem\> | [] | **1.12.0** |

+ 509 - 0
content/show/image/index-en-US.md

@@ -0,0 +1,509 @@
+---
+localeCode: en-US
+order: 54
+category: Show
+title: Image
+icon: doc-image
+brief: Used to display and preview images.
+---
+
+## Demos
+
+### How to import
+
+Image, ImagePreview supported since v2.20.0
+
+```jsx import
+import { Image, ImagePreview } from '@douyinfe/semi-ui';
+```
+
+### Basic usage
+
+You can get an image with preview function by specifying the image path through `src`, and specify the width and height of the image through `width`, `height`
+
+```jsx live=true dir="column"
+import React from 'react';
+import { Image } from '@douyinfe/semi-ui';
+
+() => (  
+    <Image 
+        width={360}
+        height={200}
+        src="https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg"
+    />
+);
+```
+
+### Loading failed placeholder
+
+You can customize the placeholder for failed loading through `fallback`, which supports string and ReactNode
+
+```jsx live=true
+import React from 'react';
+import { Image } from '@douyinfe/semi-ui';
+
+() => (
+    <div style={{ display: 'flex', alignItem: 'center', flexDirection: 'column' }}>
+        <span>Failed to load default style</span>
+        <Image 
+            width={200}
+            height={200}
+            src="https://load-error.jpeg"
+        />
+        <br />
+        <span>Custom loading failed placeholder map</span>
+        <Image 
+            width={200}
+            height={200}
+            src="https://load-error.jpeg"
+            fallback={<IconUploadError style={{ fontSize: 50 }} />}
+            />
+    </div>
+);
+```
+
+### Progressive loading
+
+Large images can be progressively loaded through `placeholder`
+
+```jsx live=true
+import React from 'react';
+import { Image, Button } from '@douyinfe/semi-ui';
+
+() => {
+    const [timestamp, setTimestamp] = React.useState('');
+    return (  
+        <>
+            <Image 
+                width={300}
+                height={200}
+                src={`https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-big.png?${timestamp}`}
+                placeholder={<Image 
+                    src='https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-small.jpeg'
+                    width={300}
+                    height={200}
+                    preview={false}
+                />}
+            />
+            <br />
+            <Button 
+                theme={'solid'}
+                onClick={() => {
+                    setTimestamp(Date.now());
+                }}
+                style={{ marginTop: 10 }}
+            >Reload</Button>
+        </>
+    );
+};
+```
+
+### Customize the preview image
+
+You can customize the preview image by setting the `src` of the Image component to be different from the `src` in the `preview` parameter
+
+ ```jsx live=true
+import React from 'react';
+import { Image } from '@douyinfe/semi-ui';
+
+() => {
+     return ( 
+          <Image
+             width={300}
+             height={200}
+             src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-small.jpeg'}
+             preview={{
+                 src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-big.png'
+             }}
+         />
+     );
+};
+```
+
+### Multi-image preview
+
+Use ImagePreview to wrap Image to achieve multi-image preview
+
+```jsx live=true dir="column"
+import React, { useMemo } from 'react';
+import { Image, ImagePreview } from '@douyinfe/semi-ui';
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/colorful.jpg",
+    ]), []);
+
+    return ( 
+        <ImagePreview>
+            {srcList.map((src, index) => {
+                return (
+                    <Image 
+                        key={index} 
+                        src={src} 
+                        width={200} 
+                        alt={`lamp${index + 1}`}
+                        style={{ marginRight: 5 }} 
+                    />
+                );
+            })}
+        </ImagePreview>
+    );
+};
+```
+
+### Use the preview component alone
+
+The preview component ImagePreview can be used alone, through `visible` and `onVisibleChange` to control whether to preview, and `src` to pass in the image that can be previewed
+
+```jsx live=true
+import React, { useMemo, useCallback } from 'react';
+import { ImagePreview, Button } from '@douyinfe/semi-ui';
+
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/colorful.jpg",
+    ]), []);
+
+    const [visible1, setVisible1] = useState(false);
+    const [visible2, setVisible2] = useState(false);
+
+    const visibleChange1 = useCallback((v) => {
+        setVisible1(v);
+    }, []);
+
+    const visibleChange2 = useCallback((v) => {
+        setVisible2(v);
+    }, []);
+
+    const onButton1Click = useCallback((v) => {
+        setVisible1(true);
+    }, []);
+
+    const onButton2Click = useCallback((v) => {
+        setVisible2(true);
+    }, []);
+
+    return ( 
+        <>
+            <Button onClick={onButton1Click}>Preview single Image</Button>
+            <ImagePreview
+                src={"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg"}
+                visible={visible1}
+                onVisibleChange={visibleChange1}
+            />
+            <br /> 
+            <Button onClick={onButton2Click} style={{ marginTop: 20 }}>Preview multiple Images</Button>
+            <ImagePreview
+                src={srcList}
+                visible={visible2}
+                onVisibleChange={visibleChange2}
+            />
+        </>
+    );
+};
+```
+
+### Render in the specified container
+
+You can specify the parent DOM of the preview component through `getPopupContainer` (you need to specify `position: relative`), and the image preview will be rendered into this DOM
+
+```jsx live=true dir="column"
+import React, { useMemo } from 'react';
+import { Image, ImagePreview } from '@douyinfe/semi-ui';
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+    ]), []);
+
+    return ( 
+        <>
+            <div 
+                id="container" 
+                style={{ 
+                    height: 400, 
+                    position: "relative" 
+                }} 
+            >
+                <ImagePreview
+                    getPopupContainer={() => {
+                        const node = document.getElementById("container");
+                        return node;
+                    }}
+                    style={{
+                        height: '100%',
+                        display: 'flex',
+                        alignItems: 'center',
+                        justifyContent: 'center',
+        
+                    }}
+                >
+                    {srcList.map((src, index) => {
+                        return (
+                            <Image 
+                                key={index} 
+                                src={src} 
+                                width={200} 
+                                alt={`lamp${index + 1}`}
+                                style={{ marginRight: 5 }} 
+                            />
+                        );
+                    })}
+                </ImagePreview>
+            </div>
+        </>
+    );
+};
+```
+
+### Customize the bottom operation area of the preview
+
+The bottom action area of the preview can be customized using `renderPreviewMenu`
+
+```jsx live=true dir="column"
+import React, { useMemo, useCallback } from 'react';
+import { Image, ImagePreview, Button } from '@douyinfe/semi-ui';
+import { IconChevronLeft, IconChevronRight, IconMinus, IconPlus, IconRotate, IconDownload, IconRealSizeStroked, IconWindowAdaptionStroked } from "@douyinfe/semi-icons";
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+    ]), []);
+
+    const renderPreviewMenu = useCallback((props) => {
+        const {
+            ratio,
+            disabledPrev,
+            disabledNext,
+            disableZoomIn,
+            disableZoomOut,
+            disableDownload,
+            onDownload,
+            onNext,
+            onPrev,
+            onRotateLeft,
+            onRatioClick,
+            onZoomIn,
+            onZoomOut,
+        } = props;
+        return (
+            <div 
+                style={{ 
+                    background: "grey", 
+                    height: 40, 
+                    width: 280, 
+                    display: "flex",
+                    alignItems: "center",
+                    justifyContent: "space-around",
+                    borderRadius: 3,
+                }}
+            >
+                <Button
+                    icon={<IconChevronLeft size="large" />}
+                    type="tertiary"
+                    onClick={!disabledPrev ? onPrev : undefined}
+                    disabled={disabledPrev}
+                />
+                <Button
+                    icon={<IconChevronRight size="large" />}
+                    type="tertiary"                     
+                    onClick={!disabledNext ? onNext : undefined}
+                    disabled={disabledNext}
+                />
+                <Button
+                    icon={<IconMinus size="large" />}
+                    type="tertiary"
+                    onClick={!disableZoomOut ? onZoomOut : undefined}
+                    disabled={disableZoomOut} 
+                />
+                <Button
+                    icon={<IconPlus size="large" />}
+                    type="tertiary"
+                    onClick={!disableZoomIn ? onZoomIn : undefined} 
+                    disabled={disableZoomIn}
+                />
+                <Button
+                    icon={ratio === "adaptation" ? <IconRealSizeStroked size="large" /> : <IconWindowAdaptionStroked size="large" />}
+                    type="tertiary"
+                    onClick={onRatioClick} 
+                />
+                <Button
+                    icon={<IconRotate size="large" />}
+                    type="tertiary"
+                    onClick={onRotateLeft}
+                />
+                <Button
+                    icon={<IconDownload size="large" />}
+                    type="tertiary"
+                    onClick={!disableDownload ? onDownload : undefined}
+                    disabled={disableDownload}
+                />
+            </div>);
+    }, []);
+
+
+    return ( 
+        <ImagePreview renderPreviewMenu={renderPreviewMenu}>
+            {srcList.map((src, index) => {
+                return (
+                    <Image 
+                        key={index} 
+                        src={src} 
+                        width={200} 
+                        alt={`lamp${index + 1}`}
+                        style={{ marginRight: 5 }} 
+                    />
+                );
+            })}
+        </ImagePreview>
+    );
+};
+```
+
+### Customize the preview top display area
+
+You can customize the preview top display area through `renderHeader`
+
+```jsx live=true dir="column"
+import React, { useMemo } from 'react';
+import { Image, ImagePreview } from '@douyinfe/semi-ui';
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+    ]), []);
+
+    return (
+        <>  
+            <ImagePreview
+                renderHeader={(title) => (
+                    <div 
+                        style={{ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" }}>
+                        <span style={{ background: "black", padding: '0 10px' }}>Custom title:{title}</span>
+                    </div>
+                )}
+            >
+                <div >
+                    {srcList.map((src, index) => {
+                        return (
+                            <Image 
+                                key={index} 
+                                src={src} 
+                                width={200} 
+                                alt={`lamp${index + 1}`} 
+                                preview={{
+                                    previewTitle: `lamp${index + 1}`,
+                                }} 
+                                style={{ marginRight: 5 }}
+                            />
+                        );
+                    })}
+                </div>
+            </ImagePreview>
+        </>
+    );
+};
+```
+
+## API reference
+
+### Image
+
+| Properties       |Instructions                          | Type             | Default | Version |
+|------------------|--------------------------------------|------------------|---------|---------|
+| alt              | Image description                    | string            | - | |
+| className        | custom style class name              | string            | - | |
+| crossOrigin      | Passthrough to the crossorigin of the native img tag | 'anonymous' \| 'use-credentials' |-| |
+| fallback         | Custom loading failed display content | ReactNode  | - | |
+| height           | Image display height                 | number            | - | |
+| onError          | Load error callback                  | (event: Event) => void | - | |
+| onLoad           | Load success callback                | (event: Event) => void | - | |
+| placeholder      | Placeholder content when the image is not loaded | ReactNode | - | |
+| preview          | Preview parameter, when false, disable preview   | boolean \| ImagePreview | - | |
+| src              | Image acquisition address            | string            | - | |
+| style            | custom style                         | CSSProperties     | - | |
+| width            | Image display width                  | number            | - | |
+
+### ImagePreview
+
+| Properties       | Instructions            | Type            | Default | Version |
+|------------------|-------------------------|-----------------|---------|---------|
+| adaptiveTip      | Adapt to page action button prompts |string  | "Adapt to the page" | |
+| className        | Custom style class name | string          | - | |
+| closable         | Whether to show the close button   | Boolean | true | |
+| closeOnEsc       | Hit esc to close the preview | boolean        | true | |
+| currentIndex     | Controlled property, the current preview image subscript | number | - | |
+| defaultCurrentIndex | First display image subscript | number | - | |
+| defaultVisible   | Whether to open the preview for the first time | boolean | - | |
+| disableDownload  | Disable downloads        | boolean        | false | |
+| downloadTip      | Download action button prompt | string  | "Download" | |
+| getPopupContainer | Specify the parent DOM, and the pop-up layer will be rendered into the DOM. For customization, you need to set container `position: relative`|() => HTMLElement;  | () => document.body | |
+| infinite         | Whether to loop infinitely  | boolean        | false | |
+| lazyLoad         | Whether to enable lazy loading | boolean      | true | |
+| lazyLoadMargin   | Pass to the rootMargin parameter in options, refer to [Intersection Observer API](https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API#interfaces) | string | "0px 100px 100px 0px" | |
+| maskClosable     | Whether the mask can be closed by clicking | Boolean  | true | |
+| nextTip          | Next action button prompt   | string  | "Next" | |
+| originTip        | Original size action button tips |string  | "Original size" | |
+| onChange         | Event triggered by switching pictures  | (index: number) => void | - | |
+| onClose          | The callback function when the close button is clicked  | () => void | - | |
+| onDownload       | Image download callback function  | (src: string, index: number) => void | - | |
+| onRotateLeft     | Callback for rotating the image     | (angle: number) => void | - | |
+| onNext           | Callback for switching pictures backwards   | (index: number) => void | - | |
+| onPrev           | Callback for switching the picture forward  | (index: number) => void | - | |
+| onZoomIn         | The callback function when the image is zoomed in  | (zoom: number) => void | - | |
+| onZoomOut        | The callback function when the image is zoomed out  | (zoom: number) => void | - | |
+| onVisibleChange  | Callback triggered by toggle visible state   | (visible: boolean) => void  | - | |
+| preLoad          | Whether to enable preloading | boolean | true | |
+| preLoadGap       | Preloaded step size      | number         | 2 | |
+| previewTitle     | Custom preview title     | ReactNode      | - | |
+| prevTip          | Previous operation button prompt   | string  | "Previous" | |
+| renderHeader     | Custom render preview top info  |(info: reactNode) => ReactNode  | - | |
+| renderPreviewMenu | Custom render preview bottom menu information | (props: MenuProps) => ReactNode; | - | |
+| rotateTip        | Tips for rotating action buttons  |string        | "Rotate" | |
+| showTooltip      | Whether to display the bottom operation area prompt | boolean | false | |
+| src              | Image list information  | string \| string[] | - | |
+| style            | Custom style            | CSSProperties   | - | |
+| viewerVisibleDelay | The length of time of inactivity before hiding the preview action button | number | 10000 | |
+| visible          | Controlled property, whether to preview  | boolean | - | |
+| zIndex           | Preview layer hierarchy  | number        | 1070 | |
+| zoomInTip        | Zoom in action button tips | string | "Zoom in" | |
+| zoomOutTip       | Zoom out action button prompt | string | "Zoom out" | |
+| zoomStep         | Image reduction/enlargement ratio each time | number | 0.1 | |
+
+### MenuProps
+
+| Properties       | Instructions            | Type             |
+|------------------|-------------------------|------------------|
+| curPage          | Current image page subscript         | number |
+| disabledPrev     | Whether to disable the left toggle button  | boolean |
+| disabledNext     | Whether to disable the right toggle button | boolean |
+| disableDownload  | Whether to disable the download button     | boolean |
+| max              | The maximum ratio of image zoom      | number |
+| min              | The minimum ratio of image scaling   | number |
+| onDownload       | Call function when the image is downloaded | () => void |
+| onZoomIn         | Call function when the image is zoomed in  | () => void |
+| onZoomOut        | Call function when the image is zoomed out | () => void |
+| onPrev           | Call function to switch the picture forward  | () => void |
+| onNext           | Call function to switch the picture backward | () => void |
+| onRotateLeft     | Call function to rotate the image counterclockwise | () => void |
+| onRotateRight    | Call function to rotate the image clockwise | () => void |
+| ratio            | Original size or Fit to page button state  | "adaptation" \| "realSize" |
+| step             | Step size of scaling                 | number |
+| totalNum         | The total number of images that can be previewed | number |
+| zoom             | Current image magnification ratio    | number |
+
+## Design Token
+
+<DesignToken/>

+ 509 - 0
content/show/image/index.md

@@ -0,0 +1,509 @@
+---
+localeCode: zh-CN
+order: 54
+category: 展示类
+title: Image 图片
+icon: doc-image
+brief: 用于展示和预览图片。
+---
+
+## 代码演示
+
+### 如何引入
+
+Image, ImagePreview 从 v2.20.0 版本开始支持
+
+```jsx import
+import { Image, ImagePreview } from '@douyinfe/semi-ui';
+```
+
+### 基本用法
+
+通过 `src` 指定图片路径即可获取一个具有预览功能的图片,通过 `width`,`height` 指定图片的宽高
+
+```jsx live=true dir="column"
+import React from 'react';
+import { Image } from '@douyinfe/semi-ui';
+
+() => (  
+    <Image 
+        width={360}
+        height={200}
+        src="https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg"
+    />
+);
+```
+
+### 加载失败的占位图
+
+可通过 `fallback` 自定义加载失败的占位图,该参数类型支持 string 和 ReactNode
+
+```jsx live=true
+import React from 'react';
+import { Image } from '@douyinfe/semi-ui';
+
+() => (
+    <div style={{ display: 'flex', alignItem: 'center', flexDirection: 'column' }}>
+        <span>加载失败默认样式</span>
+        <Image 
+            width={200}
+            height={200}
+            src="https://load-error.jpeg"
+        />
+        <br />
+        <span>自定义加载失败占位图</span>
+        <Image 
+            width={200}
+            height={200}
+            src="https://load-error.jpeg"
+            fallback={<IconUploadError style={{ fontSize: 50 }} />}
+        />
+    </div>
+);
+```
+
+### 渐进加载
+
+大图可通过`placeholder`实现渐进加载
+
+```jsx live=true
+import React from 'react';
+import { Image, Button } from '@douyinfe/semi-ui';
+
+() => {
+    const [timestamp, setTimestamp] = React.useState('');
+    return (  
+        <>
+            <Image 
+                width={300}
+                height={200}
+                src={`https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-big.png?${timestamp}`}
+                placeholder={<Image 
+                    src='https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-small.jpeg'
+                    width={300}
+                    height={200}
+                    preview={false}
+                />}
+            />
+            <br />
+            <Button 
+                theme={'solid'}
+                onClick={() => {
+                    setTimestamp(Date.now());
+                }}
+                style={{ marginTop: 10 }}
+            >Reload</Button>
+        </>
+    );
+};
+```
+
+### 自定义预览图片
+
+可以通过设置 Image 组件的 `src` 和 `preview` 参数中的 `src` 不同来自定义预览图片
+
+ ```jsx live=true
+import React from 'react';
+import { Image } from '@douyinfe/semi-ui';
+
+() => {
+     return ( 
+         <Image
+             width={300}
+             height={200}
+             src={'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-small.jpeg'}
+             preview={{
+                 src: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract-big.png'
+             }}
+         />
+     );
+};
+```
+
+### 多图预览
+
+使用 ImagePreview 包裹 Image 即可实现多图片预览
+
+```jsx live=true dir="column"
+import React, { useMemo } from 'react';
+import { Image, ImagePreview } from '@douyinfe/semi-ui';
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/colorful.jpg",
+    ]), []);
+
+    return ( 
+        <ImagePreview>
+            {srcList.map((src, index) => {
+                return (
+                    <Image 
+                        key={index} 
+                        src={src} 
+                        width={200} 
+                        alt={`lamp${index + 1}`} 
+                        style={{ marginRight: 5 }}
+                    />
+                );
+            })}
+        </ImagePreview>
+    );
+};
+```
+
+### 单独使用预览组件
+
+预览组件 ImagePreview 可以单独使用,通过 `visible` 和 `onVisibleChange` 控制是否预览,通过 `src` 传入可以预览的图片
+
+```jsx live=true
+import React, { useMemo, useCallback } from 'react';
+import { ImagePreview, Button } from '@douyinfe/semi-ui';
+
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/colorful.jpg",
+    ]), []);
+
+    const [visible1, setVisible1] = useState(false);
+    const [visible2, setVisible2] = useState(false);
+
+    const visibleChange1 = useCallback((v) => {
+        setVisible1(v);
+    }, []);
+
+    const visibleChange2 = useCallback((v) => {
+        setVisible2(v);
+    }, []);
+
+    const onButton1Click = useCallback((v) => {
+        setVisible1(true);
+    }, []);
+
+    const onButton2Click = useCallback((v) => {
+        setVisible2(true);
+    }, []);
+
+    return ( 
+        <>
+            <Button onClick={onButton1Click}>Preview single Image</Button>
+            <ImagePreview
+                src={"https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg"}
+                visible={visible1}
+                onVisibleChange={visibleChange1}
+            />
+            <br /> 
+            <Button onClick={onButton2Click} style={{ marginTop: 20 }}>Preview multiple Images</Button>
+            <ImagePreview
+                src={srcList}
+                visible={visible2}
+                onVisibleChange={visibleChange2}
+            />
+        </>
+    );
+};
+```
+
+### 渲染在指定容器
+
+可以通过 `getPopupContainer` 指定预览组件的父级 DOM(需要指定 `position: relative`),图片预览将会渲染至该 DOM 中
+
+```jsx live=true dir="column"
+import React, { useMemo } from 'react';
+import { Image, ImagePreview } from '@douyinfe/semi-ui';
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+    ]), []);
+
+    return ( 
+        <>
+            <div 
+                id="container" 
+                style={{ 
+                    height: 400, 
+                    position: "relative" 
+                }} 
+            >
+                <ImagePreview
+                    getPopupContainer={() => {
+                        const node = document.getElementById("container");
+                        return node;
+                    }}
+                    style={{
+                        height: '100%',
+                        display: 'flex',
+                        alignItems: 'center',
+                        justifyContent: 'center',
+        
+                    }}
+                >
+                    {srcList.map((src, index) => {
+                        return (
+                            <Image 
+                                key={index} 
+                                src={src} 
+                                width={200} 
+                                alt={`lamp${index + 1}`}
+                                style={{ marginRight: 5 }} 
+                            />
+                        );
+                    })}
+                </ImagePreview>
+            </div>
+        </>
+    );
+};
+```
+
+### 自定义预览底部操作区
+
+可以使用 `renderPreviewMenu` 自定义预览底部操作区域
+
+```jsx live=true dir="column"
+import React, { useMemo, useCallback } from 'react';
+import { Image, ImagePreview, Button } from '@douyinfe/semi-ui';
+import { IconChevronLeft, IconChevronRight, IconMinus, IconPlus, IconRotate, IconDownload, IconRealSizeStroked, IconWindowAdaptionStroked } from "@douyinfe/semi-icons";
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+    ]), []);
+
+    const renderPreviewMenu = useCallback((props) => {
+        const {
+            ratio,
+            disabledPrev,
+            disabledNext,
+            disableZoomIn,
+            disableZoomOut,
+            disableDownload,
+            onDownload,
+            onNext,
+            onPrev,
+            onRotateLeft,
+            onRatioClick,
+            onZoomIn,
+            onZoomOut,
+        } = props;
+        return (
+            <div 
+                style={{ 
+                    background: "grey", 
+                    height: 40, 
+                    width: 280, 
+                    display: "flex",
+                    alignItems: "center",
+                    justifyContent: "space-around",
+                    borderRadius: 3,
+                }}
+            >
+                <Button
+                    icon={<IconChevronLeft size="large" />}
+                    type="tertiary"
+                    onClick={!disabledPrev ? onPrev : undefined}
+                    disabled={disabledPrev}
+                />
+                <Button
+                    icon={<IconChevronRight size="large" />}
+                    type="tertiary"                     
+                    onClick={!disabledNext ? onNext : undefined}
+                    disabled={disabledNext}
+                />
+                <Button
+                    icon={<IconMinus size="large" />}
+                    type="tertiary"
+                    onClick={!disableZoomOut ? onZoomOut : undefined}
+                    disabled={disableZoomOut} 
+                />
+                <Button
+                    icon={<IconPlus size="large" />}
+                    type="tertiary"
+                    onClick={!disableZoomIn ? onZoomIn : undefined} 
+                    disabled={disableZoomIn}
+                />
+                <Button
+                    icon={ratio === "adaptation" ? <IconRealSizeStroked size="large" /> : <IconWindowAdaptionStroked size="large" />}
+                    type="tertiary"
+                    onClick={onRatioClick} 
+                />
+                <Button
+                    icon={<IconRotate size="large" />}
+                    type="tertiary"
+                    onClick={onRotateLeft}
+                />
+                <Button
+                    icon={<IconDownload size="large" />}
+                    type="tertiary"
+                    onClick={!disableDownload ? onDownload : undefined}
+                    disabled={disableDownload}
+                />
+            </div>);
+    }, []);
+
+
+    return ( 
+        <ImagePreview renderPreviewMenu={renderPreviewMenu}>
+            {srcList.map((src, index) => {
+                return (
+                    <Image 
+                        key={index} 
+                        src={src} 
+                        width={200} 
+                        alt={`lamp${index + 1}`} 
+                        style={{ marginRight: 5 }}
+                    />
+                );
+            })}
+        </ImagePreview>
+    );
+};
+```
+
+### 自定义预览顶部展示区
+
+通过 `renderHeader` 可以自定义预览顶部展示区
+
+```jsx live=true dir="column"
+import React, { useMemo } from 'react';
+import { Image, ImagePreview } from '@douyinfe/semi-ui';
+
+() => {
+    const srcList = useMemo(() => ([
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+    ]), []);
+
+    return (
+        <>  
+            <ImagePreview
+                renderHeader={(title) => (
+                    <div 
+                        style={{ width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center" }}>
+                        <span style={{ background: "black", padding: '0 10px' }}>自定义标题:{title}</span>
+                    </div>
+                )}
+            >
+                <div >
+                    {srcList.map((src, index) => {
+                        return (
+                            <Image 
+                                key={index} 
+                                src={src} 
+                                width={200} 
+                                alt={`lamp${index + 1}`} 
+                                preview={{
+                                    previewTitle: `lamp${index + 1}`,
+                                }}
+                                style={{ marginRight: 5 }} 
+                            />
+                        );
+                    })}
+                </div>
+            </ImagePreview>
+        </>
+    );
+};
+```
+
+## API 参考
+
+### Image
+
+| 属性              | 说明                                    | 类型              | 默认值 | 版本 |
+|-------------------|---------------------------------------|-------------------|------|------|
+| alt               | 图像描述                                | string            | - | |
+| className         | 自定义样式类名                           | string            | - | |
+| crossOrigin       | 透传给原生 img 标签的 crossorigin         | 'anonymous'|'use-credentials'| - | |
+| fallback          | 加载失败容错地址或者自定义加载失败时的显示内容 | ReactNode  | - | |
+| height            | 图片显示高度                             | number            | - | |
+| onError           | 加载错误回调                              | (event: Event) => void | - | |
+| onLoad            | 加载成功回调                              | (event: Event) => void | - | |
+| placeholder       | 图片未加载时候的占位内容                   | ReactNode         | - | |
+| preview           | 预览参数,为 false 时候禁用预览            | boolean \| ImagePreview | - | |
+| src               | 图片获取地址                             | string            | - | |
+| style             | 自定义样式                              | CSSProperties     | - | |
+| width             | 图片显示宽度                             | number            | - | |
+
+### ImagePreview
+
+| 属性               | 说明                    | 类型              | 默认值 | 版本 |
+|-------------------|-------------------------|------------------|-------|-----|
+| adaptiveTip       | 适应页面操作按钮提示       | string        | "适应页面" | |
+| className         | 自定义样式类名            | string           | - | |
+| closable          | 是否显示关闭按钮           | boolean        | true | |
+| closeOnEsc        | 点击 esc 关闭预览         | boolean        | true | |
+| currentIndex      | 受控属性,当前预览图片下标  | number               | - | |
+| defaultCurrentIndex | 首次展示图片下标        | number             | - | |
+| defaultVisible    | 首次是否开启预览           | boolean         | - | |
+| disableDownload   | 禁用下载                  | boolean        | false | |
+| downloadTip       | 下载操作按钮提示          | string         | "下载" | |
+| getPopupContainer | 指定父级 DOM,弹层将会渲染至该 DOM 中,自定义需要设置 container `position: relative` | () => HTMLElement  | () => document.body | |
+| infinite          | 是否无限循环              | boolean       | false | |
+| lazyLoad          | 是否开启懒加载             | boolean      | true | |
+| lazyLoadMargin    | 传给 options 中的rootMargin 参数,参考 [Intersection Observer API](https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API#interfaces) | string | "0px 100px 100px 0px" | |
+| maskClosable      | 点击遮罩是否可关闭         | boolean        | true | |
+| nextTip           | 下一步操作按钮提示         | string         | "下一步" | |
+| originTip         | 原始尺寸操作按钮提示       | string        | "原始尺寸" | |
+| onChange          | 切换图片触发的事件  | (index: number) => void | - | |
+| onClose           | 点击关闭按钮时的回调函数  | () => void | - | |
+| onDownload        | 图片下载回调函数     | (src: string, index: number) => void | - | |
+| onRotateLeft      | 旋转图片的回调      | (angle: number) => void | - | |
+| onNext            | 向后切换图片的回调   | (index: number) => void | - | |
+| onPrev            | 向前切换图片的回调   | (index: number) => void | - | |
+| onZoomIn          | 图片放大时的回调函数  | (zoom: number) => void | - | |
+| onZoomOut         | 图片缩小时的回调函数  | (zoom: number) => void | - | |
+| onVisibleChange   | 切换可见状态触发的回调   | (visible: boolean) => void | - | |
+| preLoad           | 是否开启预加载             | boolean        | true | |
+| preLoadGap        | 预加载的步长               | number         | 2 | |
+| previewTitle      | 自定义预览 title          | ReactNode      | - | |
+| prevTip           | 上一步操作按钮提示         | string         | "上一步" | |
+| renderHeader      | 自定义渲染预览顶部信息     | (info: ReactNode) => ReactNode  | - | |
+| renderPreviewMenu | 自定义渲染预览底部菜单信息  | (props: MenuProps) => ReactNode;| - | |
+| rotateTip         | 旋转操作按钮提示                    | string         | "旋转" | |
+| showTooltip       | 是否展示底部操作区提示      | boolean        | false | |
+| src               | 图片列表信息              | string \| string[] | - | |
+| style             | 自定义样式               | CSSProperties    | - | |
+| viewerVisibleDelay | 隐藏预览操作按钮前的无操作时长 | number         | 10000 | |
+| visible           | 受控属性,是否预览         | boolean         | - | |
+| zIndex            | 预览层层级                | number        | 1070 | |
+| zoomInTip         | 放大操作按钮提示           | string         | "放大" | |
+| zoomOutTip        | 缩小操作按钮提示           | string        | "缩小" | |
+| zoomStep          | 图片每次缩小/放大比例       | number        | 0.1 | |
+
+### MenuProps
+
+| 属性               | 说明                     | 类型    |
+|-------------------|--------------------------|--------|
+| curPage           | 当前图片页下标              | number |
+| disabledPrev      | 是否禁用向左切换按钮         | boolean |
+| disabledNext      | 是否禁用向右切换按钮         | boolean |
+| disableDownload   | 是否禁用下载按钮            | boolean |
+| max               | 图片缩放最大比例            | number |
+| min               | 图片缩放最小比例            | number |
+| onDownload        | 图片下载的调用函数           | () => void |
+| onZoomIn          | 图片放大时的调用函数         | () => void |
+| onZoomOut         | 图片缩小时的调用函数         | () => void |
+| onPrev            | 向前切换图片的调用函数       | () => void |
+| onNext            | 向后切换图片的调用函数       | () => void |
+| onRotateLeft      | 逆时针旋转图片的调用函数     | () => void |
+| onRotateRight     | 顺时针旋转图片的调用函数     | () => void |
+| ratio             | 原始尺寸或适应页面按钮状态  | "adaptation" \| "realSize"|
+| step              | 缩放的比例步长              | number |
+| totalNum          | 可预览的总图片数            | number |
+| zoom              | 当前图片缩放比例            | number |
+
+## 设计变量
+
+<DesignToken/>

+ 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

+ 224 - 218
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,50 +656,53 @@ 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

+ 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

+ 18 - 1
content/start/changelog/index-en-US.md

@@ -16,10 +16,27 @@ 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
-    - fixed the issue that the Select onblur event was not triggered when the panel was closed
+    - 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

+ 25 - 8
content/start/changelog/index.md

@@ -16,17 +16,34 @@ 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】
-    - 修复 Anchor 间距可触发选中后,键盘聚焦样式被遮挡问题
-    - 修复面板关闭时, Select onblur事件未被触发问题
-    - 修复锚点组件的子节点单行文本前面的空白处,点击不能选中的问题 [@edc-hui](https://github.com/edc-hui)
-    - 修复 Calendar weekStartsOn 改变后,事件渲染的位置没有跟着改变问题
-    - 修复在 DropDown 中使用 Input,Input onEnterPress 事件不生效问题
-    - 修复 tagGroup 污染传入 tagList 数据问题  [#1107 ](https://github.com/DouyinFE/semi-design/issues/1107)
-    - 修复 当 datepicker 的 type 为 dateTime 时,切换年月会导致时间部分被重置为8点  [@rojer95](https://github.com/rojer95)
+    - 修复当 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 依赖
+    - 移除 semi-foundation 、semi-ui中的 corejs 依赖 [#1095](https://github.com/DouyinFE/semi-design/issues/1095)
 
 #### 🎉 2.19.0-beta.0 (2022-09-05)
 - 【Feat】

+ 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 - 1
lerna.json

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

+ 4 - 4
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",
@@ -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",
@@ -187,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",

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation-react",
-  "version": "2.19.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",
+    "@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",
+  "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",
+  "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",
+  "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);
             /**

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

@@ -92,8 +92,8 @@ export interface MonthsGridFoundationProps extends MonthsGridElementProps {
     triggerRender?: (props: Record<string, any>) => any;
     insetInput: boolean;
     presetPosition?: PresetPosition;
-    renderQuickControls?: React.ReactNode;
-    renderDateInput?: React.ReactNode;
+    renderQuickControls?: any;
+    renderDateInput?: any;
 }
 
 export interface MonthInfo {
@@ -284,7 +284,7 @@ export default class MonthsGridFoundation extends BaseFoundation<MonthsGridAdapt
      * sync change another panel month when change months from the else yam panel
      * call it when
      *  - current change panel targe date month is same with another panel date
-     * 
+     *
      * @example
      *  - panelType=right, target=new Date('2022-09-01') and left panel is in '2022-09' => call it, left panel minus one month to '2022-08'
      *  - panelType=left, target=new Date('2021-12-01') and right panel is in '2021-12' => call it, right panel add one month to '2021-01'
@@ -310,7 +310,7 @@ export default class MonthsGridFoundation extends BaseFoundation<MonthsGridAdapt
         const { monthRight, monthLeft } = this._adapter.getStates();
         const currentDate = panelType === 'left' ? monthLeft.pickerDate : monthRight.pickerDate;
         let target: Date;
-        
+
         switch (switchType) {
             case 'prevMonth':
                 target = addMonths(currentDate, -1);
@@ -335,7 +335,7 @@ export default class MonthsGridFoundation extends BaseFoundation<MonthsGridAdapt
         const { type } = this._adapter.getProps();
         const diff = this._getDiff('month', target, panelType);
         this.handleYearOrMonthChange(diff < 0 ? 'prevMonth' : 'nextMonth', panelType, Math.abs(diff), false);
-    
+
         if (this.isRangeType(type)) {
             this.handleSyncChangeMonths({ panelType, target });
         }
@@ -936,7 +936,7 @@ export default class MonthsGridFoundation extends BaseFoundation<MonthsGridAdapt
 
     /**
      * Get year and month panel open type
-     * 
+     *
      * It is useful info to set minHeight of weeks.
      *  - When yam open type is 'left' or 'right', weeks minHeight should be set
      *    If the minHeight is not set, the change of the number of weeks will cause the scrollList to be unstable

+ 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

+ 2 - 2
packages/semi-foundation/datePicker/yearAndMonthFoundation.ts

@@ -14,8 +14,8 @@ export interface YearAndMonthFoundationProps {
     disabledDate?: (date: Date) => boolean;
     density?: string;
     presetPosition?: PresetPosition;
-    renderQuickControls?: React.ReactNode;
-    renderDateInput?: React.ReactNode;
+    renderQuickControls?: any;
+    renderDateInput?: any;
 }
 
 export interface YearAndMonthFoundationState {

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

+ 14 - 0
packages/semi-foundation/form/form.scss

@@ -66,6 +66,20 @@ $rating: #{$prefix}-rating;
                 }
             }
         }
+
+        .#{$field}-group {
+            .#{$col} {
+                &-right {
+                    display: flex;
+                    justify-content: flex-end;
+                }
+                &-left {
+                    display: flex;
+                    justify-content: flex-start;
+                }
+            }
+        }
+
         .#{$field}-pure {
             // padding-top: 0;
             // padding-bottom: 0;

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

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

@@ -0,0 +1,224 @@
+@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;
+        user-select: none;
+
+        &-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 {
+            user-select: none;
+            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;
+            user-select: none;
+        }
+
+        &-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 } = this.getStates();
+        const startMouseMove = this._adapter.getMouseMove();
+        const startMouseOffset = this._adapter.getMouseOffset();
+        const { canDragVertical, canDragHorizontal } = this.calcCanDragDirection();
+        if (startMouseMove && (canDragVertical || canDragHorizontal)) {
+            const { clientX, clientY } = e;
+            const { left: containerLeft, top: containerTop } = this._getContainerBounds();
+            const { left: extremeLeft, top: extremeTop } = this.calcExtremeBounds();
+            let newX = canDragHorizontal ? clientX - containerLeft - startMouseOffset.x : offset.x;
+            let newY = canDragVertical ? clientY - containerTop - 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);
+    };
+}

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

@@ -0,0 +1,264 @@
+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();
+        // 对鼠标移动做容错处理,当 x 和 y 方向在 mouseUp 的时候移动距离都小于等于 5px 时候就可以关闭预览
+        // Error-tolerant processing of mouse movement, when the movement distance in the x and y directions is less than or equal to 5px in mouseUp, the preview can be closed
+        // 不做容错处理的话,直接用 clientX !== x || y !== clientY 做判断,鼠标在用户点击时候无意识的轻微移动无法关闭预览,不符合用户预期
+        // If you do not do fault-tolerant processing, but directly use clientX !== x || y !== clientY to make judgments, the slight movement of the mouse when the user clicks will not be able to close the preview, which does not meet the user's expectations.
+        if (Math.abs(clientX - x) > 5 || Math.abs(y - clientY) > 5) {
+            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;
+            }
+        }
+
+    }
+}

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

@@ -0,0 +1,88 @@
+export const isTargetEmit = (event, targetClasses): boolean => {
+    // event.path usage is discouraged, use event.composedPath() as it's standard and is more future-proof
+    // path is the event-triggered bubbling path, which stores each node through which bubbling passes.
+    // path.length-4 is to remove elements above the root node, such as body, html, document, window
+    const path = event?.composedPath();
+    const isTarget = path?.slice(0, 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);

+ 2 - 9
packages/semi-foundation/navigation/foundation.ts

@@ -181,20 +181,13 @@ export default class NavigationFoundation<P = Record<string, any>, S = Record<st
         return [...willOpenKeys];
     }
 
-    getItemKey(item: string | number, keyPropName = 'itemKey') {
-        if (item && typeof item === 'object') {
-            return item[keyPropName];
-        }
-        return item;
-    }
-
-    getShouldOpenKeys(itemKeysMap: ItemKey2ParentKeysMap = {}, selectedKeys: string | number[] = []) {
+    getShouldOpenKeys(itemKeysMap: ItemKey2ParentKeysMap = {}, selectedKeys: string | number[]= []) {
         const willOpenKeySet = new Set();
 
         if (Array.isArray(selectedKeys) && selectedKeys.length) {
             selectedKeys.forEach(item => {
                 if (item) {
-                    const parentKeys = get(itemKeysMap, this.getItemKey(item));
+                    const parentKeys = get(itemKeysMap, item);
 
                     if (Array.isArray(parentKeys)) {
                         parentKeys.forEach(k => willOpenKeySet.add(k));

+ 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",
+    "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;
                 }
             }
         }

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini