DaiQiangReal 1 год назад
Родитель
Сommit
980e477eb2
71 измененных файлов с 1535 добавлено и 564 удалено
  1. 2 1
      content/input/form/index-en-US.md
  2. 2 1
      content/input/form/index.md
  3. 53 2
      content/input/select/index-en-US.md
  4. 57 4
      content/input/select/index.md
  5. 1 0
      content/input/treeselect/index-en-US.md
  6. 1 0
      content/input/treeselect/index.md
  7. 15 14
      content/navigation/breadcrumb/index-en-US.md
  8. 15 14
      content/navigation/breadcrumb/index.md
  9. 216 97
      content/navigation/tabs/index-en-US.md
  10. 221 106
      content/navigation/tabs/index.md
  11. 1 0
      content/navigation/tree/index-en-US.md
  12. 1 0
      content/navigation/tree/index.md
  13. 6 5
      content/show/overflowlist/index-en-US.md
  14. 14 12
      content/show/overflowlist/index.md
  15. 20 0
      content/start/changelog/index-en-US.md
  16. 19 0
      content/start/changelog/index.md
  17. 7 0
      cypress/e2e/tabs.spec.js
  18. 1 1
      lerna.json
  19. 3 3
      packages/semi-animation-react/package.json
  20. 1 1
      packages/semi-animation-styled/package.json
  21. 1 1
      packages/semi-animation/package.json
  22. 1 1
      packages/semi-eslint-plugin/package.json
  23. 40 3
      packages/semi-foundation/form/foundation.ts
  24. 9 1
      packages/semi-foundation/form/interface.ts
  25. 2 2
      packages/semi-foundation/package.json
  26. 2 0
      packages/semi-foundation/select/constants.ts
  27. 14 4
      packages/semi-foundation/select/foundation.ts
  28. 10 1
      packages/semi-foundation/select/select.scss
  29. 7 0
      packages/semi-foundation/select/variables.scss
  30. 1 0
      packages/semi-foundation/table/constants.ts
  31. 25 0
      packages/semi-foundation/table/utils.ts
  32. 16 2
      packages/semi-foundation/tooltip/foundation.ts
  33. 2 2
      packages/semi-foundation/tree/foundation.ts
  34. 2 2
      packages/semi-foundation/treeSelect/foundation.ts
  35. 3 1
      packages/semi-foundation/utils/Event.ts
  36. 1 1
      packages/semi-icons-lab/package.json
  37. 1 1
      packages/semi-icons/package.json
  38. 1 1
      packages/semi-illustrations/package.json
  39. 2 2
      packages/semi-next/package.json
  40. 0 1
      packages/semi-next/tsconfig.json
  41. 1 1
      packages/semi-rspack/package.json
  42. 1 1
      packages/semi-scss-compile/package.json
  43. 1 1
      packages/semi-theme-default/package.json
  44. 1 1
      packages/semi-ui/_base/baseComponent.tsx
  45. 6 4
      packages/semi-ui/breadcrumb/index.tsx
  46. 1 3
      packages/semi-ui/breadcrumb/item.tsx
  47. 90 0
      packages/semi-ui/form/_story/FormApi/scrollToError.jsx
  48. 4 0
      packages/semi-ui/form/_story/form.stories.jsx
  49. 7 0
      packages/semi-ui/form/baseForm.tsx
  50. 27 9
      packages/semi-ui/overflowList/index.tsx
  51. 7 7
      packages/semi-ui/package.json
  52. 1 0
      packages/semi-ui/pagination/index.tsx
  53. 31 1
      packages/semi-ui/select/_story/select.stories.jsx
  54. 72 11
      packages/semi-ui/select/index.tsx
  55. 3 2
      packages/semi-ui/table/TableCell.tsx
  56. 1 0
      packages/semi-ui/table/_story/virtualizedFixed/index.jsx
  57. 60 38
      packages/semi-ui/tabs/TabBar.tsx
  58. 21 1
      packages/semi-ui/tabs/_story/tabs.stories.jsx
  59. 16 1
      packages/semi-ui/tabs/index.tsx
  60. 15 3
      packages/semi-ui/tabs/interface.ts
  61. 8 0
      packages/semi-ui/tooltip/index.tsx
  62. 21 0
      packages/semi-ui/tree/__test__/treeMultiple.test.js
  63. 22 0
      packages/semi-ui/tree/_story/tree.stories.jsx
  64. 2 0
      packages/semi-ui/tree/index.tsx
  65. 2 1
      packages/semi-ui/tree/interface.ts
  66. 24 0
      packages/semi-ui/treeSelect/__test__/treeMultiple.test.js
  67. 22 0
      packages/semi-ui/treeSelect/_story/treeSelect.stories.jsx
  68. 26 38
      packages/semi-ui/treeSelect/index.tsx
  69. 1 1
      packages/semi-webpack/package.json
  70. 153 153
      sitemap.xml
  71. 92 0
      yarn.lock

+ 2 - 1
content/input/form/index-en-US.md

@@ -2123,7 +2123,8 @@ The table below describes the features available in the formApi.
 | setError      | Modify the error information of a field                                                                                                                                                                                                                                                                                            | formApi.setError(field: string, fieldErrorMessage: string)                                                                    |
 | getError      | Get Error Status of Field                                                                                                                                                                                                                                                                                                          | formApi.getError(field: string)                                                                                               |
 | getFieldExist | Get whether the field exists in the Form                                                                                                                                                                                                                                                                                           | formApi.getFieldExist(field: string)                                                                                          |
-| scrollToField | Scroll to field                                                                                                                                                                                                                                                                                                                    | formApi.scrollToField(field: string, scrollOpts: [object](<(https://github.com/stipsan/scroll-into-view-if-needed#options)>)) |
+| scrollToField | Scroll to the specified field, the second input parameter will be passed to scroll-into-view-if-needed | formApi.scrollToField(field: string, scrollOpts: [ScrollIntoViewOptions](https://github.com/stipsan/scroll-into-view-if-needed#options))                                                            |
+| scrollToError | Scroll to the field with validation error. You can pass a specified field or index. If you pass index, scroll to the index-th error DOM. If you do not pass any parameters, scroll to the first validation error position in the DOM tree. Available after v2.61.0  | formApi.scrollToError(<ApiType detail='{field?: string; index?: number; scrollOpts?: ScrollIntoViewOptions }'>ScrollToErrorOptions</ApiType>) 
 
 ### How to access formApi
 

+ 2 - 1
content/input/form/index.md

@@ -2121,7 +2121,8 @@ FormState 存储了所有 Form 内部的状态值,包括各表单控件的值
 | setError      | 修改 某个 field 的 error 信息                                                                                                                                                                                                    | formApi.setError(field: string, fieldErrorMessage: string)                                                          |
 | getError      | 获取 Field 的 error 状态                                                                                                                                                                                                         | formApi.getError(field: string)                                                                                     |
 | getFieldExist | 获取 Form 中是否存在对应的 field                                                                                                                                                                                                 | formApi.getFieldExist(field: string)                                                                                |
-| scrollToField | 滚动至指定的 field                                                                                                                                                                                                                   | formApi.scrollToField(field: string, scrollOpts: object)                                                            |
+| scrollToField | 滚动至指定的 field, 第二个入参将透传至scroll-into-view-if-needed | formApi.scrollToField(field: string, scrollOpts: [ScrollIntoViewOptions](https://github.com/stipsan/scroll-into-view-if-needed#options))                                                            |
+| scrollToError | 滚动至校验错误的field,可传指定 field 或者 index,传入 index 则滚动到第 index 个错误的 DOM,若不传参则滚动到DOM树中第一个校验出错的位置。 v2.61.0后提供  | formApi.scrollToError(<ApiType detail='{field?: string; index?: number; scrollOpts?: ScrollIntoViewOptions }'>ScrollToErrorOptions</ApiType>)                                                            |
 ### 如何获取 formApi
 
 -   Form 组件在 ComponentDidMount 阶段,会执行 props 传入的 getFormApi 回调,你可以在回调函数中保存 formApi 的引用,以便后续进行调用(**示例如下代码**)  

+ 53 - 2
content/input/select/index-en-US.md

@@ -616,6 +616,52 @@ import { Select } from '@douyinfe/semi-ui';
 );
 ```
 
+
+### Search position
+The default search input is displayed on the Select Trigger. You can specify different positions through `searchPosition`, and you can choose `dropdown` or `trigger`. Available after `v2.61.0`  
+If you want to customize the placeholder of the Input search box in the dropdown, you can control it through `searchPlaceholder`  
+If the `searchPosition` is `trigger`, when `showClear=true`, clicking the clear button of the input will clear the selected items and the search text in the input at the same time   
+If the `searchPosition` is `dropdown`, when `showClear=true`, clicking the clear button of the trigger will clear the selected items,  clicking the clear button in the dropdown input will clear search text  
+
+
+```jsx live=true
+import React from 'react';
+import { Select } from '@douyinfe/semi-ui';
+
+() => (
+    <>
+        <Select
+            filter
+            searchPosition='dropdown'
+            style={{ width: 200 }}
+            defaultValue={'ulikecam'}
+        >
+            <Select.Option value="douyin">Douyin</Select.Option>
+            <Select.Option value="ulikecam">UlikeCam</Select.Option>
+            <Select.Option value="jianying">Capcut</Select.Option>
+            <Select.Option value="xigua">XiguaVideo</Select.Option>
+        </Select>
+        <br />
+        <br />
+        <Select
+            filter
+            searchPosition='dropdown'
+            multiple
+            style={{ width: 300 }}
+            defaultValue={['semi-1']}
+            autoClearSearchValue={false}
+        >
+            <Select.Option value="semi-0">Semi-0</Select.Option>
+            <Select.Option value="semi-1">Semi-1</Select.Option>
+            <Select.Option value="semi-2">Semi-2</Select.Option>
+            <Select.Option value="semi-3">Semi-3</Select.Option>
+            <Select.Option value="semi-4">Semi-4</Select.Option>
+        </Select>
+    </>
+);
+
+```
+
 ### Remote search
 
 A multi-select example with remote search, request debounce, loading status.
@@ -1168,6 +1214,8 @@ import { IconAppCenter, IconChevronDown } from '@douyinfe/semi-icons';
                 value={valList}
                 triggerRender={triggerRender}
                 optionList={list}
+                filter
+                searchPosition='dropdown'
                 onChange={value => setValList(value)}
                 multiple
                 style={{ width: 240 }}
@@ -1180,6 +1228,8 @@ import { IconAppCenter, IconChevronDown } from '@douyinfe/semi-icons';
                 onChange={value => setVal(value)}
                 triggerRender={triggerRender2}
                 optionList={list}
+                filter
+                searchPosition='dropdown'
                 style={{ width: 240, marginTop: 20, outline: 0 }}
             ></Select>
         </div>
@@ -1363,10 +1413,11 @@ import { Select, Checkbox } from '@douyinfe/semi-ui';
 | renderCreateItem | When allowCreate is true, you can customize the rendering of the creation label                                                                                                                                                                                                                                                                                    | function(inputValue: string) | InputValue => 'Create' + InputValue |
 | renderSelectedItem | Customize the rendering of selected tabs in the selection box                                                                                                                                                                                                                                                                                                      | function(option) |  |
 | restTagsPopoverProps | The configuration properties of the [Popover](/en-US/show/popover#API%20Reference)                                                                                                                                                                                                                                                                                 | PopoverProps | {} | 2.22.0 |
+| size | Size, optional value `default` / `small` / `large`   
+| searchPosition | When the filter is turned on, the search box is in the trigger by default. You can set it to 'dropdown' to put the search box at the top of the popup list.  | string | 'trigger' | 2.61.0
 | showArrow | Whether to show arrow icon                                                                                                                                                                                                                                                                                                                                         | boolean | true |
 | showClear | Whether to show the clear button                                                                                                                                                                                                                                                                                                                                   | boolean | false |
 | showRestTagsPopover | When the number of tags exceeds maxTagCount and hover reaches +N, whether to display the remaining content through Popover                                                                                                                                                                                                                                         | boolean | false | 2.22.0 |
-| size | Size, optional value `default` / `small` / `large`                                                                                                                                                                                                                                                                                                                 | string | 'default' |
 | spacing | Spacing between popup layer and trigger                                                                                                                                                                                                                                                                                                                            | number | 4 |
 | stopPropagation | Whether to prevent click events on the popup layer from bubbling                                                                                                                                                                                                                                                                                                   | boolean | true |  |
 | style | Inline Style                                                                                                                                                                                                                                                                                                                                                       | object |  |
@@ -1472,7 +1523,7 @@ Some internal methods provided by Select can be accessed through ref:
 <DesignToken/>
 
 ## Related Material
-<semi-material-list code="3, 4, 58, 62"></semi-material-list>
+<semi-material-list code="3, 4, 58, 62, 696"></semi-material-list>
 
 ## FAQ
 

+ 57 - 4
content/input/select/index.md

@@ -699,11 +699,58 @@ import { Select } from '@douyinfe/semi-ui';
 );
 ```
 
+### 搜索框位置
+默认搜索框展示于 Select 的 Trigger 触发器上。通过 `searchPosition` 可以指定不同的位置,可选 `dropdown`、`trigger`。 在 v2.61.0后提供
+若希望定制位于 dropdown 中的 Input 搜索框的 placeholder,可以通过 `searchPlaceholder` 控制  
+若 `searchPosition` 值为 `trigger`,当showClear=true 时,点击Trigger区域的清空按钮,将同时清空已选项以及搜索框中的文本  
+若 `searchPosition` 值为 `dropdown`,当showClear=true 时,点击Trigger区域清空按钮,仅清空已选项。点击搜索框中的清空按钮,仅清空搜索文本  
+
+```jsx live=true
+import React from 'react';
+import { Select } from '@douyinfe/semi-ui';
+
+() => (
+    <>
+        <Select
+            filter
+            searchPosition='dropdown'
+            style={{ width: 200 }}
+            defaultValue={'ulikecam'}
+            placeholder='我的搜索框在下拉菜单中'
+            searchPlaceholder="带搜索功能的单选"
+        >
+            <Select.Option value="douyin">抖音</Select.Option>
+            <Select.Option value="ulikecam">轻颜相机</Select.Option>
+            <Select.Option value="jianying">剪映</Select.Option>
+            <Select.Option value="xigua">西瓜视频</Select.Option>
+        </Select>
+        <br />
+        <br />
+        <Select
+            filter
+            searchPosition='dropdown'
+            multiple
+            style={{ width: 300 }}
+            defaultValue={['semi-1']}
+            placeholder='我的搜索框在下拉菜单中'
+            searchPlaceholder="带搜索功能的多选"
+            autoClearSearchValue={false}
+        >
+            <Select.Option value="semi-0">Semi-0</Select.Option>
+            <Select.Option value="semi-1">Semi-1</Select.Option>
+            <Select.Option value="semi-2">Semi-2</Select.Option>
+            <Select.Option value="semi-3">Semi-3</Select.Option>
+            <Select.Option value="semi-4">Semi-4</Select.Option>
+        </Select>
+    </>
+);
+```
+
 ### 远程搜索
 
 带有远程搜索,防抖请求,加载状态的多选示例  
 通过`filter`开启搜索能力  
-将`remote`设置为 true 关闭对当前数据的筛选过滤(在 v0.24.0 后提供)  
+将`remote`设置为 true 关闭对当前数据的筛选过滤
 通过动态更新`optionList`更新下拉菜单中的备选项  
 使用受控的 value 属性
 
@@ -1121,7 +1168,8 @@ class VirtualizeDemo extends React.Component {
 
 ### 自定义触发器
 
-如果 Select 默认的触发器样式满足不了你的需求,可以用`triggerRender`自定义选择框的展示
+如果 Select 默认的触发器样式满足不了你的需求,可以用`triggerRender`自定义选择框的展示  
+如果想保留搜索筛选能力,又不希望自己渲染 Input 相关的结构,可以同时通过 searchPosition='dropdown',将默认的搜索框置于下拉列表中
 
 triggerRender 入参如下
 
@@ -1230,6 +1278,8 @@ import { IconAppCenter, IconChevronDown } from '@douyinfe/semi-icons';
                 optionList={list}
                 onChange={value => setValList(value)}
                 multiple
+                filter
+                searchPosition='dropdown'
                 style={{ width: 240 }}
             ></Select>
             <br />
@@ -1240,6 +1290,8 @@ import { IconAppCenter, IconChevronDown } from '@douyinfe/semi-icons';
                 onChange={value => setVal(value)}
                 triggerRender={triggerRender2}
                 optionList={list}
+                filter
+                searchPosition='dropdown'
                 style={{ width: 240, marginTop: 20, outline: 0 }}
             ></Select>
         </div>
@@ -1394,7 +1446,7 @@ import { Select, Checkbox, Highlight } from '@douyinfe/semi-ui';
 | autoFocus | 初始渲染时是否自动 focus                                                                                                                       | boolean | false |
 | borderless        | 无边框模式  >=2.33.0                                                                                                                       | boolean                         |           |
 | className | 类名                                                                                                                                    | string |  |
-| clearIcon | 可用于自定义清除按钮, showClear为true时有效                                                                                                         | ReactNode | 2.25.0  |
+| clearIcon | 可用于自定义清除按钮, showClear为true时有效                                                                                                         | ReactNode |   | 2.25.0
 | clickToHide | 已展开时,点击选择框是否自动收起下拉列表                                                                                                                  | boolean | false |
 | defaultValue | 初始选中的值                                                                                                                                | string\|number\|array |  |
 | defaultOpen | 是否默认展开下拉列表                                                                                                                            | boolean | false |
@@ -1430,6 +1482,7 @@ import { Select, Checkbox, Highlight } from '@douyinfe/semi-ui';
 | renderOptionItem | 通过 renderOptionItem 完全自定义下拉列表中候选项的渲染                                                                                                  | function(props) 入参详见 Demo |  |
 | restTagsPopoverProps | Popover 的配置属性,可以控制 position、zIndex、trigger 等,具体参考[Popover](/zh-CN/show/popover#API%20%E5%8F%82%E8%80%83)                              | PopoverProps | {} | 2.22.0 |
 | remote | 是否开启远程搜索,当 remote 为 true 时,input 内容改变后不会进行本地筛选匹配                                                                                      | boolean | false |
+| searchPosition | filter开启时,搜索框的位置,默认在 trigger中,可以通过设为 'dropdown' 将搜索框置于下拉列表顶部。搭配 triggerRender 使用可以实现更高自由度的交互   | string | 'trigger' | 2.61.0
 | size | 大小,可选值 `default`/`small`/`large`                                                                                                      | string | 'default' |
 | style | 样式                                                                                                                                    | object |  |
 | stopPropagation | 是否阻止浮层上的点击事件冒泡                                                                                                                        | boolean | true |  |
@@ -1603,4 +1656,4 @@ import { Select, Checkbox, Highlight } from '@douyinfe/semi-ui';
 3,4,44,54,58,62,72
 ``` -->
 ## 相关物料
-<semi-material-list code="3, 4, 58, 62"></semi-material-list>
+<semi-material-list code="3, 4, 58, 62, 696"></semi-material-list>

+ 1 - 0
content/input/treeselect/index-en-US.md

@@ -1412,6 +1412,7 @@ function Demo() {
 | arrowIcon|Customize the right drop-down arrow Icon, when the showClear switch is turned on and there is currently a selected value, hover will give priority to the clear icon| ReactNode | | 1.15.0|
 |autoAdjustOverflow|Whether the pop-up layer automatically adjusts the direction when it is obscured (only vertical direction is supported for the time being, and the inserted parent is body)|boolean | true| 0.34.0|
 | autoExpandParent | Toggle whether to expand parent nodes automatically | boolean | false | 0.34.0 |
+| autoMergeValue | Sets the automerge value. Specifically, when enabled, when a parent node is selected, value will include that node and its children. (Works if leafOnly is false)| boolean | true | 2.61.0 | 
 | borderless        | borderless mode  >=2.33.0                                                                                                                                                                     | boolean                         |           |
 | checkRelation | In multiple, the relationship between the checked states of the nodes, optional: 'related'、'unRelated' | string | 'related' | 2.5.0 |
 | className                | Class name                                                                          | string                                                            | -           | -       |

+ 1 - 0
content/input/treeselect/index.md

@@ -1395,6 +1395,7 @@ function Demo() {
 | arrowIcon| 自定义右侧下拉箭头Icon,当showClear开关打开且当前有选中值时,hover会优先显示clear icon                                                                                  | ReactNode | | 1.15.0|
 | autoAdjustOverflow| 浮层被遮挡时是否自动调整方向(暂时仅支持竖直方向,且插入的父级为 body)                                                                                                     |boolean | true| 0.34.0|
 | autoExpandParent | 是否自动展开父节点                                                                                                                                  | boolean | false | 0.34.0 |
+| autoMergeValue | 设置自动合并 value。具体而言是,开启后,当某个父节点被选中时,value 将包括该节点以及该子孙节点。(在leafOnly为false的情况下生效)| boolean | true | 2.61.0 | 
 | borderless        | 无边框模式  >=2.33.0                                                                                                                            | boolean                         |           |
 | checkRelation | 多选时,节点之间选中状态的关系,可选:'related'、'unRelated'                                                                                                   | string | 'related' | 2.5.0 |
 | className | 选择框的 `className` 属性                                                                                                                        | string | - | - |

+ 15 - 14
content/navigation/breadcrumb/index-en-US.md

@@ -297,20 +297,21 @@ import { IconHome, IconArticle } from '@douyinfe/semi-icons';
 
 ### Breadcrumb
 
-| Properties | Instructions                                                                                      | type                                         | Default   | version |
-| ---------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------- | --------- | ------- |
-| autoCollapse      | Toggle whether to auto collapse when exceed maxItemCount                                                                                     | boolean                                     | true     |    1.9.0   |
-| className  | Class name                                                                                        | string                                       | -         |         |
-| compact    | Compact sizing                                                                                    | boolean                                      | true      |         |
-| maxItemCount      | Set the number of item when reached to collapse                                                                                      | number                                     | 4    | 1.9.0       |
-|moreType|...area rendering type,one of 'default'、'popover'|string|'default'|1.27.0|
-| renderItem | Custom function, used with routes                                                                 | (Route: [Route](#Route)) => React Node               | -         | 0.27.0  |
-|renderMore|Custom ... area rendering|(restItem: ReactNode[]) => ReactNode|-|1.27.0|
-| routes     | Routing information, an array of route objects or strings, format reference: [Route](#Route) | Array<[Route](#Route) \| string\>                              | -         |         |
-| separator  | Customized delimiter                                                                              | string                                       | ReactNode | '/'     |  |
-| showTooltip | Toggle whether to show tooltip if text overflowed. If passed in as an object: width, overflowed width; ellipsisPos, ways of truncation;  opts, passed directly to Tooltip component                              | boolean \| showToolTipProps             | {width: 150, ellipsisPos: 'end', opts: { autoAdjustOverflow: true, position: "bottomLeft" }}      | 0.34.0 |
-| style      | Inline style                                                                                      | CSSProperties                                       | -         |         |
-| onClick    | Click event                                                                                       |  (item: [Route](#Route), e: Event) => void| -         | 0.27.0  |
+| Properties | Instructions                                                                                                                                                                        | type                                         | Default   | version |
+| ---------- |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -------------------------------------------- | --------- | ------- |
+| activeIndex| Controlled use, currently selected navigation index                                                                                                                                 | - | 2.61.0 |
+| autoCollapse      | Toggle whether to auto collapse when exceed maxItemCount                                                                                                                            | boolean                                     | true     |    1.9.0   |
+| className  | Class name                                                                                                                                                                          | string                                       | -         |         |
+| compact    | Compact sizing                                                                                                                                                                      | boolean                                      | true      |         |
+| maxItemCount      | Set the number of item when reached to collapse                                                                                                                                     | number                                     | 4    | 1.9.0       |
+|moreType| ...area rendering type,one of 'default'、'popover'                                                                                                                                   |string|'default'|1.27.0|
+| renderItem | Custom function, used with routes                                                                                                                                                   | (Route: [Route](#Route)) => React Node               | -         | 0.27.0  |
+|renderMore| Custom ... area rendering                                                                                                                                                           |(restItem: ReactNode[]) => ReactNode|-|1.27.0|
+| routes     | Routing information, an array of route objects or strings, format reference: [Route](#Route)                                                                                        | Array<[Route](#Route) \| string\>                              | -         |         |
+| separator  | Customized delimiter                                                                                                                                                                | string                                       | ReactNode | '/'     |  |
+| showTooltip | Toggle whether to show tooltip if text overflowed. If passed in as an object: width, overflowed width; ellipsisPos, ways of truncation;  opts, passed directly to Tooltip component | boolean \| showToolTipProps             | {width: 150, ellipsisPos: 'end', opts: { autoAdjustOverflow: true, position: "bottomLeft" }}      | 0.34.0 |
+| style      | Inline style                                                                                                                                                                        | CSSProperties                                       | -         |         |
+| onClick    | Click event                                                                                                                                                                         |  (item: [Route](#Route), e: Event) => void| -         | 0.27.0  |
 
 ### Breadcrumb.Item
 

+ 15 - 14
content/navigation/breadcrumb/index.md

@@ -300,20 +300,21 @@ import { IconHome, IconArticle } from '@douyinfe/semi-icons';
 
 ### Breadcrumb
 
-| 属性       | 说明                                                                                      | 类型                                       | 默认值 | 版本   |
-| ---------- | ----------------------------------------------------------------------------------------- | ------------------------------------------ | ------ | ------ |
-| autoCollapse      | 是否超出maxItemCount后自动折叠                                                                                     | boolean                                     | true     |    1.9.0   |
-| className  | 类名                                                                                      | string                                     | -      |        |
-| compact    | 显示尺寸,是否紧凑                                                                         | boolean                                    | true   |        |
-| maxItemCount      | 超出多少个进行自动折叠                                                                                      | number                                     | 4    | 1.9.0       |
-|moreType|内置的...区域的渲染类型,可选值为 'default'、'popover'|string|'default'|1.27.0|
-| renderItem | 自定义链接函数,配合 routes 使用                                                          | (Route: [Route](#Route)) => ReactNode             | -      | 0.27.0 |
-| renderMore|自定义...区域的渲染|(restItem: ReactNode[]) => ReactNode|-|1.27.0|
-| routes     | router 的路由信息,由路由对象或字符串组成的数组,路由对象格式参考: [Route](#Route) | Array<[Route](#Route) \| string\>                            | -      |        |
-| separator  | 自定义的分隔符                                                                            | ReactNode                          | '/'    |        |
-| showTooltip | 是否展示 Tooltip 及相关配置: width,溢出宽度;   ellipsisPos,截断方式,从中间/末尾截断;                         opts,透传给Tooltip的属性                              | boolean \| showToolTipProps             | {width: 150, ellipsisPos: 'end', opts: { autoAdjustOverflow: true, position: "bottomLeft" }}      | 0.34.0 |
-| style      | 内联样式                                                                                      | CSSProperties                                     | -      |        |
-| onClick    | 单击事件                                                                                  | (item: [Route](#Route) , e: Event) => void | -      | 0.27.0 |
+| 属性       | 说明                                                                                                      | 类型                                       | 默认值 | 版本   |
+| ---------- |---------------------------------------------------------------------------------------------------------| ------------------------------------------ | ------ | ------ |
+| activeIndex| 受控使用,当前选择的导航序号                                                                                          | - | 2.61.0 |
+| autoCollapse      | 是否超出maxItemCount后自动折叠                                                                                   | boolean                                     | true     |    1.9.0   |
+| className  | 类名                                                                                                      | string                                     | -      |        |
+| compact    | 显示尺寸,是否紧凑                                                                                               | boolean                                    | true   |        |
+| maxItemCount      | 超出多少个进行自动折叠                                                                                             | number                                     | 4    | 1.9.0       |
+|moreType| 内置的...区域的渲染类型,可选值为 'default'、'popover'                                                                  |string|'default'|1.27.0|
+| renderItem | 自定义链接函数,配合 routes 使用                                                                                    | (Route: [Route](#Route)) => ReactNode             | -      | 0.27.0 |
+| renderMore| 自定义...区域的渲染                                                                                             |(restItem: ReactNode[]) => ReactNode|-|1.27.0|
+| routes     | router 的路由信息,由路由对象或字符串组成的数组,路由对象格式参考: [Route](#Route)                                                   | Array<[Route](#Route) \| string\>                            | -      |        |
+| separator  | 自定义的分隔符                                                                                                 | ReactNode                          | '/'    |        |
+| showTooltip | 是否展示 Tooltip 及相关配置: width,溢出宽度;   ellipsisPos,截断方式,从中间/末尾截断;                         opts,透传给Tooltip的属性 | boolean \| showToolTipProps             | {width: 150, ellipsisPos: 'end', opts: { autoAdjustOverflow: true, position: "bottomLeft" }}      | 0.34.0 |
+| style      | 内联样式                                                                                                    | CSSProperties                                     | -      |        |
+| onClick    | 单击事件                                                                                                    | (item: [Route](#Route) , e: Event) => void | -      | 0.27.0 |
 
 ### Breadcrumb.Item
 

+ 216 - 97
content/navigation/tabs/index-en-US.md

@@ -21,15 +21,15 @@ import { Tabs, TabPane } from '@douyinfe/semi-ui';
 Tbs supports three types of styles: `line`, `button`, and `card`. By default, the first tab is selected.
 
 Tabs supports two declare ways, and the rendering process of the two is different:
-- Pass the array of objects through `tabList`, when using `tabList`, only render the currently passed node each time
-- Or use `<TabPane>` to explicitly pass in item by item. When using `<TabPane>`, all panels will be rendered by default. You can set `keepDOM={false}` to only render the current panel, and there will be no animation effect at this time .
+
+-   Pass the array of objects through `tabList`, when using `tabList`, only render the currently passed node each time
+-   Or use `<TabPane>` to explicitly pass in item by item. When using `<TabPane>`, all panels will be rendered by default. You can set `keepDOM={false}` to only render the current panel, and there will be no animation effect at this time .
 
 <Notice title='Notice'>
     1. When tabList and TabPane Children are used at the same time, the data passed in through tabList will be rendered first. It is not recommended to configure both <br/>
     2. When using TabPane Children, TabPane must be a direct child element of Tabs, otherwise Tabs will not be able to correctly collect related attributes such as itemKey and other subcomponents
 </Notice>
 
-
 ```jsx live=true
 import React from 'react';
 import { Tabs, TabPane } from '@douyinfe/semi-ui';
@@ -80,9 +80,7 @@ class App extends React.Component {
                                 backgroundColor: 'var(--semi-color-fill-0)',
                             }}
                         >
-                            <code>
-                                yarn add @douyinfe/semi-ui
-                            </code>
+                            <code>yarn add @douyinfe/semi-ui</code>
                         </pre>
                     </TabPane>
                     <TabPane tab="Help" itemKey="3">
@@ -220,50 +218,57 @@ class App extends React.Component {
 }
 ```
 
-
-### More with Dropdown 
-
+### More with Dropdown
 
 Supports merging redundant tabs into a `more` drop-down menu. Just pass in a number for `more`. The number represents the number of tabs included in the drop-down menu. **(>=v2.59.0)**
 
-
 ```jsx live=true dir=column
 import React from 'react';
 import { Tabs, TabPane } from '@douyinfe/semi-ui';
 
-function Demo(){
-  return (
-          <Tabs more={4} style={{ width: '60%', margin: '20px' }} type="card">
+function Demo() {
+    return (
+        <Tabs more={4} style={{ width: '60%', margin: '20px' }} type="card">
             {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
-                    <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
-                      Content of card tab {i}
-                    </TabPane>
+                <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                    Content of card tab {i}
+                </TabPane>
             ))}
-          </Tabs>
-  );
+        </Tabs>
+    );
 }
 ```
 
 Advanced configuration is also supported, passing the object to `more`, and it can be passed in
 
-- `count`: Represents the number of Tabs in the income drop-down menu
-- `render`: Customize the rendering function of Trigger. The returned ReactNode will be rendered as the Trigger of the drop-down menu.
-- `dropdownProps`: Incoming DropDown Props will be transparently transmitted to the drop-down menu. If you need to customize the drop-down menu, use the render method in dropdownProps
+-   `count`: Represents the number of Tabs in the income drop-down menu
+-   `render`: Customize the rendering function of Trigger. The returned ReactNode will be rendered as the Trigger of the drop-down menu.
+-   `dropdownProps`: Incoming DropDown Props will be transparently transmitted to the drop-down menu. If you need to customize the drop-down menu, use the render method in dropdownProps
 
 ```jsx live=true dir=column
 import React from 'react';
-import { Tabs, TabPane } from '@douyinfe/semi-ui';
+import { Tabs, TabPane, Button } from '@douyinfe/semi-ui';
 
-function Demo(){
-  return (
-          <Tabs more={{ count: 4, render: ()=>{return <div style={{ display: 'inline-block' }}>Click to show More</div>;}, dropdownProps: { trigger: "click" } }} style={{ width: '60%', margin: '20px' }} type="card">
+function Demo() {
+    return (
+        <Tabs
+            more={{
+                count: 4,
+                render: () => {
+                    return <Button type='tertiary'>Click to show More</Button>;
+                },
+                dropdownProps: { trigger: 'click', position: 'bottomRight' },
+            }}
+            style={{ width: '60%', margin: '20px' }}
+            type="card"
+        >
             {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
-                    <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
-                      Content of card tab {i}
-                    </TabPane>
+                <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                    Content of card tab {i}
+                </TabPane>
             ))}
-          </Tabs>
-  );
+        </Tabs>
+    );
 }
 ```
 
@@ -296,7 +301,7 @@ class App extends React.Component {
                 <RadioGroup
                     onChange={e => this.onSelect(e)}
                     value={this.state.type}
-                    type='button'
+                    type="button"
                     style={{
                         display: 'flex',
                         width: 200,
@@ -374,6 +379,112 @@ class App extends React.Component {
 }
 ```
 
+**Modify the scrolling rendering Arrow**
+
+`renderArrow` modifies the Arrow, with the input parameters being the overflowed items and position
+
+```jsx live=true dir="column"
+import React from 'react';
+import { Tabs, TabPane, Dropdown } from '@douyinfe/semi-ui';
+
+() => {
+    const [activeKey, setActiveKey] = useState('Tab-0');
+    const renderArrow = (items, pos, handleArrowClick) => {
+        const style = {
+            width: 32,
+            height: 32,
+            margin: '0 12px',
+            display: 'flex',
+            justifyContent: 'center',
+            alignItems: 'center',
+            borderRadius: '100%',
+            background: 'rgba(var(--semi-grey-1), 1)',
+            color: 'var(--semi-color-text)',
+            cursor: 'pointer',
+        };
+        return (
+            <Dropdown
+                render={
+                    <Dropdown.Menu>
+                        {items.map(item => {
+                            return (
+                                <Dropdown.Item onClick={() => setActiveKey(item.itemKey)}>{item.itemKey}</Dropdown.Item>
+                            );
+                        })}
+                    </Dropdown.Menu>
+                }
+            >
+                {pos === 'start' ? (
+                    <div style={style} onClick={handleArrowClick}>
+                        ←
+                    </div>
+                ) : (
+                    <div style={style} onClick={handleArrowClick}>
+                        →
+                    </div>
+                )}
+            </Dropdown>
+        );
+    };
+
+    return (
+        <Tabs
+            renderArrow={renderArrow}
+            style={{ width: '50%', margin: '20px' }}
+            activeKey={activeKey}
+            type="card"
+            collapsible
+            onChange={k => setActiveKey(k)}
+        >
+            {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
+                <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                    Content of card tab {i}
+                </TabPane>
+            ))}
+        </Tabs>
+    );
+};
+```
+
+**Modify Arrow rendering position**
+
+Use `arrowPosition` to modify the overflow indicator position, optional start both end
+
+```jsx live=true dir=column
+import React from 'react';
+import { Tabs, TabPane } from '@douyinfe/semi-ui';
+
+class App extends React.Component {
+    render() {
+        return (
+            <>
+                <Tabs style={{ width: '60%', margin: '20px' }} type="card" collapsible arrowPosition={'start'}>
+                    {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
+                        <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                            Content of card tab {i}
+                        </TabPane>
+                    ))}
+                </Tabs>
+                <Tabs style={{ width: '60%', margin: '20px' }} type="card" collapsible arrowPosition={'both'}>
+                    {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
+                        <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                            Content of card tab {i}
+                        </TabPane>
+                    ))}
+                </Tabs>
+                <Tabs style={{ width: '60%', margin: '20px' }} type="card" collapsible arrowPosition={'end'}>
+                    {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
+                        <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                            Content of card tab {i}
+                        </TabPane>
+                    ))}
+                </Tabs>
+            </>
+        );
+    }
+}
+```
+
 ### Disable
 
 Disable one tab.
@@ -544,10 +655,9 @@ class App extends React.Component {
 
 ### Closeable
 
-Close a tab in the tab bar.
-Only card style tabs support the close option. Use `closable={true}` to turn it on.
+Close a tab in the tab bar. Only card style tabs support the close option. Use `closable={true}` to turn it on.
 
-```jsx live=true 
+```jsx live=true
 import React from 'react';
 import { Tabs, TabPane } from '@douyinfe/semi-ui';
 
@@ -559,21 +669,23 @@ class App extends React.Component {
                 { tab: 'Doc', itemKey: '1', text: 'Doc', closable: true },
                 { tab: 'Quick Start', itemKey: '2', text: 'Quick Start', closable: true },
                 { tab: 'Help', itemKey: '3', text: 'Help' },
-            ]
+            ],
         };
     }
     close(key) {
         const newTabList = [...this.state.tabList];
-        const closeIndex = newTabList.findIndex(t=>t.itemKey===key);
+        const closeIndex = newTabList.findIndex(t => t.itemKey === key);
         newTabList.splice(closeIndex, 1);
         this.setState({ tabList: newTabList });
     }
     render() {
         return (
             <Tabs type="card" defaultActiveKey="1" onTabClose={this.close.bind(this)}>
-                {
-                    this.state.tabList.map(t=><TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey}>{t.text}</TabPane>)
-                }
+                {this.state.tabList.map(t => (
+                    <TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey}>
+                        {t.text}
+                    </TabPane>
+                ))}
             </Tabs>
         );
     }
@@ -584,77 +696,84 @@ class App extends React.Component {
 
 ### Tab
 
-Property | Description | Type | Default Value |
---- | --- | --- | --- |
-activeKey | The itemKey value of the currently active tab page | string | None |
-className | class name | string | None |
-collapsible | collapsed Tabs, **>=1.1.0** | boolean | false |
-contentStyle | The outer style object of the content area | CSSProperties | None |
-defaultActiveKey | Initialize the key value of the selected tab page | string | '1' |
-keepDOM | Whether to render the DOM structure of the hidden panel when using TabPane writing, **>=1.0.0** | boolean | true |
-lazyRender | Lazy rendering, only when the panel is activated will it be rendered in the DOM tree, **>=1.0.0** | boolean | false |
-more | Render a portion of the Tab into a drop-down menu ** >= 2.59.0** | number \| {count:number,render:()=>ReactNode,dropdownProps:DropDownProps} | -       |    
-renderTabBar | Used for secondary packaging tab bar | (tabBarProps: object, defaultTabBar: React.ComponentType) => ReactNode | None |
-preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user | boolean |  |  |
-size | Size, providing three types of `large`, `medium`, and `small`, **>=1.11.0, currently only supports linear Tabs** | string | `large` |
-style | style object | CSSProperties | None |
-tabBarExtraContent | Used to extend the content of the tab bar | ReactNode | None |
-tabList | An array of tab page objects that supports itemKey (corresponding to activeKey, tab (tab page text) and icon (tab page icon) | TabPane[] | None |
-tabPaneMotion | Whether to use animation to switch tabs | boolean | true |
-tabPosition | The position of the tab, support `top` (horizontal), `left` (vertical), **>=1.0.0** | string | `top` |
-type | The style of the label bar, optional `line`, `card`, `button` | string | `line` |
-onChange | Callback function when switching tab pages | function(activeKey: string) | None |
-onTabClick | Click event | function(key: string, e: Event) | None |
-onTabClose | executed when tab closed by user, **>=2.1.0**  |  function(tabKey: string) | None
+| Property | Description | Type | Default Value |
+| --- | --- | --- | --- |
+| activeKey | The itemKey value of the currently active tab page | string | None |
+| className | class name | string | None |
+| collapsible | collapsed Tabs, **>=1.1.0** | boolean | false |
+| visibleTabsStyle | Overall scrolling area style **>=2.61.0** | style: CSSProperties | None |
+| contentStyle | The outer style object of the content area | CSSProperties | None |
+| defaultActiveKey | Initialize the key value of the selected tab page | string | '1' |
+| keepDOM | Whether to render the DOM structure of the hidden panel when using TabPane writing, **>=1.0.0** | boolean | true |
+| lazyRender | Lazy rendering, only when the panel is activated will it be rendered in the DOM tree, **>=1.0.0** | boolean | false |
+| more | Render a portion of the Tab into a drop-down menu ** >= 2.59.0** | number \| {count:number,render:()=>ReactNode,dropdownProps:DropDownProps} | - |
+| renderTabBar | Used for secondary packaging tab bar | (tabBarProps: object, defaultTabBar: React.ComponentType) => ReactNode | None |
+| renderArrow | Customize how overflow items indicator are rendered externally. By default, the overflow items are expanded when the arrow button is hovered. **>=2.61.0** | (items: OverflowItem[],pos:"start"\|"end", handleArrowClick:()=>void)=> ReactNode | None |
+| preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user | boolean |
+| showRestInDropdown | Whether to display the collapsed Tab in the drop-down menu (only effective when collapsible is true) **>= 2.61.0** | boolean | true |
+| size | Size, providing three types of `large`, `medium`, and `small`, **>=1.11.0, currently only supports linear Tabs** | string | `large` |
+| style | style object | CSSProperties | None |
+| tabBarExtraContent | Used to extend the content of the tab bar | ReactNode | None |
+| tabList | An array of tab page objects that supports itemKey (corresponding to activeKey, tab (tab page text) and icon (tab page icon) | TabPane[] | None |
+| tabPaneMotion | Whether to use animation to switch tabs | boolean | true |
+| tabPosition | The position of the tab, support `top` (horizontal), `left` (vertical), **>=1.0.0** | string | `top` |
+| type | The style of the label bar, optional `line`, `card`, `button` | string | `line` |
+| onChange | Callback function when switching tab pages | function(activeKey: string) | None |
+| onTabClick | Click event | function(key: string, e: Event) | None |
+| onTabClose | executed when tab closed by user, **>=2.1.0** | function(tabKey: string) | None |
+| arrowPosition | Arrow rendering position **>=2.61.0** | "start" "end" "both" | 无 |
+| onVisibleTabsChange | Overflow item switching change callback **>=2.61.0** | (visibleState:Record\<string,bool\>)=>void | None |
 
 ### TabPane
 
-Property | Description | Type | Default Value |
---------- | ---------------- | ------------------ | ---- - |
-className | class name | string | None |
-disabled | Whether the tab bar is disabled | boolean | None |
-icon | Tab bar icon | ReactNode | None |
-itemKey | corresponding to `activeKey` | string | None |
-style | style object | CSSProperties | None |
-tab | Tab page bar display text | ReactNode | None |
-closable | whether user can close the tab **>=2.1.0** | boolean | false |
-
+| Property  | Description                                | Type          | Default Value |
+| --------- | ------------------------------------------ | ------------- | ------------- |
+| className | class name                                 | string        | None          |
+| disabled  | Whether the tab bar is disabled            | boolean       | None          |
+| icon      | Tab bar icon                               | ReactNode     | None          |
+| itemKey   | corresponding to `activeKey`               | string        | None          |
+| style     | style object                               | CSSProperties | None          |
+| tab       | Tab page bar display text                  | ReactNode     | None          |
+| closable  | whether user can close the tab **>=2.1.0** | boolean       | false         |
 
 ## Accessibility
 
 ### ARIA
-- About role
-  - TabBar has a role of `tablist`
-  - Tab in TabBar has a role of `tab`
-  - TabPane has a role of `tabpanel`
-- aria-orientation: Indicates TabBar's orientation, can be `vertical` or `horizontal`. When tabPosition is `left`,aria-orientation will be `vertical`, when tabPosition is `top`, aria-orientation will be `horizontal`.
-- aria-disabled: When TabPane is disabled, the related Tab's aria-disabled will be set to true.
-- aria-selected: Indicates whether the Tab is selected.
-- aria-controls: Indicates the TabPane controlled by the Tab
-- aria-labelledby: Indicates the element labels the TabPane
+
+-   About role
+    -   TabBar has a role of `tablist`
+    -   Tab in TabBar has a role of `tab`
+    -   TabPane has a role of `tabpanel`
+-   aria-orientation: Indicates TabBar's orientation, can be `vertical` or `horizontal`. When tabPosition is `left`,aria-orientation will be `vertical`, when tabPosition is `top`, aria-orientation will be `horizontal`.
+-   aria-disabled: When TabPane is disabled, the related Tab's aria-disabled will be set to true.
+-   aria-selected: Indicates whether the Tab is selected.
+-   aria-controls: Indicates the TabPane controlled by the Tab
+-   aria-labelledby: Indicates the element labels the TabPane
 
 ### Keyboard and Focus
+
 WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/
-- Tabs can be given focus, except for disabled tabs
-- Keyboard users can use the `Tab` key to move the focus to the tab panel of the selected tab element
-- Use `left and right arrows` to toggle options when focus is on a tab element in a horizontal tab list
-- Use `up and down arrows` to toggle options when focus is on a tab element in a vertical tab list
-- When the focus is on an inactive tab element in the tab list, the `Space` or `Enter` keys can be used to activate the tab
-- When keyboard users want to focus directly on the last tab element in the tab list:
-  - Mac users: `fn` + `right arrow`
-  - Windows users: `End`
-- When keyboard users want to focus directly on the first tab element in the tab list:
-  - Mac users: `fn` + `left arrow`
-  - Windows users: `Home`
-- When a tab is allowed to be deleted:
-  - Users can use `Delete` keys to delete tab
-  - After deletion, the focus is transferred to the next element of the deleted tab element; if the deleted element has no subsequent element, it is transferred to the previous element
+
+-   Tabs can be given focus, except for disabled tabs
+-   Keyboard users can use the `Tab` key to move the focus to the tab panel of the selected tab element
+-   Use `left and right arrows` to toggle options when focus is on a tab element in a horizontal tab list
+-   Use `up and down arrows` to toggle options when focus is on a tab element in a vertical tab list
+-   When the focus is on an inactive tab element in the tab list, the `Space` or `Enter` keys can be used to activate the tab
+-   When keyboard users want to focus directly on the last tab element in the tab list:
+    -   Mac users: `fn` + `right arrow`
+    -   Windows users: `End`
+-   When keyboard users want to focus directly on the first tab element in the tab list:
+    -   Mac users: `fn` + `left arrow`
+    -   Windows users: `Home`
+-   When a tab is allowed to be deleted:
+    -   Users can use `Delete` keys to delete tab
+    -   After deletion, the focus is transferred to the next element of the deleted tab element; if the deleted element has no subsequent element, it is transferred to the previous element
 
 ## Content Guidelines
-- Label copy needs to explain the label content accurately and clearly
-- Use short, easily distinguishable labels
-- try to stay within one word
 
+-   Label copy needs to explain the label content accurately and clearly
+-   Use short, easily distinguishable labels
+-   try to stay within one word
 
 ## Design Token
 

+ 221 - 106
content/navigation/tabs/index.md

@@ -19,8 +19,9 @@ import { Tabs, TabPane } from '@douyinfe/semi-ui';
 
 标签栏支持三种样式的显示:线条式,按钮式,卡片式。默认选中第一项。  
 标签页支持两种传入方式,两者渲染流程上有所区别:
-- 通过 `tabList` 传入标签页对象的数组,当使用 `tabList` 时每次只渲染当前传入的节点
-- 或使用 `<TabPane>` 逐项显式传入,使用 `<TabPane>` 时默认会渲染所有面板,可以通过设置 `keepDOM={false}` 只渲染当前面板,此时不会有动画效果。
+
+-   通过 `tabList` 传入标签页对象的数组,当使用 `tabList` 时每次只渲染当前传入的节点
+-   或使用 `<TabPane>` 逐项显式传入,使用 `<TabPane>` 时默认会渲染所有面板,可以通过设置 `keepDOM={false}` 只渲染当前面板,此时不会有动画效果。
 
 <Notice title='注意事项'>
     1. tabList 与 TabPane Children 同时使用时,会优先渲染通过 tabList 传入的数据。不建议同时配置 <br/>
@@ -42,8 +43,7 @@ import { Tabs, TabPane } from '@douyinfe/semi-ui';
                     Web 应用。
                 </p>
                 <p style={{ lineHeight: 1.8 }}>
-                    区别于其他的设计系统而言,Semi Design
-                    以用户中心、内容优先、设计人性化为设计理念,具有以下优势:
+                    区别于其他的设计系统而言,Semi Design 以用户中心、内容优先、设计人性化为设计理念,具有以下优势:
                 </p>
                 <ul>
                     <li>
@@ -73,9 +73,7 @@ import { Tabs, TabPane } from '@douyinfe/semi-ui';
                         backgroundColor: 'var(--semi-color-fill-0)',
                     }}
                 >
-                    <code>
-                        yarn add @douyinfe/semi-ui
-                    </code>
+                    <code>yarn add @douyinfe/semi-ui</code>
                 </pre>
             </TabPane>
             <TabPane tab="帮助" itemKey="3">
@@ -84,8 +82,7 @@ import { Tabs, TabPane } from '@douyinfe/semi-ui';
                     Q:有新组件需求、或者现有组件feature不能满足业务需求?
                 </p>
                 <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-1)' }}>
-                    右上角问题反馈,提交issue,label选择Feature Request / New Component Request
-                    我们会高优处理这些需求。
+                    右上角问题反馈,提交issue,label选择Feature Request / New Component Request 我们会高优处理这些需求。
                 </p>
                 <p style={{ lineHeight: 1.8, color: 'var(--semi-color-text-0)', fontWeight: 600 }}>
                     Q:对组件的使用有疑惑?
@@ -97,7 +94,6 @@ import { Tabs, TabPane } from '@douyinfe/semi-ui';
         </Tabs>
     </div>
 );
-
 ```
 
 ```jsx live=true
@@ -205,8 +201,7 @@ import { IconFile, IconGlobe, IconHelpCircle } from '@douyinfe/semi-icons';
 );
 ```
 
-### 更多选项收入 More 展示 
-
+### 更多选项收入 More 展示
 
 支持将多余 Tab 合并为 ”更多“ 下拉菜单,`more` 传入数字即可,数字表示收入下拉菜单的 Tab 数量。**(>=v2.59.0)**
 
@@ -214,43 +209,52 @@ import { IconFile, IconGlobe, IconHelpCircle } from '@douyinfe/semi-icons';
 import React from 'react';
 import { Tabs, TabPane } from '@douyinfe/semi-ui';
 
-function Demo(){
-  return (
-          <Tabs more={4} style={{ width: '60%', margin: '20px' }} type="card">
+function Demo() {
+    return (
+        <Tabs more={4} style={{ width: '60%', margin: '20px' }} type="card">
             {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
-                    <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
-                      Content of card tab {i}
-                    </TabPane>
+                <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                    Content of card tab {i}
+                </TabPane>
             ))}
-          </Tabs>
-  );
+        </Tabs>
+    );
 }
 ```
 
-也支持高级配置,向 `more` 传入对象,内可传入 
+也支持高级配置,向 `more` 传入对象,内可传入
 
-- `count`: 表示收入下拉菜单的 Tab 数量
-- `render`: 自定义 Trigger 的渲染函数,返回的 ReactNode 会被渲染为下拉菜单的 Trigger
-- `dropdownProps`: 传入 DropDown Props,会被透传到下拉菜单,如果需要自定义下拉菜单,使用 dropdownProps 中的 render 方法
+-   `count`: 表示收入下拉菜单的 Tab 数量
+-   `render`: 自定义 Trigger 的渲染函数,返回的 ReactNode 会被渲染为下拉菜单的 Trigger
+-   `dropdownProps`: 传入 DropDown Props,会被透传到下拉菜单,如果需要自定义下拉菜单,使用 dropdownProps 中的 render 方法
 
 ```jsx live=true dir=column
 import React from 'react';
-import { Tabs, TabPane } from '@douyinfe/semi-ui';
+import { Tabs, TabPane, Button } from '@douyinfe/semi-ui';
 
-function Demo(){
-  return (
-          <Tabs more={{ count: 4, render: ()=>{return <div style={{ display: 'inline-block' }}>Click to show More</div>;}, dropdownProps: { trigger: "click" } }} style={{ width: '60%', margin: '20px' }} type="card">
+function Demo() {
+    return (
+        <Tabs
+            more={{
+                count: 4,
+                render: () => {
+                    return <Button type='tertiary'>Click to show More</Button>;
+                },
+                dropdownProps: { trigger: 'click', position: 'bottomRight' },
+            }}
+            style={{ width: '60%', margin: '20px' }}
+            type="card"
+        >
             {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
-                    <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
-                      Content of card tab {i}
-                    </TabPane>
+                <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                    Content of card tab {i}
+                </TabPane>
             ))}
-          </Tabs>
-  );
+        </Tabs>
+    );
 }
 ```
 
-
 ### 垂直的标签栏
 
 支持水平和垂直两种模式, `tabPosition='left|top'`
@@ -280,7 +284,7 @@ class App extends React.Component {
                 <RadioGroup
                     onChange={e => this.onSelect(e)}
                     value={this.state.type}
-                    type='button'
+                    type="button"
                     style={{
                         display: 'flex',
                         width: 200,
@@ -338,9 +342,7 @@ class App extends React.Component {
                                     backgroundColor: 'var(--semi-color-fill-0)',
                                 }}
                             >
-                                <code>
-                                    yarn add @douyinfe/semi-ui
-                                </code>
+                                <code>yarn add @douyinfe/semi-ui</code>
                             </pre>
                         </div>
                     </TabPane>
@@ -379,7 +381,6 @@ class App extends React.Component {
 
 ### 滚动折叠
 
-**v>= 1.1.0**  
 通过设置 `collapsible` 可以支持滚动折叠,目前只支持 horizontal 模式。
 
 ```jsx live=true dir=column
@@ -401,6 +402,112 @@ class App extends React.Component {
 }
 ```
 
+**自定义滚动箭头渲染**
+
+通过 renderArrow 修改滚动折叠模式下,左右切换箭头的渲染,入参为溢出的 items 和 位置
+
+```jsx live=true dir="column"
+import React from 'react';
+import { Tabs, TabPane, Dropdown } from '@douyinfe/semi-ui';
+
+() => {
+    const [activeKey, setActiveKey] = useState('Tab-0');
+    const renderArrow = (items, pos, handleArrowClick) => {
+        const style = {
+            width: 32,
+            height: 32,
+            margin: '0 12px',
+            display: 'flex',
+            justifyContent: 'center',
+            alignItems: 'center',
+            borderRadius: '100%',
+            background: 'rgba(var(--semi-grey-1), 1)',
+            color: 'var(--semi-color-text)',
+            cursor: 'pointer',
+        };
+        return (
+            <Dropdown
+                render={
+                    <Dropdown.Menu>
+                        {items.map(item => {
+                            return (
+                                <Dropdown.Item onClick={() => setActiveKey(item.itemKey)}>{item.itemKey}</Dropdown.Item>
+                            );
+                        })}
+                    </Dropdown.Menu>
+                }
+            >
+                {pos === 'start' ? (
+                    <div style={style} onClick={handleArrowClick}>
+                        ←
+                    </div>
+                ) : (
+                    <div style={style} onClick={handleArrowClick}>
+                        →
+                    </div>
+                )}
+            </Dropdown>
+        );
+    };
+
+    return (
+        <Tabs
+            renderArrow={renderArrow}
+            style={{ width: '50%', margin: '20px' }}
+            activeKey={activeKey}
+            type="card"
+            collapsible
+            onChange={k => setActiveKey(k)}
+        >
+            {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
+                <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                    Content of card tab {i}
+                </TabPane>
+            ))}
+        </Tabs>
+    );
+};
+```
+
+**修改切换箭头的渲染位置**
+
+通过 `arrowPosition` 来修改溢出指示器的位置,可选 `start` `both` `end`
+
+```jsx live=true dir=column
+import React from 'react';
+import { Tabs, TabPane } from '@douyinfe/semi-ui';
+
+class App extends React.Component {
+    render() {
+        return (
+            <>
+                <Tabs style={{ width: '60%', margin: '20px' }} type="card" collapsible arrowPosition={'start'}>
+                    {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
+                        <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                            Content of card tab {i}
+                        </TabPane>
+                    ))}
+                </Tabs>
+                <Tabs style={{ width: '60%', margin: '20px' }} type="card" collapsible arrowPosition={'both'}>
+                    {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
+                        <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                            Content of card tab {i}
+                        </TabPane>
+                    ))}
+                </Tabs>
+                <Tabs style={{ width: '60%', margin: '20px' }} type="card" collapsible arrowPosition={'end'}>
+                    {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => (
+                        <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={i}>
+                            Content of card tab {i}
+                        </TabPane>
+                    ))}
+                </Tabs>
+            </>
+        );
+    }
+}
+```
+
 ### 禁用
 
 禁用标签栏中的某一个标签页。
@@ -563,7 +670,7 @@ class App extends React.Component {
 
 ### 关闭
 
-关闭标签栏中的某一个标签页。   
+关闭标签栏中的某一个标签页。  
 只有卡片样式的页签支持关闭选项。使用 `closable={true}` 来开启。
 
 ```jsx live=true
@@ -578,114 +685,122 @@ class App extends React.Component {
                 { tab: '文档', itemKey: '1', text: '文档', closable: true },
                 { tab: '快速起步', itemKey: '2', text: '快速起步', closable: true },
                 { tab: '帮助', itemKey: '3', text: '帮助' },
-            ]
+            ],
         };
     }
     close(key) {
         const newTabList = [...this.state.tabList];
-        const closeIndex = newTabList.findIndex(t=>t.itemKey===key);
+        const closeIndex = newTabList.findIndex(t => t.itemKey === key);
         newTabList.splice(closeIndex, 1);
         this.setState({ tabList: newTabList });
     }
     render() {
         return (
             <Tabs type="card" defaultActiveKey="1" onTabClose={this.close.bind(this)}>
-                {
-                    this.state.tabList.map(t=><TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey}>{t.text}</TabPane>)
-                }
+                {this.state.tabList.map(t => (
+                    <TabPane closable={t.closable} tab={t.tab} itemKey={t.itemKey} key={t.itemKey}>
+                        {t.text}
+                    </TabPane>
+                ))}
             </Tabs>
         );
     }
 }
-
 ```
 
 ## API 参考
 
 ### Tab
 
-属性 | 说明 | 类型                                                                        | 默认值     |
---- | --- |---------------------------------------------------------------------------|---------|
-activeKey | 当前激活的 tab 页的 itemKey 值 | string                                                                    | 无       |
-className | 类名 | string                                                                    | 无       |
-collapsible | 折叠的 Tabs,**>=1.1.0** | boolean                                                                   | false   |
-contentStyle | 内容区域外层样式对象 | CSSProperties                                                             | 无       |
-defaultActiveKey | 初始化选中的 tab 页的 key 值 | string                                                                    | '1'     |
-keepDOM | 使用 TabPane 写法时是否渲染隐藏面板的 DOM 结构,**>=1.0.0** | boolean                                                                   | true    |
-lazyRender | 懒渲染,仅当面板激活过才被渲染在 DOM 树中, **>=1.0.0** | boolean                                                                   | false   |
-more | 将一部分 Tab 渲染到下拉菜单中 ** >= 2.59.0** | number \| {count:number,render:()=>ReactNode,dropdownProps:DropDownProps} | -       |                                                              
-renderTabBar | 用于二次封装标签栏 | (tabBarProps: object, defaultTabBar: React.ComponentType) => ReactNode    | 无       |
-preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean                                                                   |         |  |
-size | 大小,提供 `large`、`medium`、`small` 三种类型,**>=1.11.0,目前仅支持线性 Tabs** | string                                                                    | `large` |
-style | 样式对象 | CSSProperties                                                             | 无       |
-tabBarExtraContent | 用于扩展标签栏的内容 | ReactNode                                                                 | 无       |
-tabList | 标签页对象组成的数组,该对象支持 itemKey(对应 activeKey,tab(标签页文字)及 icon(标签页图标) | TabPane[]                                                                 | 无       |
-tabPaneMotion | 是否使用动画切换 tabs | boolean                                                                   | true    |
-tabPosition | tab 的位置,支持`top`(水平), `left`(垂直),**>=1.0.0** | string                                                                    | `top`   |
-type | 标签栏的样式,可选`line`、 `card`、 `button` | string                                                                    | `line`  |
-onChange | 切换 tab 页时的回调函数 | function(activeKey: string)                                               | 无       |
-onTabClick | 单击事件 | function(key: string, e: Event)                                           | 无       |
-onTabClose | 关闭 tab 页时的回调函数 **>=2.1.0** | function(tabKey: string)                                                  | 无       
+| 属性 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| activeKey | 当前激活的 tab 页的 itemKey 值 | string | 无 |
+| arrowPosition | 折叠模式下,左右切换箭头渲染位置 **>=2.61.0** | "start" "end" "both" | 无 |
+| className | 类名 | string | 无 |
+| collapsible | 折叠的 Tabs,**>=1.1.0** | boolean | false |
+| visibleTabsStyle | 整体滚动区域 Style **>=2.61.0** | style: CSSProperties | 无 |
+| contentStyle | 内容区域外层样式对象 | CSSProperties | 无 |
+| defaultActiveKey | 初始化选中的 tab 页的 key 值 | string | '1' |
+| keepDOM | 使用 TabPane 写法时是否渲染隐藏面板的 DOM 结构 | boolean | true |
+| lazyRender | 懒渲染,仅当面板激活过才被渲染在 DOM 树中  | boolean | false |
+| more | 将一部分 Tab 渲染到下拉菜单中 ** >= 2.59.0** | number \| {count:number,render:()=>ReactNode,dropdownProps:DropDownProps} | - |
+| renderTabBar | 用于二次封装标签栏 | (tabBarProps: object, defaultTabBar: React.ComponentType) => ReactNode | 无 |
+| renderArrow | 折叠滚动模式下,自定义左右切换箭头如何渲染,默认为箭头按钮 hover 时展开溢出项 **>=2.61.0** | (items: OverflowItem[],pos:"start"\|"end", handleArrowClick:()=>void)=> ReactNode | 无 |
+| preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean |  |  |
+| showRestInDropdown | 是否将收起的 Tab 展示在下拉菜单中(仅当 collapsible 为 true 时生效) **>= 2.61.0** | boolean | true |
+| size | 大小,提供 `large`、`medium`、`small` 三种类型,**>=1.11.0,目前仅支持线性 Tabs** | string | `large` |
+| style | 样式对象 | CSSProperties | 无 |
+| tabBarExtraContent | 用于扩展标签栏的内容 | ReactNode | 无 |
+| tabList | 标签页对象组成的数组,该对象支持 itemKey(对应 activeKey,tab(标签页文字)及 icon(标签页图标) | TabPane[] | 无 |
+| tabPaneMotion | 是否使用动画切换 tabs | boolean | true |
+| tabPosition | tab 的位置,支持`top`(水平), `left`(垂直) | string | `top` |
+| type | 标签栏的样式,可选`line`、 `card`、 `button` | string | `line` |
+| onChange | 切换 tab 页时的回调函数 | function(activeKey: string) | 无 |
+| onTabClick | 单击事件 | function(key: string, e: Event) | 无 |
+| onTabClose | 关闭 tab 页时的回调函数 **>=2.1.0** | function(tabKey: string) | 无 |
+| onVisibleTabsChange | 折叠滚动模式下,溢出项切换变化回调 **>= 2.61.0** | (visibleState:Record\<string,bool\>)=>void | 无 |
 
 ### TabPane
 
-属性      | 说明             | 类型               | 默认值 |
---------- | ---------------- | ------------------ | ------ |
-className | 类名             | string             | 无     |
-disabled  | 标签页栏是否禁用 | boolean            | 无     |
-icon      | 标签页栏 icon    | ReactNode | 无     |
-itemKey   | 对应 `activeKey` | string             | 无     |
-style     | 样式对象         | CSSProperties             | 无     |
-tab       | 标签页栏显示文字 | ReactNode | 无     |
-closable  | 允许关闭tab **>=2.1.0**| boolean | false |
+| 属性      | 说明                     | 类型          | 默认值 |
+| --------- | ------------------------ | ------------- | ------ |
+| className | 类名                     | string        | 无     |
+| disabled  | 标签页栏是否禁用         | boolean       | 无     |
+| icon      | 标签页栏 icon            | ReactNode     | 无     |
+| itemKey   | 对应 `activeKey`         | string        | 无     |
+| style     | 样式对象                 | CSSProperties | 无     |
+| tab       | 标签页栏显示文字         | ReactNode     | 无     |
+| closable  | 允许关闭 tab **>=2.1.0** | boolean       | false  |
 
 ## Accessibility
 
 ### ARIA
-- 关于 role
-  - TabBar 对应的 role 为 `tablist`
-  - TabBar 中的 Tab 对应的 role 为 `tab`
-  - TabPane 对应的 role 为 `tabpanel`
-- aria-orientation: 表明 TabBar 的方向,有 `vertical` 和 `horizontal` 两种。当传入 tabPosition 为 left 时, aria-orientation 会被设置为 `vertical`,tabPosition 为 top 时,设置为 `horizontal`
-- aria-disabled: 当 TabPane 设置为 disabled 时,对应 Tab 的 aria-disabled 会被设置为 true
-- aria-selected: 表明 Tab 是否被选中
-- aria-controls: 指向 Tab 标签所控制的 TabPane
-- aria-labelledby: 指向设置 TabPane 标签的元素
+
+-   关于 role
+    -   TabBar 对应的 role 为 `tablist`
+    -   TabBar 中的 Tab 对应的 role 为 `tab`
+    -   TabPane 对应的 role 为 `tabpanel`
+-   aria-orientation: 表明 TabBar 的方向,有 `vertical` 和 `horizontal` 两种。当传入 tabPosition 为 left 时, aria-orientation 会被设置为 `vertical`,tabPosition 为 top 时,设置为 `horizontal`
+-   aria-disabled: 当 TabPane 设置为 disabled 时,对应 Tab 的 aria-disabled 会被设置为 true
+-   aria-selected: 表明 Tab 是否被选中
+-   aria-controls: 指向 Tab 标签所控制的 TabPane
+-   aria-labelledby: 指向设置 TabPane 标签的元素
 
 ### 键盘和焦点
+
 WAI-ARIA: https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/
-- 选项卡可以被获取到焦点,但禁用的选项卡除外
-- 键盘用户可以使用 `Tab` 键,将焦点移动到已被选择的选项卡元素的选项卡面板上
-- 当焦点位于水平选项卡列表中的选项卡元素上时,使用 `左右箭头` 来切换选项
-- 当焦点位于垂直选项卡列表中的选项卡元素上时,使用 `上下箭头` 来切换选项
-- 当焦点位于选项卡列表中的未被激活的选项卡元素上时,可以使用 `Space` 或 `Enter` 键来激活该选项卡
-- 当键盘用户想要直接将焦点聚焦到选项卡列表中的最后一个选项卡元素时:
-    - Mac 用户:`fn` + `右箭头`
-    - Windows 用户:`End`
-- 当键盘用户想要直接将焦点聚焦到选项卡列表中的第一个选项卡元素时:
-    - Mac 用户:`fn` + `左箭头`
-    - Windows 用户:`Home`
-- 当选项卡允许被删除时:
-    - 用户可以使用 `Delete` 键删除选项卡
-    - 删除后,焦点转移到被删除选项卡元素的后一个元素上;若被删除元素无后一个元素则转移到前一个元素上
 
+-   选项卡可以被获取到焦点,但禁用的选项卡除外
+-   键盘用户可以使用 `Tab` 键,将焦点移动到已被选择的选项卡元素的选项卡面板上
+-   当焦点位于水平选项卡列表中的选项卡元素上时,使用 `左右箭头` 来切换选项
+-   当焦点位于垂直选项卡列表中的选项卡元素上时,使用 `上下箭头` 来切换选项
+-   当焦点位于选项卡列表中的未被激活的选项卡元素上时,可以使用 `Space` 或 `Enter` 键来激活该选项卡
+-   当键盘用户想要直接将焦点聚焦到选项卡列表中的最后一个选项卡元素时:
+    -   Mac 用户:`fn` + `右箭头`
+    -   Windows 用户:`End`
+-   当键盘用户想要直接将焦点聚焦到选项卡列表中的第一个选项卡元素时:
+    -   Mac 用户:`fn` + `左箭头`
+    -   Windows 用户:`Home`
+-   当选项卡允许被删除时:
+    -   用户可以使用 `Delete` 键删除选项卡
+    -   删除后,焦点转移到被删除选项卡元素的后一个元素上;若被删除元素无后一个元素则转移到前一个元素上
 
 ## 设计变量
 
 <DesignToken/>
 
 ## 文案规范
-- 标签文案需要准确清晰地解释标签内容
-- 用简短的,易区分的标签
-- 尽量保持在一个词以内
 
+-   标签文案需要准确清晰地解释标签内容
+-   用简短的,易区分的标签
+-   尽量保持在一个词以内
 
 ## FAQ
 
--   **为什么在Tabs中使用 Typography 的省略 ellipsis 失效?**
+-   **为什么在 Tabs 中使用 Typography 的省略 ellipsis 失效?**
 
-    因为Tabs渲染TabPane时,默认是全部渲染display: none。此时这些组件无法获取到正确的宽度或高度值。建议1.x的版本开启lazyRender,或者关闭keepDOM。0.x的版本需要使用tabList的写法。
+    因为 Tabs 渲染 TabPane 时,默认是全部渲染 display: none。此时这些组件无法获取到正确的宽度或高度值。建议 1.x 的版本开启 lazyRender,或者关闭 keepDOM。0.x 的版本需要使用 tabList 的写法。
 
--   **为什么在Tabs中使用Collapse/Collapsible/Resizable Table等组件的高度或宽度值不对?**
+-   **为什么在 Tabs 中使用 Collapse/Collapsible/Resizable Table 等组件的高度或宽度值不对?**
 
     原因同上,另外如果 collapse 不需要动画,也可以通过设置 motion={false} 来关闭动画效果。此时无需获取组件的高度。

+ 1 - 0
content/navigation/tree/index-en-US.md

@@ -2271,6 +2271,7 @@ import { IconFixedStroked, IconSectionStroked, IconAbsoluteStroked, IconInnerSec
 | ------------------- | --------------------- | ------------------------------------------------- | ------- | ------ |
 | autoExpandParent | Toggle whether to expand parent node automatically | boolean | false | 0.34.0 |
 | autoExpandWhenDragEnter | Toggle whether allow autoExpand when drag enter node | boolean | true | 1.8.0 | 
+| autoMergeValue | Sets the automerge value. Specifically, when enabled, when a parent node is selected, value will include that node and its children. (Works if leafOnly is false)| boolean | true | 2.61.0 | 
 | blockNode           | Toggle whether to display node as row     | boolean                     | true    | - |
 | checkRelation | In multiple, the relationship between the checked states of the nodes, optional: 'related'、'unRelated' | string | 'related' | 2.5.0 |
 | className           | Class name| string                      | -       | - |

+ 1 - 0
content/navigation/tree/index.md

@@ -2286,6 +2286,7 @@ import { IconFixedStroked, IconSectionStroked, IconAbsoluteStroked, IconInnerSec
 |-------------   | ----------- | -------------- | -------------- | --------|
 | autoExpandParent | 是否自动展开父节点,默认为 false,当组件初次挂载时为 true | boolean | false | 0.34.0 |
 | autoExpandWhenDragEnter | 是否允许拖拽到节点上时自动展开改节点 | boolean | true | 1.8.0 | 
+| autoMergeValue | 设置自动合并 value。具体而言是,开启后,当某个父节点被选中时,value 将包括该节点以及该子孙节点。(在leafOnly为false的情况下生效)| boolean | true | 2.61.0 | 
 | blockNode | 行显示节点 | boolean | true | - |
 | checkRelation | 多选时,节点之间选中状态的关系,可选:'related'、'unRelated' | string | 'related' | 2.5.0 |
 | className | 类名 | string | - | - |

+ 6 - 5
content/show/overflowlist/index-en-US.md

@@ -215,11 +215,12 @@ import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder
 
 ## API Reference
 
-| Properties | Instructions  | type                  | Default | version |
-| ---------- | ------------- | --------------------- | ------- | ------- |
-| className  | Class name.   | string                | -       | 1.1.0   |
-| renderMode | Render mode.  | `collapse`\| `scroll` | `true`  | -       |
-| style      | OverflowList style  | React.CSSProperties   | -       | 1.1.0   |
+| Properties | Instructions                           | type                  | Default | version |
+| ---------- |----------------------------------------| --------------------- | ------- | ------- |
+| className  | Class name.                            | string                | -       | 1.1.0   |
+| onVisibleStateChange | Hide and display state change callback | (visibleState: Map\<string, boolean\>) => void; | -          | 2.61.0 |
+| renderMode | Render mode.                           | `collapse`\| `scroll` | `true`  | -       |
+| style      | OverflowList style                     | React.CSSProperties   | -       | 1.1.0   |
 
 ### renderMode='collapse'
 

+ 14 - 12
content/show/overflowlist/index.md

@@ -214,11 +214,11 @@ import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder
 
 ## API 参考
 
-| 属性       | 说明     | 类型                  | 默认值     | 版本  |
-| ---------- | -------- | --------------------- | ---------- | ----- |
-| className  | 类名     | string                | -          | 1.1.0 |
-| renderMode | 渲染模式 | `collapse`\| `scroll` | `collapse` | 1.1.0 |
-| style      | OverflowList的样式 | React.CSSProperties                | -       | 1.1.0   |
+| 属性       | 说明     | 类型                                              | 默认值        | 版本     |
+| ---------- | -------- |-------------------------------------------------|------------|--------|
+| className  | 类名     | string                                          | -          | 1.1.0  |
+| renderMode | 渲染模式 | `collapse`\| `scroll`                           | `collapse` | 1.1.0  |
+| style      | OverflowList的样式 | React.CSSProperties                             | -          | 1.1.0  |
 
 ### renderMode='collapse'
 
@@ -233,12 +233,14 @@ import { IconAlarm, IconBookmark, IconCamera, IconDuration, IconEdit, IconFolder
 
 ### renderMode='scroll'
 
-| 属性 | 说明 | 类型 | 默认值 | 版本 |
-| --- | --- | --- | --- | --- |
-| items | 渲染项目,**要求必含 key 项** | Record<string, any>[] | - | 1.1.0 |
+| 属性 | 说明 | 类型                                                                                                                                 | 默认值 | 版本 |
+| --- | --- |------------------------------------------------------------------------------------------------------------------------------------| --- | --- |
+| items | 渲染项目,**要求必含 key 项** | Record<string, any>[]                                                                                                              | - | 1.1.0 |
 | onIntersect | 溢出回调 | ({[key: string]: [IntersectionObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)}) => void | - | 1.1.0 |
-| overflowRenderer | 溢出项的自定义渲染函数 | (overflowItems: Record<string, any>[]) => React.ReactNode[] | - | 1.1.0 |
-| threshold | 触发溢出回调的阈值 | number | 0.75 | 1.1.0 |
-| visibleItemRenderer | 展示项的自定义渲染函数 | (item: Record<string, any>, index: number) => React.ReactElement | - | 1.1.0 |
-| wrapperClassName | 滚动 wrapper 的类名 | string | - | 1.1.0 |
+| onVisibleStateChange | 隐藏显示状态变化回调 | (visibleState: Map\<string, boolean\>) => void; | -          | 2.61.0 |
+| overflowRenderer | 溢出项的自定义渲染函数 | (overflowItems: Record<string, any>[]) => React.ReactNode[]                                                                        | - | 1.1.0 |
+| threshold | 触发溢出回调的阈值 | number                                                                                                                             | 0.75 | 1.1.0 |
+| visibleItemRenderer | 展示项的自定义渲染函数 | (item: Record<string, any>, index: number) => React.ReactElement                                                                   | - | 1.1.0 |
+| wrapperClassName | 滚动 wrapper 的类名 | string                                                                                                                             | - | 1.1.0 |
 | wrapperStyle | 滚动 wrapper 的样式 | React.CSSProperties | - | 1.1.0 |
+

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

@@ -20,6 +20,26 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
 - 【Fix】
     - Fixed the problem in Typography that icon size and size settings are not consistent, affecting version 2.59.0-2.60.1 [#2308](https://github.com/DouyinFE/semi-design/pull/2308)
 
+
+#### 🎉 2.61.0-beta.0 (2024-06-18)
+- 【Feat】
+  - Breadcrumb supports controlling the current highlighted navigation item through activeIndex [#2301](https://github.com/DouyinFE/semi-design/pull/2301)
+  - Select supports searchPosition configuration [#2298](https://github.com/DouyinFE/semi-design/pull/2298)
+  - Form component formApi adds scrollToError to support manual scrolling to the validation error [#2294](https://github.com/DouyinFE/semi-design/pull/2294)
+  - Tree, TreeSelect add autoMergeValue API [@LuyangFE](https://github.com/LuyangFE) [#2233](https://github.com/DouyinFE/semi-design/pull/2233)
+  - Tabs adds arrowPosition to set the rendering position of the arrow switch in scroll collapse mode [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+  - Tabs adds renderArrow to customize the rendering of the arrow left and right switch in scroll collapse mode [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+  - Tabs adds visibleTabsStyle to set the scroll area style [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+  - Tabs adds onVisibleTabsChange to get unhidden items when tabs overflow [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+  - Tabs adds showRestInDropdown is used to control the visibility of the collapsible Tabs Dropdown panel [#2289](https://github.com/DouyinFE/semi-design/pull/2289)
+  - OverflowList adds onVisibleStateChange in scroll mode to get non-hidden items when overflowing [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+  - Avatar size supports passing in legal width attribute values ​​such as "10px" [#2290](https://github.com/DouyinFE/semi-design/pull/2290)
+- 【Fix】
+  - Fix the problem of Tooltip not hide when cursor quickly moving [#2306](https://github.com/DouyinFE/semi-design/pull/2306)
+  - Fixed the issue where the position of the drop-down menu in Pagination did not change due to position changes [2307](https://github.com/DouyinFE/semi-design/pull/2307)
+  - Fixed ths issue that align not work when Table is virtualized [@icwoker](https://github.com/icwoker) [#2300](https://github.com/DouyinFE/semi-design/pull/2300)
+  - Fixed the issue that the tooltip cannot be triggered when the selected item in the trigger wants to display the tooltip (for example, the label is ReactNode and there is a tooltip, or renderSelectedItem is used to customize the rendering of the selected item, which has a tooltip) in the single-select, searchable, search box in the trigger TreeSelect [#2291](https://github.com/DouyinFE/semi-design/issues/2291) [#2292](https://github.com/DouyinFE/semi-design/pull/2292)
+
 #### 🎉 2.60.0 (2024-06-07)
 - 【Docs】
     - update filter API defination In Tranfer

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

@@ -17,6 +17,25 @@ Semi 版本号遵循 **Semver** 规范(主版本号-次版本号-修订版本
 - 【Fix】
     - 修复 Typography 组件中 icon 大小和 size 设置未保持一致问题,影响版本2.59.0-2.60.1 [#2308](https://github.com/DouyinFE/semi-design/pull/2308)
 
+#### 🎉 2.61.0-beta.0 (2024-06-18)
+- 【Feat】
+    - Breadcrumb 支持通过 activeIndex 受控当前高亮导航项 [#2301](https://github.com/DouyinFE/semi-design/pull/2301)
+    - Select 支持 searchPosition 配置 [#2298](https://github.com/DouyinFE/semi-design/pull/2298)
+    - Form 组件 formApi 增加 scrollToError ,支持手动滚动至校验错误处 [#2294](https://github.com/DouyinFE/semi-design/pull/2294)
+    - Tree、TreeSelect 增加 autoMergeValue API [@LuyangFE](https://github.com/LuyangFE) [#2233](https://github.com/DouyinFE/semi-design/pull/2233)
+    - Tabs 添加 arrowPosition 设置滚动折叠模式下,箭头切换器的渲染位置 [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+    - Tabs 添加 renderArrow 用于自定义滚动折叠模式下,箭头左右切换器的渲染 [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+    - Tabs 添加 visibleTabsStyle 用于设置滚动区域样式 [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+    - Tabs 添加 onVisibleTabsChange 用于在 tabs 溢出时获取未隐藏的项目 [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+    - Tabs 增加 showRestInDropdown 用于控制可折叠 Tabs Dropdown 面板的显隐 [#2289](https://github.com/DouyinFE/semi-design/pull/2289)
+    - OverflowList 在 scroll 模式下添加 onVisibleStateChange 用于在溢出时获取未隐藏的项目 [#2288](https://github.com/DouyinFE/semi-design/pull/2288)
+    - Avatar size 支持传入合法的 width 属性值例如 "10px" [#2290](https://github.com/DouyinFE/semi-design/pull/2290)
+- 【Fix】
+    - 修复 Pagination 因位置变化下拉菜单位置没有跟随变化的问题 [2307](https://github.com/DouyinFE/semi-design/pull/2307)
+    - 修复 Tooltip 在快速移动下,可能由于 React 未正确触发 onMouseLeave 导致的未消失的问题 [#2306](https://github.com/DouyinFE/semi-design/pull/2306)
+    - Table 组件配置 virtualized 虚拟化后 align 失效的问题。 [@icwoker](https://github.com/icwoker) [#2300](https://github.com/DouyinFE/semi-design/pull/2300)
+    - 修复单选,可搜索,搜索框在 trigger 的 TreeSelect,当 trigger 中的选中项想要展示 Tooltip 时(比如 label 为 ReactNode,并且有 Tooltip,或者使用 renderSelectedItem 自定义渲染已选项目, 其中有 Tooltip), tooltip 无法被触发问题  [#2291](https://github.com/DouyinFE/semi-design/issues/2291) [#2292](https://github.com/DouyinFE/semi-design/pull/2292)
+
 #### 🎉 2.60.0 (2024-06-07)
 - 【Docs】
     - 更新对 Transfer 的 filter API 说明 [#2280](https://github.com/DouyinFE/semi-design/pull/2280)

+ 7 - 0
cypress/e2e/tabs.spec.js

@@ -100,4 +100,11 @@ describe('tabs', () => {
         cy.get('.semi-button-disabled').eq(0).should('exist');
         cy.get('.semi-tabs-bar-arrow .semi-button-primary').eq(0).should('exist');
     });
+
+    it('showRestInDropdown', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=tabs--show-rest-in-dropdown-demo&args=&viewMode=story');
+
+        cy.get('.semi-button').eq(1).trigger('mouseover');
+        cy.get('.semi-dropdown-content .semi-dropdown-item').should('not.exist');
+    });
 });

+ 1 - 1
lerna.json

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

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-animation-react",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "motion library for semi-ui-react",
     "keywords": [
         "motion",
@@ -25,8 +25,8 @@
         "prepublishOnly": "npm run build:lib"
     },
     "dependencies": {
-        "@douyinfe/semi-animation": "2.60.1",
-        "@douyinfe/semi-animation-styled": "2.60.1",
+        "@douyinfe/semi-animation": "2.61.0-beta.0",
+        "@douyinfe/semi-animation-styled": "2.61.0-beta.0",
         "classnames": "^2.2.6"
     },
     "devDependencies": {

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-animation-styled",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "semi styled animation",
     "keywords": [
         "semi",

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-animation",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "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.60.1",
+    "version": "2.61.0-beta.0",
     "description": "semi ui eslint plugin",
     "keywords": [
         "semi",

+ 40 - 3
packages/semi-foundation/form/foundation.ts

@@ -9,6 +9,11 @@ import { BaseFormAdapter, FormState, CallOpts, FieldState, FieldStaff, Component
 
 export type { BaseFormAdapter };
 
+type ScrollToErrorOpts = {
+    field?: string;
+    index?: number;
+    scrollOpts?: ScrollIntoViewOptions
+}
 export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
 
     data: FormState;
@@ -72,6 +77,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
         this.getFormProps = this.getFormProps.bind(this);
         this.getFieldExist = this.getFieldExist.bind(this);
         this.scrollToField = this.scrollToField.bind(this);
+        this.scrollToError = this.scrollToError.bind(this);
     }
 
     init() {
@@ -130,7 +136,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
         this._adapter.forceUpdate();
     }
 
-    // in order to slove byted-issue-289
+    // in order to solve bytedance internal issue-289
     registerArrayField(arrayFieldPath: string, val: any): void {
         this.updateArrayField(arrayFieldPath, {
             updateKey: new Date().valueOf(),
@@ -622,6 +628,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
             submitForm: () => this.submit(),
             getFieldExist: (field: string) => this.getFieldExist(field),
             scrollToField: (field: string, scrollOpts?: ScrollIntoViewOptions) => this.scrollToField(field, scrollOpts),
+            scrollToError: (opts?: ScrollToErrorOpts) => this.scrollToError(opts),
         };
     }
 
@@ -701,8 +708,8 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
         const errorDOM = this._adapter.getAllErrorDOM();
         if (errorDOM && errorDOM.length) {
             try {
-                const fieldDom = errorDOM[0].parentNode.parentNode;
-                scrollIntoView(fieldDom as Element, scrollOpts);
+                const fieldDOM = errorDOM[0].parentNode.parentNode;
+                scrollIntoView(fieldDOM as Element, scrollOpts);
             } catch (error) {}
         }
     }
@@ -713,4 +720,34 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
             scrollIntoView(fieldDOM as Element, scrollOpts);
         }
     }
+
+    scrollToError(config?: ScrollToErrorOpts): void { 
+        let scrollOpts: ScrollIntoViewOptions = config && config.scrollOpts ? config.scrollOpts : { behavior: 'smooth', block: 'start' };
+        let field = config && config.field;
+        let index = config && config.index;
+        let fieldDOM, errorDOM;
+        if (typeof index === 'number') {
+            const allErrorDOM = this._adapter.getAllErrorDOM();
+            let errorDOM = allErrorDOM[index];
+            if (errorDOM) {
+                fieldDOM = errorDOM.parentNode.parentNode;
+            }
+        } else if (field) {
+            // If field is specified, find the error dom of the corresponding field
+            errorDOM = this._adapter.getFieldErrorDOM(field);
+            if (errorDOM) {
+                fieldDOM = errorDOM.parentNode.parentNode;
+            }
+        } else if (typeof field === 'undefined') {
+            // If field is not specified, find all error doms and scroll to the first one
+            let allErrorDOM = this._adapter.getAllErrorDOM();
+            if (allErrorDOM && allErrorDOM.length) {
+                fieldDOM = allErrorDOM[0].parentNode.parentNode;
+            }
+        }
+
+        if (fieldDOM) {
+            scrollIntoView(fieldDOM as Element, scrollOpts);
+        }
+    }
 }

+ 9 - 1
packages/semi-foundation/form/interface.ts

@@ -22,6 +22,7 @@ export interface BaseFormAdapter<P = Record<string, any>, S = Record<string, any
     getFormProps: (keys: undefined | string | Array<string>) => any;
     getAllErrorDOM: () => NodeList;
     getFieldDOM: (field: string) => Node;
+    getFieldErrorDOM: (field: string) => Node;
     initFormId: () => void
 }
 
@@ -62,6 +63,12 @@ export type FieldPathValue<T, P extends FieldPath<T>> =
           ? T[P]
           : never;
 
+export type ScrollToErrorOptions<K> = {
+    field?: K;
+    index?: number;
+    scrollConfig?: ScrollIntoViewOptions
+}
+
 // use object replace Record<string, any>, fix issue 933
 export interface BaseFormApi<T extends object = any> {
     /** get value of field */
@@ -91,7 +98,8 @@ export interface BaseFormApi<T extends object = any> {
     getValues: () => T;
     /** set value of multiple fields */
     setValues: (fieldsValue: Partial<T>, config?: setValuesConfig) => void;
-    scrollToField: <K extends keyof T>(field: K, scrollConfig?: ScrollIntoViewOptions) => void
+    scrollToField: <K extends keyof T>(field: K, scrollConfig?: ScrollIntoViewOptions) => void;
+    scrollToError: <K extends keyof T>(config?: ScrollToErrorOptions<K>) => void
 }
 
 export interface CallOpts {

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

@@ -1,13 +1,13 @@
 {
     "name": "@douyinfe/semi-foundation",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "",
     "scripts": {
         "build:lib": "node ./scripts/compileLib.js",
         "prepublishOnly": "npm run build:lib"
     },
     "dependencies": {
-        "@douyinfe/semi-animation": "2.60.1",
+        "@douyinfe/semi-animation": "2.61.0-beta.0",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "date-fns": "^2.29.3",

+ 2 - 0
packages/semi-foundation/select/constants.ts

@@ -14,6 +14,8 @@ const strings = {
     MODE_AUTOCOMPLETE: 'autoComplete',
     // MODE_TAGS: 'tags',
     STATUS: VALIDATE_STATUS,
+    SEARCH_POSITION_TRIGGER: 'trigger',
+    SEARCH_POSITION_DROPDOWN: 'dropdown'
 } as const;
 
 const numbers = { LIST_HEIGHT: 270 };

+ 14 - 4
packages/semi-foundation/select/foundation.ts

@@ -6,6 +6,7 @@ import isNullOrUndefined from '../utils/isNullOrUndefined';
 import { BasicOptionProps } from './optionFoundation';
 import isEnterPress from '../utils/isEnterPress';
 import { handlePrevent } from '../utils/a11y';
+import { strings } from './constants';
 
 export interface SelectAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
     getTriggerWidth(): number;
@@ -18,7 +19,7 @@ export interface SelectAdapter<P = Record<string, any>, S = Record<string, any>>
     rePositionDropdown(): void;
     updateFocusIndex(index: number): void;
     updateSelection(selection: Map<any, any>): void;
-    openMenu(): void;
+    openMenu(cb?: () => void): void;
     notifyDropdownVisibleChange(visible: boolean): void;
     registerClickOutsideHandler(event: any): void;
     toggleInputShow(show: boolean, cb: () => void): void;
@@ -30,6 +31,7 @@ export interface SelectAdapter<P = Record<string, any>, S = Record<string, any>>
     notifyClear(): void;
     updateInputValue(inputValue: string): void;
     focusInput(): void;
+    focusDropdownInput(): void;
     notifySearch(inputValue: string, event?: any): void;
     registerKeyDown(handler: () => void): void;
     unregisterKeyDown(): void;
@@ -367,7 +369,12 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
             // whether it is a filter or not, isFocus is guaranteed to be true when open
             this._adapter.updateFocusState(true);
         }
-        this._adapter.openMenu();
+        this._adapter.openMenu(() => {
+            const { searchPosition, autoFocus } = this.getProps();
+            if (autoFocus && searchPosition === strings.SEARCH_POSITION_DROPDOWN) {
+                this._adapter.focusDropdownInput();
+            }
+        });
         this._setDropdownWidth();
         this._adapter.notifyDropdownVisibleChange(true);
 
@@ -378,6 +385,7 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
             this._notifyBlur(e);
             this._adapter.updateFocusState(false);
         });
+
     }
 
     toggle2SearchInput(isShow: boolean) {
@@ -714,6 +722,8 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
         }
     }
 
+    // When searchPosition is trigger, the keyboard events bind to the outer trigger div
+    // When searchPosition is dropdown, the popup and the outer trigger div are not parent- child relationships, keyboard events bind to the dorpdown input
     _handleKeyDown(event: KeyboardEvent) {
         const key = event.keyCode;
         const { loading, filter, multiple, disabled } = this.getProps();
@@ -1044,8 +1054,8 @@ export default class SelectFoundation extends BaseFoundation<SelectAdapter> {
     }
 
     handleClearClick(e: MouseEvent) {
-        const { filter } = this.getProps();
-        if (filter) {
+        const { filter, searchPosition } = this.getProps();
+        if (filter && searchPosition === strings.SEARCH_POSITION_TRIGGER) {
             this.clearInput(e);
         }
         // after click showClear button, the select need to be focused

+ 10 - 1
packages/semi-foundation/select/select.scss

@@ -548,4 +548,13 @@ $overflowList: #{$prefix}-overflow-list;
 }
 
 
-    @import './rtl.scss';
+.#{$module}-dropdown-search-wrapper {
+    padding-top: $spacing-select_dropdown_input_paddingTop;
+    padding-right: $spacing-select_dropdown_input_paddingRight;
+    padding-bottom: $spacing-select_dropdown_input_paddingBottom;
+    padding-top: $spacing-select_dropdown_input_paddingTop;
+    padding-left: $spacing-select_dropdown_input_paddingLeft;
+    border-bottom: 1px solid $color-select_dropdown_input-border;
+}
+
+@import './rtl.scss';

+ 7 - 0
packages/semi-foundation/select/variables.scss

@@ -8,6 +8,8 @@ $color-select-border-hover: $color-select-border-default;  // 选择器输入框
 $color-select-border-active: var(--semi-color-focus-border); // 选择器输入框描边颜色 - 按下态
 $color-select-border-focus: $color-select-border-active;// 选择器输入框描边颜色 - 选中态
 
+$color-select_dropdown_input-border: $color-select-border-default; // 下拉搜索框底部描边颜色
+
 $color-select_warning-bg-default: var(--semi-color-warning-light-default); // 警示选择器输入框背景色 - 默认态
 $color-select_warning-border-default: var(--semi-color-warning-light-default); // 警示选择器输入框描边颜色 - 默认态
 $color-select_warning-bg-hover: var(--semi-color-warning-light-hover); // 警示选择器输入框背景色 - 悬停态
@@ -109,6 +111,11 @@ $spacing-select_option_list-paddingRight: 0px; // 选择器内容区右侧内边
 $spacing-select_option_list-paddingBottom: $spacing-extra-tight; // 选择器内容区底部内边距
 $spacing-select_option_list-paddingLeft: 0px; // 选择器内容区左侧内边距
 
+$spacing-select_dropdown_input_paddingTop: 8px; // 下拉搜索框顶部内边距
+$spacing-select_dropdown_input_paddingBottom: 8px; // 下拉搜索框底部内边距
+$spacing-select_dropdown_input_paddingRight: 12px; // 下拉搜索框右侧内边距
+$spacing-select_dropdown_input_paddingLeft: 12px; // 下拉搜索框左侧内边距
+
 // Radius
 $radius-select: var(--semi-border-radius-small); // 选择器输入框圆角
 $radius-select_option: 0px; // 选择器待选项圆角

+ 1 - 0
packages/semi-foundation/table/constants.ts

@@ -20,6 +20,7 @@ const strings = {
     SORT_DIRECTIONS: ['ascend', 'descend'],
     FIXED_SET: [false, true, 'left', 'right'],
     ALIGNS: ['left', 'right', 'center'],
+    JUSTIFY_CONTENT: ['flex-start', 'flex-end', 'center'],
     SCROLL_HORIZONTAL_POSITIONS: ['left', 'middle', 'right'],
     DEFAULT_KEY_COLUMN_SELECTION: 'column-selection',
     DEFAULT_KEY_COLUMN_EXPAND: 'column-expand',

+ 25 - 0
packages/semi-foundation/table/utils.ts

@@ -479,6 +479,31 @@ export function getRTLAlign(align: typeof strings.ALIGNS[number], direction?: 'l
     return align;
 }
 
+export function getRTLFlexAlign(align: typeof strings.ALIGNS[number], direction?: 'ltr' | 'rtl'): typeof strings.JUSTIFY_CONTENT[number] {
+    if (direction === 'rtl') {
+        switch (align) {
+            case 'left':
+                return 'flex-end';
+            case 'right':
+                return 'flex-start';
+            default:
+                return align;
+        }
+    }
+    else
+    {
+        switch (align) {
+            case 'left':
+                return 'flex-start';
+            case 'right':
+                return 'flex-end';
+            default:
+                return align;
+        }
+    }
+}
+
+
 export function shouldShowEllipsisTitle(ellipsis: BaseEllipsis) {
     const shouldShowTitle = ellipsis === true || get(ellipsis, 'showTitle', true);
     return shouldShowTitle;

+ 16 - 2
packages/semi-foundation/tooltip/foundation.ts

@@ -29,7 +29,7 @@ export interface TooltipAdapter<P = Record<string, any>, S = Record<string, any>
     notifyVisibleChange(isVisible: any): void;
     getPopupContainerRect(): PopupContainerDOMRect;
     containerIsBody(): boolean;
-    off(arg0: string): void;
+    off(arg0: string, arg1?: () => void): void;
     canMotion(): boolean;
     registerScrollHandler(arg: () => Record<string, any>): void;
     unregisterScrollHandler(): void;
@@ -66,7 +66,8 @@ export interface TooltipAdapter<P = Record<string, any>, S = Record<string, any>
     setInitialFocus(): void;
     notifyEscKeydown(event: any): void;
     getTriggerNode(): any;
-    setId(): void
+    setId(): void;
+    getTriggerDOM(): HTMLElement|null
 }
 
 export type Position = ArrayElement<typeof strings.POSITION_SET>;
@@ -312,6 +313,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
     show = () => {
         const content = this.getProp('content');
         const trigger = this.getProp('trigger');
+
         const clickTriggerToHide = this.getProp('clickTriggerToHide');
         const { visible, displayNone } = this.getStates();
         if (displayNone) {
@@ -332,6 +334,18 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
             this.calcPosition();
         });
 
+        if (trigger==="hover") {
+            const checkTriggerIsHover = () => {
+                const triggerDOM = this._adapter.getTriggerDOM();
+                if (trigger && !triggerDOM.matches(":hover")) {
+                    this.hide();
+                }
+                this._adapter.off("portalInserted", checkTriggerIsHover);
+            };
+            this._adapter.on('portalInserted', checkTriggerIsHover);
+        }
+
+
         this._adapter.on('positionUpdated', () => {
             this._togglePortalVisible(true);
         });

+ 2 - 2
packages/semi-foundation/tree/foundation.ts

@@ -456,11 +456,11 @@ export default class TreeFoundation extends BaseFoundation<TreeAdapter, BasicTre
 
     notifyMultipleChange(key: string[], e: any) {
         const { keyEntities } = this.getStates();
-        const { leafOnly, checkRelation, keyMaps } = this.getProps();
+        const { leafOnly, checkRelation, keyMaps, autoMergeValue } = this.getProps();
         let value;
         let keyList = [];
         if (checkRelation === 'related') {
-            keyList = normalizeKeyList(key, keyEntities, leafOnly, true);
+            keyList = autoMergeValue ? normalizeKeyList(key, keyEntities, leafOnly, true) : key;
         } else if (checkRelation === 'unRelated') {
             keyList = key;
         }

+ 2 - 2
packages/semi-foundation/treeSelect/foundation.ts

@@ -399,10 +399,10 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
 
     _notifyMultipleChange(key: string[], e: any) {
         const { keyEntities } = this.getStates();
-        const { leafOnly, checkRelation, keyMaps } = this.getProps();
+        const { leafOnly, checkRelation, keyMaps, autoMergeValue } = this.getProps();
         let keyList = [];
         if (checkRelation === 'related') {
-            keyList = normalizeKeyList(key, keyEntities, leafOnly, true);
+            keyList = autoMergeValue ? normalizeKeyList(key, keyEntities, leafOnly, true) : key;
         } else if (checkRelation === 'unRelated') {
             keyList = key as string[];
         }

+ 3 - 1
packages/semi-foundation/utils/Event.ts

@@ -44,7 +44,9 @@ export default class Event {
         if (!this._eventMap.has(event)) {
             return false;
         }
-        this._eventMap.get(event).forEach(callback => callback(...args));
+        const callbacks = [...this._eventMap.get(event)];
+        // clone to avoid someone writing  the logic of deleting callback in callbacks into his or her callback code, for example the once func above
+        callbacks.forEach(callback => callback(...args));
         return true;
     }
 }

+ 1 - 1
packages/semi-icons-lab/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-icons-lab",
-  "version": "2.60.1",
+  "version": "2.61.0-beta.0",
   "description": "semi icons lab",
   "keywords": [
     "semi",

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-icons",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "semi icons",
     "keywords": [
         "semi",

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-illustrations",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "semi illustrations",
     "keywords": [
         "semi",

+ 2 - 2
packages/semi-next/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-next",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "Plugin that support Semi Design in Next.js",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",
@@ -23,7 +23,7 @@
         "typescript": "^4"
     },
     "dependencies": {
-        "@douyinfe/semi-webpack-plugin": "2.60.1"
+        "@douyinfe/semi-webpack-plugin": "2.61.0-beta.0"
     },
     "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
 }

+ 0 - 1
packages/semi-next/tsconfig.json

@@ -10,7 +10,6 @@
         "moduleResolution": "node",
         "noImplicitAny": true,
         "declaration": true,
-        "suppressImplicitAnyIndexErrors": true,
         "forceConsistentCasingInFileNames": true,
         "allowSyntheticDefaultImports": true,
         "experimentalDecorators": true,

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-rspack-plugin",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "",
     "homepage": "",
     "license": "MIT",

+ 1 - 1
packages/semi-scss-compile/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-scss-compile",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "compile semi scss to css",
     "author": "[email protected]",
     "license": "MIT",

+ 1 - 1
packages/semi-theme-default/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-theme-default",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "semi-theme-default",
     "keywords": [
         "semi-theme",

+ 1 - 1
packages/semi-ui/_base/baseComponent.tsx

@@ -16,7 +16,7 @@ export type ValidateStatus = ArrayElement<typeof VALIDATE_STATUS>;
 export interface BaseProps {
     style?: React.CSSProperties;
     className?: string;
-    children?: ReactNode | undefined | any
+    children?: ReactNode | undefined | any;
 }
 
 // eslint-disable-next-line

+ 6 - 4
packages/semi-ui/breadcrumb/index.tsx

@@ -41,6 +41,7 @@ export interface BreadcrumbProps extends BaseProps {
     /* Style type for ellipsis area */
     moreType?: MoreType;
     'aria-label'?: React.AriaAttributes['aria-label']
+    activeIndex?: number
 }
 
 interface BreadcrumbState {
@@ -53,6 +54,7 @@ class Breadcrumb extends BaseComponent<BreadcrumbProps, BreadcrumbState> {
     static Item: typeof BreadcrumbItem = BreadcrumbItem;
 
     static propTypes = {
+        activeIndex: propTypes.number,
         routes: propTypes.array,
         onClick: propTypes.func,
         separator: propTypes.node,
@@ -199,9 +201,9 @@ class Breadcrumb extends BaseComponent<BreadcrumbProps, BreadcrumbState> {
                     <BreadcrumbItem
                         {...route}
                         key={key}
-                        active={idx === items.length - 1}
+                        active={this.props.activeIndex !== undefined ? this.props.activeIndex===idx : idx === items.length - 1}
                         route={route._origin}
-                        shouldRenderSeparator={!(shouldCollapse && (hasRenderMore || moreTypeIsPopover) && inCollapseArea)}
+                        shouldRenderSeparator={ (idx !== items.length - 1) && !(shouldCollapse && (hasRenderMore || moreTypeIsPopover) && inCollapseArea)}
                     >
                         {renderItem ? renderItem(route._origin) : route.name}
                     </BreadcrumbItem>
@@ -252,8 +254,8 @@ class Breadcrumb extends BaseComponent<BreadcrumbProps, BreadcrumbState> {
 
                     return React.cloneElement(item, {
                         key: `${idx}-item`,
-                        active: idx === items.length - 1,
-                        shouldRenderSeparator: !(shouldCollapse && (hasRenderMore || moreTypeIsPopover) && inCollapseArea)
+                        active: this.props.activeIndex !== undefined ? this.props.activeIndex === idx : idx === items.length - 1,
+                        shouldRenderSeparator: (idx !== items.length - 1) && (!(shouldCollapse && (hasRenderMore || moreTypeIsPopover) && inCollapseArea))
                     });
                 })
             );

+ 1 - 3
packages/semi-ui/breadcrumb/item.tsx

@@ -199,9 +199,7 @@ export default class BreadcrumbItem extends BaseComponent<BreadcrumbItemProps, B
         } = this.props;
         const pageLabel = active ? { 'aria-current': 'page' as const } : {};
         const item = this.renderItem();
-        const separator = !active ?
-            this.props.separator || <span className={`${clsPrefix}-separator`}>{this.context.separator}</span> :
-            null;
+        const separator = this.props.separator || <span className={`${clsPrefix}-separator`}>{this.context.separator}</span>;
         const wrapperCLs = cls({
             [`${clsPrefix}-item-wrap`]: true,
             // [`${clsPrefix}-item-wrap-iconOnly`]: !!children && this.props.icon,

+ 90 - 0
packages/semi-ui/form/_story/FormApi/scrollToError.jsx

@@ -0,0 +1,90 @@
+import React, { useState, useLayoutEffect, useRef } from 'react';
+import {
+    Form,
+    Toast,
+    Button, Modal, TreeSelect, Row, Col, Avatar, Select as BasicSelect
+} from '../../../index';
+const { Input, Select, DatePicker, Switch, Slider, CheckboxGroup, Checkbox, RadioGroup, Radio, TimePicker, InputNumber, InputGroup } = Form;
+
+import { IconPlusCircle, IconMinusCircle } from '@douyinfe/semi-icons';
+
+class ScrollToErrorDemo extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            fields: Array.from({ length: 100 }, (v, i) => i + 1),
+        };
+        this.formApi = null;
+        this.getFormApi = this.getFormApi.bind(this);
+        this.validate = this.validate.bind(this);
+        this.scrollToError = this.scrollToError.bind(this);
+    }
+
+    getFormApi(formApi) {
+        this.formApi = formApi;
+    }
+
+    validate() {
+        let begin = new Date().valueOf();
+        let end, time;
+        this.formApi
+            .validate(['No22', 'No55', 'No88'])
+            .then(values => {
+                end = new Date().valueOf();
+                time = (end - begin) / 1000;
+                console.log(`validate用时:${ time }s`);
+                // debugger
+            })
+            .catch(err => {
+                end = new Date().valueOf();
+                time = (end - begin) / 1000;
+                console.log(`validate用时:${ time }s`);
+                // debugger
+            });
+    }
+
+    scrollToError(target) {
+        // this.formApi.scrollToError({ field: `No${targetIndex}`});
+        if (typeof target === 'string') {
+            this.formApi.scrollToError({ field: target });
+        } else if (typeof target === 'number') {
+            this.formApi.scrollToError({ index: target });
+        } else if (!target) {
+            this.formApi.scrollToError();
+        }
+    }
+
+    renderFields() {
+        const { fields } = this.state;
+
+        return fields.map(item => (
+            <Form.Input
+                key={`No${item}`}
+                field={`No${item}`}
+                rules={[
+                    { required: true, message: 'required error' },
+                    {
+                        pattern: /^[a-zA-Z0-9_]+$/,
+                        message: '限制输入字符为:a-z, A-Z, 0-9, _',
+                    },
+                ]}
+            />
+        ));
+    }
+    render() {
+        let fields = this.renderFields();
+        return (
+            <Form getFormApi={this.getFormApi}>
+                <div style={{ height: 500, overflow: 'scroll' }}>{fields}</div>
+                <Button type='primary' onClick={this.validate}>validate</Button>
+                <Button onClick={() => this.scrollToError(88)}>scrollTo 88 Error</Button>
+                <Button onClick={() => this.scrollToError()}>scrollTo first Error</Button>
+                <Button onClick={() => this.scrollToError('No55')}>scrollTo No55 Error</Button>
+                <Button htmlType="reset">reset</Button>
+                <Button htmlType="submit">submit</Button>
+            </Form>
+        );
+    }
+}
+
+export { ScrollToErrorDemo };

+ 4 - 0
packages/semi-ui/form/_story/form.stories.jsx

@@ -69,6 +69,7 @@ import {
 } from './Performance/performanceDemo';
 import { SetValuesDemo, SetValuesWithArrayField } from './FormApi/setValuesDemo';
 import { SetValueUsingParentPath } from './FormApi/formApiDemo';
+import { ScrollToErrorDemo } from './FormApi/scrollToError';
 import { FieldPathWithArrayDemo } from './Debug/bugDemo';
 import ChildDidMount from './Debug/childDidMount';
 
@@ -76,6 +77,9 @@ export { default as FormSubmit } from './FormSubmit';
 export { default as TabelForm } from './TableDemo';
 export { default as RemountInit } from './ArrayField/remountInit'
 
+export const ScrollToError = () => <ScrollToErrorDemo></ScrollToErrorDemo>
+// export { default as ScrollToError } from './FormApi/scrollToError'
+
 const {
   Input,
   Select,

+ 7 - 0
packages/semi-ui/form/baseForm.tsx

@@ -208,6 +208,13 @@ class Form<Values extends Record<string, any> = any> extends BaseComponent<BaseF
             },
             getFieldDOM: (field: string) =>
                 document.querySelector(`.${cssClasses.PREFIX}-field[x-field-id="${field}"]`),
+            getFieldErrorDOM: (field: string) => {
+                const { formId } = this.state;
+                const { id } = this.props;
+                const xId = id ? id : formId;
+                let selector = `form[x-form-id="${xId}"] .${cssClasses.PREFIX}-field[x-field-id="${field}"] .${cssClasses.PREFIX}-field-error-message`;
+                return document.querySelector(selector);
+            }
         };
     }
 

+ 27 - 9
packages/semi-ui/overflowList/index.tsx

@@ -1,4 +1,4 @@
-import React, { CSSProperties, ReactNode, MutableRefObject, RefCallback, Key, ReactElement } from 'react';
+import React, { CSSProperties, ReactNode, MutableRefObject, RefCallback, ReactElement } from 'react';
 import cls from 'classnames';
 import BaseComponent from '../_base/baseComponent';
 import PropTypes from 'prop-types';
@@ -20,6 +20,8 @@ const RenderMode = strings.MODE_MAP;
 export type { ReactIntersectionObserverProps } from './intersectionObserver';
 export type OverflowItem = Record<string, any>;
 
+type Key = string|number
+
 export interface OverflowListProps {
     className?: string;
     collapseFrom?: 'start' | 'end';
@@ -34,7 +36,9 @@ export interface OverflowListProps {
     visibleItemRenderer?: (item: OverflowItem, index: number) => ReactElement;
     wrapperClassName?: string;
     wrapperStyle?: CSSProperties;
-    itemKey?: Key | ((item: OverflowItem) => Key)
+    itemKey?: Key | ((item: OverflowItem) => Key);
+    onVisibleStateChange?: (visibleState: Map<string, boolean>) => void;
+    overflowRenderDirection?: "both"|"start"|'end' // used in tabs, not exposed to user
 }
 
 export interface OverflowListState {
@@ -64,6 +68,7 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
         threshold: 0.75,
         visibleItemRenderer: (): ReactElement => null,
         onOverflow: () => null,
+        overflowRenderDirection: "both",
     })
     static propTypes = {
         // if render in scroll mode, key is required in items
@@ -81,6 +86,8 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
         visibleItemRenderer: PropTypes.func,
         wrapperClassName: PropTypes.string,
         wrapperStyle: PropTypes.object,
+        collapseMask: PropTypes.object,
+        overflowRenderDirection: PropTypes.string,
     };
 
     constructor(props: OverflowListProps) {
@@ -143,7 +150,9 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
         return {
             ...super.adapter,
             updateVisibleState: (visibleState): void => {
-                this.setState({ visibleState });
+                this.setState({ visibleState }, ()=>{
+                    this.props.onVisibleStateChange?.(visibleState);
+                });
             },
             updateStates: (states): void => {
                 this.setState({ ...states });
@@ -257,9 +266,8 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
         }
         const inner =
             renderMode === RenderMode.SCROLL ?
-                [
-                    overflow[0],
-                    <div
+                (()=>{
+                    const list = [<div
                         className={cls(wrapperClassName, `${prefixCls}-scroll-wrapper`)}
                         ref={(ref): void => {
                             this.scroller = ref;
@@ -275,9 +283,19 @@ class OverflowList extends BaseComponent<OverflowListProps, OverflowListState> {
                                 key,
                             });
                         })}
-                    </div>,
-                    overflow[1],
-                ] :
+                    </div>];
+                    if (this.props.overflowRenderDirection === "both") {
+                        list.unshift(overflow[0]);
+                        list.push(overflow[1]);
+                    } else if (this.props.overflowRenderDirection === "start") {
+                        list.unshift(overflow[1]);
+                        list.unshift(overflow[0]);
+                    } else {
+                        list.push(overflow[0]);
+                        list.push(overflow[1]);
+                    }
+                    return list;
+                })() :
                 [
                     collapseFrom === Boundary.START ? overflow : null,
                     visible.map((item, idx) => {

+ 7 - 7
packages/semi-ui/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-ui",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.",
     "main": "lib/cjs/index.js",
     "module": "lib/es/index.js",
@@ -20,12 +20,12 @@
         "@dnd-kit/core": "^6.0.8",
         "@dnd-kit/sortable": "^7.0.2",
         "@dnd-kit/utilities": "^3.2.1",
-        "@douyinfe/semi-animation": "2.60.1",
-        "@douyinfe/semi-animation-react": "2.60.1",
-        "@douyinfe/semi-foundation": "2.60.1",
-        "@douyinfe/semi-icons": "2.60.1",
-        "@douyinfe/semi-illustrations": "2.60.1",
-        "@douyinfe/semi-theme-default": "2.60.1",
+        "@douyinfe/semi-animation": "2.61.0-beta.0",
+        "@douyinfe/semi-animation-react": "2.61.0-beta.0",
+        "@douyinfe/semi-foundation": "2.61.0-beta.0",
+        "@douyinfe/semi-icons": "2.61.0-beta.0",
+        "@douyinfe/semi-illustrations": "2.61.0-beta.0",
+        "@douyinfe/semi-theme-default": "2.61.0-beta.0",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "copy-text-to-clipboard": "^2.1.1",

+ 1 - 0
packages/semi-ui/pagination/index.tsx

@@ -374,6 +374,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
                 i < 3 ? (content = restLeftPageList) : (content = restRightPageList);
                 return (
                     <Popover
+                        rePosKey={this.props.currentPage}
                         trigger="hover"
                         // onVisibleChange={visible=>this.handleRestHover(visible, i < 3 ? 'left' : 'right')}
                         content={this.renderRestPageList(content)}

+ 31 - 1
packages/semi-ui/select/_story/select.stories.jsx

@@ -3567,4 +3567,34 @@ export const UpdateOtherKeyNotInList = () => {
       <Button id='change' onClick={() => change()}>change</Button>
     </>
   );
-};
+};
+
+export const SearchPosition = () => {
+  
+  return (<>
+        <Select
+          filter
+          searchPosition='dropdown'
+          onChangeWithObject
+          placeholder={'single searchPosition=dropdown'}
+          optionList={optionList}
+          searchPlaceholder='dropdown input place'
+          showClear
+          autoFocus
+          style={{ width: 320 }}
+        />
+        <Select
+          filter
+          multiple
+          placeholder={'multiple searchPosition=dropdown'}
+          searchPosition='dropdown'
+          onChangeWithObject
+          showClear
+          searchPlaceholder='dropdown input place'
+          autoClearSearchValue={false}
+          optionList={optionList}
+          style={{ width: 320 }}
+        />
+    </>
+  )
+}

+ 72 - 11
packages/semi-ui/select/index.tsx

@@ -25,7 +25,7 @@ import Option, { OptionProps } from './option';
 import OptionGroup from './optionGroup';
 import Spin from '../spin';
 import Trigger from '../trigger';
-import { IconChevronDown, IconClear } from '@douyinfe/semi-icons';
+import { IconChevronDown, IconClear, IconSearch } from '@douyinfe/semi-icons';
 import { isSemiIcon, getFocusableElements, getActiveElement, getDefaultPropsFromGlobalConfig } from '../_utils';
 import { getUuidShort } from '@douyinfe/semi-foundation/utils/uuid';
 
@@ -151,6 +151,8 @@ export type SelectProps = {
     defaultActiveFirstOption?: boolean;
     onChangeWithObject?: boolean;
     suffix?: React.ReactNode;
+    searchPosition?: string;
+    searchPlaceholder?: string;
     prefix?: React.ReactNode;
     insetLabel?: React.ReactNode;
     insetLabelId?: string;
@@ -269,6 +271,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
         defaultActiveFirstOption: PropTypes.bool,
         triggerRender: PropTypes.func,
         stopPropagation: PropTypes.bool,
+        searchPosition: PropTypes.string,
         // motion doesn't need to be exposed
         motion: PropTypes.bool,
 
@@ -345,6 +348,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
         defaultActiveFirstOption: true, // In order to meet the needs of A11y, change to true
         showArrow: true,
         showClear: false,
+        searchPosition: strings.SEARCH_POSITION_TRIGGER,
         remote: false,
         autoAdjustOverflow: true,
         autoClearSearchValue: true,
@@ -360,6 +364,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
     })
 
     inputRef: React.RefObject<HTMLInputElement>;
+    dropdownInputRef: React.RefObject<HTMLInputElement>;
     triggerRef: React.RefObject<HTMLDivElement>;
     optionContainerEl: React.RefObject<HTMLDivElement>;
     optionsRef: React.RefObject<any>;
@@ -395,6 +400,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
         this.selectID = '';
         this.virtualizeListRef = React.createRef();
         this.inputRef = React.createRef();
+        this.dropdownInputRef = React.createRef(); // only work when searchPosition = 'dropdown'
         this.triggerRef = React.createRef();
         this.optionsRef = React.createRef();
         this.optionContainerEl = React.createRef();
@@ -444,6 +450,12 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                     this.inputRef.current.focus({ preventScroll });
                 }
             },
+            focusDropdownInput: () => {
+                const { preventScroll } = this.props;
+                if (this.dropdownInputRef && this.dropdownInputRef.current) {
+                    this.dropdownInputRef.current.focus({ preventScroll });
+                }
+            }
         };
         const multipleAdapter = {
             notifyMaxLimit: (option: OptionProps) => this.props.onExceed(option),
@@ -515,8 +527,10 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             updateOptions: (options: OptionProps[]) => {
                 this.setState({ options });
             },
-            openMenu: () => {
-                this.setState({ isOpen: true });
+            openMenu: (cb?: () => void) => {
+                this.setState({ isOpen: true }, () => {
+                    cb?.();
+                });
             },
             closeMenu: () => {
                 this.setState({ isOpen: false });
@@ -658,7 +672,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
 
     handleInputChange = (value: string, event: React.ChangeEvent<HTMLInputElement>) => this.foundation.handleInputChange(value, event);
 
-    renderInput() {
+    renderTriggerInput() {
         const { size, multiple, disabled, inputProps, filter } = this.props;
         const inputPropsCls = get(inputProps, 'className');
         const inputcls = cls(`${prefixcls}-input`, {
@@ -704,6 +718,46 @@ class Select extends BaseComponent<SelectProps, SelectState> {
         );
     }
 
+    renderDropdownInput() {
+        const { size, multiple, disabled, inputProps, filter, searchPosition, searchPlaceholder } = this.props;
+        const { inputValue, focusIndex } = this.state;
+        const wrapperCls = cls(`${prefixcls}-dropdown-search-wrapper`, {
+
+        });
+        const inputPropsCls = get(inputProps, 'className');
+        const inputCls = cls(`${prefixcls}-dropdown-input`, {
+            [`${prefixcls}-dropdown-input-single`]: !multiple,
+            [`${prefixcls}-dropdown-input-multiple`]: multiple,
+        }, inputPropsCls);
+
+        const selectInputProps: Record<string, any> = {
+            value: inputValue,
+            disabled,
+            className: inputCls,
+            onChange: this.handleInputChange,
+            placeholder: searchPlaceholder,
+            showClear: true,
+            ...inputProps,
+            /**
+             * When searchPosition is trigger, the keyboard events are bound to the outer trigger div, so there is no need to listen in input.
+             * When searchPosition is dropdown, the popup and the outer trigger div are not parent- child relationships,
+             * and bubbles cannot occur, so onKeydown needs to be listened in input.
+             *  */ 
+            onKeyDown: (e) => this.foundation._handleKeyDown(e)
+        };
+
+        return (
+            <div className={wrapperCls}>
+                <Input
+                    ref={this.dropdownInputRef}
+                    prefix={<IconSearch></IconSearch>}
+                    aria-activedescendant={focusIndex !== -1 ? `${this.selectID}-option-${focusIndex}` : ''}
+                    {...selectInputProps}
+                />
+            </div>
+        );
+    }
+
     close() {
         this.foundation.close();
     }
@@ -889,7 +943,9 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             loading,
             virtualize,
             multiple,
-            emptyContent
+            emptyContent,
+            searchPosition,
+            filter,
         } = this.props;
 
         // Do a filter first, instead of directly judging in forEach, so that the focusIndex can correspond to
@@ -922,6 +978,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                 onKeyDown={e => this.foundation.handleContainerKeyDown(e)}
             >
                 {outerTopSlot ? <div className={`${prefixcls}-option-list-outer-top-slot`} onMouseEnter={() => this.foundation.handleSlotMouseEnter()}>{outerTopSlot}</div> : null}
+                {searchPosition === strings.SEARCH_POSITION_DROPDOWN && filter ? this.renderDropdownInput() : null}
                 <div
                     style={{ maxHeight: `${maxHeight}px` }}
                     className={optionListCls}
@@ -939,7 +996,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
     }
 
     renderSingleSelection(selections: Map<OptionProps['label'], any>, filterable: boolean) {
-        let { renderSelectedItem } = this.props;
+        let { renderSelectedItem, searchPosition } = this.props;
         const { placeholder } = this.props;
         const { showInput, inputValue } = this.state;
         let renderText: React.ReactNode = '';
@@ -955,11 +1012,13 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             renderText = (renderSelectedItem as RenderSingleSelectedItemFn)(selectedItem);
         }
 
+        const showInputInTrigger = searchPosition === strings.SEARCH_POSITION_TRIGGER;
+
         const spanCls = cls({
             [`${prefixcls}-selection-text`]: true,
             [`${prefixcls}-selection-placeholder`]: !renderText && renderText !== 0,
-            [`${prefixcls}-selection-text-hide`]: inputValue && showInput, // show Input
-            [`${prefixcls}-selection-text-inactive`]: !inputValue && showInput, // Stack Input & RenderText(opacity 0.4)
+            [`${prefixcls}-selection-text-hide`]: inputValue && showInput && showInputInTrigger, // show Input
+            [`${prefixcls}-selection-text-inactive`]: !inputValue && showInput && showInputInTrigger, // Stack Input & RenderText(opacity 0.4)
         });
 
         const contentWrapperCls = `${prefixcls}-content-wrapper`;
@@ -971,7 +1030,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                             {renderText || renderText === 0 ? renderText : placeholder}
                         </span>
                     }
-                    {filterable && showInput ? this.renderInput() : null}
+                    {filterable && showInput && showInputInTrigger ? this.renderTriggerInput() : null}
                 </div>
             </>
         );
@@ -1176,7 +1235,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
 
 
     renderMultipleSelection(selections: Map<OptionProps['label'], any>, filterable: boolean) {
-        let { renderSelectedItem } = this.props;
+        let { renderSelectedItem, searchPosition } = this.props;
         const { placeholder, maxTagCount, expandRestTagsOnClick, ellipsisTrigger } = this.props;
         const { inputValue, isOpen } = this.state;
 
@@ -1210,11 +1269,13 @@ class Select extends BaseComponent<SelectProps, SelectState> {
             ? selectedItems.map((item, i) => this.renderTag(item, i))
             : oneLineTags;
 
+        const showTriggerInput = filterable && searchPosition === strings.SEARCH_POSITION_TRIGGER;
+
         return (
             <>
                 <div className={contentWrapperCls}>
                     {selectedItems && selectedItems.length ? tagContent : placeholderText}
-                    {!filterable ? null : this.renderInput()}
+                    {showTriggerInput ? this.renderTriggerInput() : null}
                 </div>
             </>
         );

+ 3 - 2
packages/semi-ui/table/TableCell.tsx

@@ -5,7 +5,7 @@ import { get, noop, set, omit, isEqual, merge } from 'lodash';
 
 import { cssClasses, numbers } from '@douyinfe/semi-foundation/table/constants';
 import TableCellFoundation, { TableCellAdapter } from '@douyinfe/semi-foundation/table/cellFoundation';
-import { isSelectionColumn, isExpandedColumn, getRTLAlign, shouldShowEllipsisTitle } from '@douyinfe/semi-foundation/table/utils';
+import { isSelectionColumn, isExpandedColumn, getRTLAlign, shouldShowEllipsisTitle, getRTLFlexAlign } from '@douyinfe/semi-foundation/table/utils';
 
 import BaseComponent, { BaseProps } from '../_base/baseComponent';
 import Context, { TableContextProps } from './table-context';
@@ -202,7 +202,8 @@ export default class TableCell extends BaseComponent<TableCellProps, Record<stri
 
         if (column.align) {
             const textAlign = getRTLAlign(column.align, direction);
-            tdProps.style = { ...tdProps.style, textAlign };
+            const justifyContent = getRTLFlexAlign(column.align, direction);
+            tdProps.style = { ...tdProps.style, textAlign, justifyContent };
         }
 
         return { tdProps, customCellProps };

+ 1 - 0
packages/semi-ui/table/_story/virtualizedFixed/index.jsx

@@ -15,6 +15,7 @@ const Demo = () => {
             width: 250,
             fixed: 'left',
             key: 'en_name',
+            align: 'right',
             render: (text, record, index, { expandIcon: realExpandIcon }) => {
                 return (
                     <>

+ 60 - 38
packages/semi-ui/tabs/TabBar.tsx

@@ -18,7 +18,8 @@ export interface TabBarState {
     endInd: number;
     rePosKey: number;
     startInd: number;
-    uuid: string
+    uuid: string;
+    currentVisibleItems: string[]
 }
 
 export interface OverflowItem extends PlainTab {
@@ -50,6 +51,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
             rePosKey: 0,
             startInd: 0,
             uuid: '',
+            currentVisibleItems: []
         };
     }
 
@@ -58,11 +60,11 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
             uuid: getUuidv4(),
         });
     }
-    
+
     componentDidUpdate(prevProps) {
         if (prevProps.activeKey !== this.props.activeKey) {
             if (this.props.collapsible) {
-                this.scrollActiveTabItemIntoView()
+                this.scrollActiveTabItemIntoView();
             }
         }
     }
@@ -106,11 +108,10 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
     renderTabItem = (panel: PlainTab): ReactNode => {
         const { size, type, deleteTabItem, handleKeyDown, tabPosition } = this.props;
         const isSelected = this._isActive(panel.itemKey);
-        
         return (
             <TabItem
                 {...pick(panel, ['disabled', 'icon', 'itemKey', 'tab', 'closable'])}
-                key={this._getItemKey(panel.itemKey)}
+                key={this._getBarItemKeyByItemKey(panel.itemKey)}
                 selected={isSelected}
                 size={size}
                 type={type}
@@ -128,8 +129,8 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
     }
 
     scrollActiveTabItemIntoView = (logicalPosition?: ScrollLogicalPosition) => {
-        const key = this._getItemKey(this.props.activeKey);
-        this.scrollTabItemIntoViewByKey(key, logicalPosition)
+        const key = this._getBarItemKeyByItemKey(this.props.activeKey);
+        this.scrollTabItemIntoViewByKey(key, logicalPosition);
     }
 
     renderTabComponents = (list: Array<PlainTab>): Array<ReactNode> => list.map(panel => this.renderTabItem(panel));
@@ -139,8 +140,8 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
         if (!lastItem) {
             return;
         }
-        const key = this._getItemKey(lastItem.itemKey);
-        this.scrollTabItemIntoViewByKey(key)
+        const key = this._getBarItemKeyByItemKey(lastItem.itemKey);
+        this.scrollTabItemIntoViewByKey(key);
     };
 
     renderCollapse = (items: Array<OverflowItem>, icon: ReactNode, pos: 'start' | 'end'): ReactNode => {
@@ -148,7 +149,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
             [`${cssClasses.TABS_BAR}-arrow-${pos}`]: pos,
             [`${cssClasses.TABS_BAR}-arrow`]: true,
         });
-        
+
         if (isEmpty(items)) {
             return (
                 <div role="presentation" className={arrowCls}>
@@ -160,7 +161,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
                 </div>
             );
         }
-        const { dropdownClassName, dropdownStyle } = this.props;
+        const { dropdownClassName, dropdownStyle, showRestInDropdown } = this.props;
         const { rePosKey } = this.state;
         const disabled = !items.length;
         const menu = (
@@ -182,38 +183,48 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
             </Dropdown.Menu>
         );
 
+        const button = (
+            <div role="presentation" className={arrowCls} onClick={(e): void => this.handleArrowClick(items, pos)}>
+                <Button
+                    disabled={disabled}
+                    icon={icon}
+                    theme="borderless"
+                />
+            </div>
+        );
+
         const dropdownCls = cls(dropdownClassName, {
             [`${cssClasses.TABS_BAR}-dropdown`]: true,
         });
 
         return (
-            <Dropdown
-                className={dropdownCls}
-                clickToHide
-                clickTriggerToHide
-                key={`${rePosKey}-${pos}`}
-                position={pos === 'start' ? 'bottomLeft' : 'bottomRight'}
-                render={disabled ? null : menu}
-                showTick
-                style={dropdownStyle}
-                trigger={'hover'}
-                disableFocusListener // prevent the panel from popping up again after clicking
-            >
-                <div role="presentation" className={arrowCls} onClick={(e): void => this.handleArrowClick(items, pos)}>
-                    <Button
-                        disabled={disabled}
-                        icon={icon}
-                        // size="small"
-                        theme="borderless"
-                    />
-                </div>
-            </Dropdown>
+            <>
+                {showRestInDropdown ? (
+                    <Dropdown
+                        className={dropdownCls}
+                        clickToHide
+                        clickTriggerToHide
+                        key={`${rePosKey}-${pos}`}
+                        position={pos === 'start' ? 'bottomLeft' : 'bottomRight'}
+                        render={disabled ? null : menu}
+                        showTick
+                        style={dropdownStyle}
+                        trigger={'hover'}
+                        disableFocusListener // prevent the panel from popping up again after clicking
+                    >
+                        {button}
+                    </Dropdown>
+                ) : (button)}
+            </>
         );
     };
 
-    renderOverflow = (items: any[]): Array<ReactNode> => items.map((item, ind) => {
-        const icon = ind === 0 ? <IconChevronLeft/> : <IconChevronRight/>;
-        const pos = ind === 0 ? 'start' : 'end';
+    renderOverflow = (items: any[]): Array<ReactNode> => items.map((item, index) => {
+        const pos = index === 0 ? 'start' : 'end';
+        if (this.props.renderArrow) {
+            return this.props.renderArrow(item, pos, ()=>this.handleArrowClick(item, pos));
+        }
+        const icon = index === 0 ? <IconChevronLeft/> : <IconChevronRight/>;
         return this.renderCollapse(item, icon, pos);
     });
 
@@ -222,16 +233,26 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
         const { list } = this.props;
         const renderedList = list.map(item => {
             const { itemKey } = item;
-            return { key: this._getItemKey(itemKey), active: this._isActive(itemKey), ...item };
+            return { key: this._getBarItemKeyByItemKey(itemKey), active: this._isActive(itemKey), ...item };
         });
         return (
             <OverflowList
                 items={renderedList}
+                overflowRenderDirection={this.props.arrowPosition}
+                wrapperStyle={this.props.visibleTabsStyle}
                 overflowRenderer={this.renderOverflow}
                 renderMode="scroll"
                 className={`${cssClasses.TABS_BAR}-overflow-list`}
                 visibleItemRenderer={this.renderTabItem as any}
+                onVisibleStateChange={(visibleMap)=>{
+                    const visibleMapWithItemKey: Map<string, boolean> = new Map();
+                    visibleMap.forEach((v, k )=>{
+                        visibleMapWithItemKey.set(this._getItemKeyByBarItemKey(k), v);
+                    });
+                    this.props.onVisibleTabsChange?.(visibleMapWithItemKey);
+                }}
             />
+
         );
     };
 
@@ -246,7 +267,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
                 {(locale: Locale['Tabs'], localeCode: Locale['code']) => (
                     <div className={`${cssClasses.TABS_BAR}-more-trigger-content`}>
                         <div>{locale.more}</div>
-                        <IconChevronDown className={`${cssClasses.TABS_BAR}-more-trigger-content-icon`}/>
+                        <IconChevronDown className={`${cssClasses.TABS_BAR}-more-trigger-content-icon`} />
                     </div>
                 )}
             </LocaleConsumer>
@@ -315,7 +336,8 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
 
     private _isActive = (key: string): boolean => key === this.props.activeKey;
 
-    private _getItemKey = (key: string): string => `${key}-bar`;
+    private _getBarItemKeyByItemKey = (key: string): string => `${key}-bar`;
+    private _getItemKeyByBarItemKey = (key: string): string => key.replace(/-bar$/, "");
 }
 
 export default TabBar;

+ 21 - 1
packages/semi-ui/tabs/_story/tabs.stories.jsx

@@ -1038,4 +1038,24 @@ export const Fix2239 = () => {
 
 Fix2239.story = {
   name: 'Fix 2239',
-};
+};
+
+export const ShowRestInDropdownDemo = () => {
+  return (
+    <Tabs
+      style={{
+        width: '60%',
+        margin: '20px',
+      }}
+      type="card"
+      collapsible
+      showRestInDropdown={false}
+    >
+      {[...Array(30).keys()].map(i => (
+        <TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={`${i}`}>
+          Content of card tab {i}
+        </TabPane>
+      ))}
+    </Tabs>
+  )
+}

+ 16 - 1
packages/semi-ui/tabs/index.tsx

@@ -41,6 +41,7 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
         onChange: PropTypes.func,
         onTabClick: PropTypes.func,
         renderTabBar: PropTypes.func,
+        showRestInDropdown: PropTypes.bool,
         size: PropTypes.oneOf(strings.SIZE),
         style: PropTypes.object,
         tabBarClassName: PropTypes.string,
@@ -53,6 +54,8 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
         onTabClose: PropTypes.func,
         preventScroll: PropTypes.bool,
         more: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
+        arrowPosition: PropTypes.string,
+        renderArrow: PropTypes.func,
     };
     static __SemiComponentName__ = "Tabs";
     static defaultProps: TabsProps = getDefaultPropsFromGlobalConfig(Tabs.__SemiComponentName__, {
@@ -66,7 +69,9 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
         tabPaneMotion: true,
         tabPosition: 'top',
         type: 'line',
-        onTabClose: () => undefined
+        onTabClose: () => undefined,
+        showRestInDropdown: true,
+        arrowPosition: "both",
     });
 
     contentRef: RefObject<HTMLDivElement>;
@@ -246,6 +251,7 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
             keepDOM,
             lazyRender,
             renderTabBar,
+            showRestInDropdown,
             size,
             style,
             tabBarClassName,
@@ -255,6 +261,10 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
             tabPosition,
             type,
             more,
+            onVisibleTabsChange,
+            visibleTabsStyle,
+            arrowPosition,
+            renderArrow,
             ...restProps
         } = this.props;
         const { panes, activeKey } = this.state;
@@ -274,6 +284,7 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
             collapsible,
             list: panes,
             onTabClick: this.onTabClick,
+            showRestInDropdown,
             size,
             style: tabBarStyle,
             tabBarExtraContent,
@@ -282,6 +293,10 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
             deleteTabItem: this.deleteTabItem,
             handleKeyDown: this.foundation.handleKeyDown,
             more,
+            onVisibleTabsChange,
+            visibleTabsStyle,
+            arrowPosition,
+            renderArrow
         } as TabBarProps;
 
         const tabBar = renderTabBar ? renderTabBar(tabBarProps, TabBar) : <TabBar {...tabBarProps} />;

+ 15 - 3
packages/semi-ui/tabs/interface.ts

@@ -1,7 +1,8 @@
 import React, { ComponentType, CSSProperties, MouseEvent, ReactNode } from 'react';
 import { Motion } from '../_base/base';
-import TabBar from './TabBar';
+import TabBar, { OverflowItem } from './TabBar';
 import { DropdownProps } from "../dropdown";
+import { OverflowListProps } from "../overflowList";
 
 export type TabType = 'line' | 'card' | 'button';
 export type TabSize = 'small' | 'medium' | 'large';
@@ -28,6 +29,7 @@ export interface TabsProps {
     onChange?: (activeKey: string) => void;
     onTabClick?: (activeKey: string, e: MouseEvent<Element>) => void;
     renderTabBar?: (tabBarProps: TabBarProps, defaultTabBar: typeof TabBar) => ReactNode;
+    showRestInDropdown?: boolean;
     size?: TabSize;
     style?: CSSProperties;
     tabBarClassName?: string;
@@ -39,7 +41,11 @@ export interface TabsProps {
     type?: TabType;
     onTabClose?: (tabKey: string) => void;
     preventScroll?: boolean;
-    more?: number | { count: number; render?: () => ReactNode; dropdownProps?: DropdownProps }
+    more?: number | { count: number; render?: () => ReactNode; dropdownProps?: DropdownProps };
+    onVisibleTabsChange?: TabBarProps["onVisibleTabsChange"];
+    visibleTabsStyle?: TabBarProps['visibleTabsStyle'];
+    arrowPosition?: TabBarProps['arrowPosition'];
+    renderArrow?: TabBarProps['renderArrow']
 }
 
 export interface TabBarProps {
@@ -48,6 +54,7 @@ export interface TabBarProps {
     collapsible?: boolean;
     list?: Array<PlainTab>;
     onTabClick?: (activeKey: string, event: MouseEvent<Element>) => void;
+    showRestInDropdown?: boolean;
     size?: TabSize;
     style?: CSSProperties;
     tabBarExtraContent?: ReactNode;
@@ -58,7 +65,12 @@ export interface TabBarProps {
     closable?: boolean;
     deleteTabItem?: (tabKey: string, event: MouseEvent<Element>) => void;
     handleKeyDown?: (event: React.KeyboardEvent, itemKey: string, closable: boolean) => void;
-    more?: TabsProps['more']
+    more?: TabsProps['more'];
+    onVisibleTabsChange?: (visibleState: Map<string, boolean>) => void;
+    visibleTabsStyle?: CSSProperties;
+    arrowPosition?: OverflowListProps['overflowRenderDirection'];
+    renderArrow?: (items: OverflowItem[], pos: "start"|"end", handleArrowClick: () => void) => ReactNode
+
 }
 
 export interface TabPaneProps {

+ 8 - 0
packages/semi-ui/tooltip/index.tsx

@@ -457,6 +457,14 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
             },
             setId: () => {
                 this.setState({ id: getUuidShort() });
+            },
+            getTriggerDOM: ()=>{
+                if (this.triggerEl.current) {
+                    return ReactDOM.findDOMNode(this.triggerEl.current as ReactInstance) as HTMLElement;
+                } else {
+                    return null;
+                }
+
             }
         };
     }

+ 21 - 0
packages/semi-ui/tree/__test__/treeMultiple.test.js

@@ -821,4 +821,25 @@ describe('Tree', () => {
         expect(spyOnChange.calledWithMatch(['fish', 'Yazhou'])).toEqual(true);
         tree.unmount();
     })
+
+    it('onChange + autoMergeValue', () => {
+        let spyOnSelect = sinon.spy(() => { });
+        let spyOnChange = sinon.spy(() => { });
+        let treeSelect = getTree({
+            defaultExpandAll: true,
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+            autoMergeValue: false,
+        });
+        let nodeChina = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`).at(0);
+        // select China
+        nodeChina.simulate('click');
+        // onSelect & onChange
+        expect(spyOnSelect.calledOnce).toBe(true);
+        expect(spyOnChange.calledOnce).toBe(true);
+        expect(spyOnSelect.calledWithMatch('zhongguo', true, { key: "zhongguo" })).toEqual(true);
+        expect(spyOnChange.calledWithMatch(
+            ['Zhongguo', 'Beijing', 'Shanghai'],
+        )).toEqual(true);
+    });
 })

+ 22 - 0
packages/semi-ui/tree/_story/tree.stories.jsx

@@ -3116,4 +3116,26 @@ export const CustomRenderIcon = () => {
       treeData={treeDataWithSuffix}
       icon={iconFunc}
   />);
+}
+
+export const AutoMerge = () => {
+  const [value, setValue] = useState([]);
+
+  const onChange = useCallback((val) => {
+    console.log('onChange', val);
+    setValue(val);
+  }, []);
+
+  return (
+    <>
+      <Tree
+        autoMergeValue={false}
+        style={{ width: 300}}
+        multiple
+        value={value}
+        onChange={onChange}
+        treeData={treeData1}
+      />
+    </>
+  )
 }

+ 2 - 0
packages/semi-ui/tree/index.tsx

@@ -52,6 +52,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
     static contextType = ConfigContext;
 
     static propTypes = {
+        autoMergeValue: PropTypes.bool,
         blockNode: PropTypes.bool,
         className: PropTypes.string,
         showClear: PropTypes.bool,
@@ -140,6 +141,7 @@ class Tree extends BaseComponent<TreeProps, TreeState> {
         draggable: false,
         autoExpandWhenDragEnter: true,
         checkRelation: 'related',
+        autoMergeValue: true,
     };
 
     static TreeNode: typeof TreeNode;

+ 2 - 1
packages/semi-ui/tree/interface.ts

@@ -84,7 +84,8 @@ export interface TreeProps extends BasicTreeProps {
     onSelect?: (selectedKey: string, selected: boolean, selectedNode: TreeNodeData) => void;
     renderDraggingNode?: (nodeInstance: HTMLElement, node: TreeNodeData) => HTMLElement;
     renderFullLabel?: (renderFullLabelProps: RenderFullLabelProps) => ReactNode;
-    renderLabel?: (label?: ReactNode, treeNode?: TreeNodeData) => ReactNode
+    renderLabel?: (label?: ReactNode, treeNode?: TreeNodeData) => ReactNode;
+    autoMergeValue?: boolean
 }
 export interface OptionProps {
     index: number;

+ 24 - 0
packages/semi-ui/treeSelect/__test__/treeMultiple.test.js

@@ -1129,4 +1129,28 @@ describe('TreeSelect', () => {
         let selectContentNode = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-select-selection`).at(0);
         expect(selectContentNode.find(`.${BASE_CLASS_PREFIX}-tag-content`).instance().textContent).toEqual('中国');
     });
+
+    it('onChange + autoMergeValue', () => {
+        let spyOnSelect = sinon.spy(() => { });
+        let spyOnChange = sinon.spy(() => { });
+        let treeSelect = getTreeSelect({
+            defaultExpandAll: true,
+            onSelect: spyOnSelect,
+            onChange: spyOnChange,
+            autoMergeValue: false,
+        });
+        let nodeChina = treeSelect.find(`.${BASE_CLASS_PREFIX}-tree-option.${BASE_CLASS_PREFIX}-tree-option-level-2`).at(0);
+        // select China
+        nodeChina.simulate('click');
+        // onSelect & onChange
+        expect(spyOnSelect.calledOnce).toBe(true);
+        expect(spyOnChange.calledOnce).toBe(true);
+        expect(spyOnSelect.calledWithMatch('zhongguo', true, { key: "zhongguo" })).toEqual(true);
+        expect(spyOnChange.calledWithMatch(
+            ['Zhongguo', 'Beijing', 'Shanghai'],
+        )).toEqual(true);
+
+        let tagGroup = treeSelect.find(`.${BASE_CLASS_PREFIX}-tag`);
+        expect(tagGroup.length).toEqual(3);
+    });
 })

+ 22 - 0
packages/semi-ui/treeSelect/_story/treeSelect.stories.jsx

@@ -2791,3 +2791,25 @@ export const CustomSelectAll = () => {
     </>  
   );
 };
+
+export const AutoMerge = () => {
+  const [value, setValue] = useState([]);
+
+  const onChange = useCallback((val) => {
+    console.log('onChange', val);
+    setValue(val);
+  }, []);
+
+  return (
+    <>
+      <TreeSelect
+        autoMergeValue={false}
+        style={{ width: 300}}
+        multiple
+        value={value}
+        onChange={onChange}
+        treeData={treeData1}
+      />
+    </>
+  )
+}

+ 26 - 38
packages/semi-ui/treeSelect/index.tsx

@@ -150,7 +150,8 @@ export interface TreeSelectProps extends Omit<BasicTreeSelectProps, OverrideComm
     onChange?: OnChange;
     onFocus?: (e: React.MouseEvent) => void;
     onVisibleChange?: (isVisible: boolean) => void;
-    onClear?: (e: React.MouseEvent | React.KeyboardEvent<HTMLDivElement>) => void
+    onClear?: (e: React.MouseEvent | React.KeyboardEvent<HTMLDivElement>) => void;
+    autoMergeValue?: boolean
 }
 
 export type OverrideCommonState =
@@ -271,6 +272,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         restTagsPopoverProps: PropTypes.object,
         preventScroll: PropTypes.bool,
         clickTriggerToHide: PropTypes.bool,
+        autoMergeValue: PropTypes.bool,
     };
 
     static defaultProps: Partial<TreeSelectProps> = {
@@ -304,6 +306,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         showRestTagsPopover: false,
         restTagsPopoverProps: {},
         clickTriggerToHide: true,
+        autoMergeValue: true,
     };
     inputRef: React.RefObject<typeof Input>;
     tagInputRef: React.RefObject<TagInput>;
@@ -859,15 +862,14 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         return showClear && (this.hasValue() || triggerSearchHasInputValue) && !disabled && (isOpen || isHovering);
     };
 
-    renderTagList = () => {
-        const { checkedKeys, keyEntities, disabledKeys, realCheckedKeys } = this.state;
+    renderTagList = (triggerRenderKeys: string[]) => {
+        const { keyEntities, disabledKeys } = this.state;
         const {
             treeNodeLabelProp,
             leafOnly,
             disabled,
             disableStrictly,
             size,
-            checkRelation,
             renderSelectedItem: propRenderSelectedItem,
             keyMaps
         } = this.props;
@@ -878,14 +880,8 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 isRenderInTag: true,
                 content: get(item, realLabelName, null)
             });
-        let renderKeys = [];
-        if (checkRelation === 'related') {
-            renderKeys = normalizeKeyList([...checkedKeys], keyEntities, leafOnly, true);
-        } else if (checkRelation === 'unRelated' && Object.keys(keyEntities).length > 0) {
-            renderKeys = [...realCheckedKeys];
-        }
         const tagList: Array<React.ReactNode> = [];
-        renderKeys.forEach((key: TreeNodeData['key'], index) => {
+        triggerRenderKeys.forEach((key: TreeNodeData['key'], index) => {
             const item = (keyEntities[key] && keyEntities[key].key === key) ? keyEntities[key].data : this.getDataForKeyNotInKeyEntities(key);
             const onClose = (tagContent: any, e: React.MouseEvent) => {
                 if (e && typeof e.preventDefault === 'function') {
@@ -950,7 +946,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         );
     };
 
-    renderSelectContent = () => {
+    renderSelectContent = (triggerRenderKeys: string[]) => {
         const {
             multiple,
             placeholder,
@@ -963,7 +959,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         const isTriggerPositionSearch = filterTreeNode && searchPosition === strings.SEARCH_POSITION_TRIGGER;
         // searchPosition = trigger
         if (isTriggerPositionSearch) {
-            return multiple ? this.renderTagInput() : this.renderSingleTriggerSearch();
+            return multiple ? this.renderTagInput(triggerRenderKeys) : this.renderSingleTriggerSearch();
         }
         // searchPosition = dropdown and single seleciton
         if (!multiple || !this.hasValue()) {
@@ -974,7 +970,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
             return <span className={spanCls}>{renderText ? renderText : placeholder}</span>;
         }
         // searchPosition = dropdown and multiple seleciton
-        const tagList = this.renderTagList();
+        const tagList = this.renderTagList(triggerRenderKeys);
         // mode=custom to return tagList directly
         return (
             <TagGroup<'custom'>
@@ -1071,6 +1067,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
             searchPosition,
             triggerRender,
             borderless,
+            autoMergeValue,
             checkRelation,
             ...rest
         } = this.props;
@@ -1110,17 +1107,19 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 className
             );
         let inner: React.ReactNode | React.ReactNode[];
-        if (useCustomTrigger) {
-            let triggerRenderKeys = [];
-            if (multiple) {
-                if (checkRelation === 'related') {
-                    triggerRenderKeys = normalizeKeyList([...checkedKeys], keyEntities, leafOnly, true);
-                } else if (checkRelation === 'unRelated') {
-                    triggerRenderKeys = [...realCheckedKeys];
-                }
-            } else {
-                triggerRenderKeys = selectedKeys;
+        let triggerRenderKeys = [];
+        if (multiple) {
+            if (!autoMergeValue) {
+                triggerRenderKeys =[...checkedKeys];
+            } else if (checkRelation === 'related') {
+                triggerRenderKeys = normalizeKeyList([...checkedKeys], keyEntities, leafOnly, true);
+            } else if (checkRelation === 'unRelated') {
+                triggerRenderKeys = [...realCheckedKeys];
             }
+        } else {
+            triggerRenderKeys = selectedKeys;
+        }
+        if (useCustomTrigger) {
             inner = <Trigger
                 inputValue={inputValue}
                 value={triggerRenderKeys.map((key: string) => get(keyEntities, [key, 'data']))}
@@ -1137,7 +1136,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
             inner = [
                 <Fragment key={'prefix'}>{prefix || insetLabel ? this.renderPrefix() : null}</Fragment>,
                 <Fragment key={'selection'}>
-                    <div className={`${prefixcls}-selection`}>{this.renderSelectContent()}</div>
+                    <div className={`${prefixcls}-selection`}>{this.renderSelectContent(triggerRenderKeys)}</div>
                 </Fragment>,
                 <Fragment key={'suffix'}>{suffix ? this.renderSuffix() : null}</Fragment>,
                 <Fragment key={'clearBtn'}>
@@ -1238,15 +1237,13 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
         );
     };
 
-    renderTagInput = () => {
+    renderTagInput = (triggerRenderKeys: string[]) => {
         const {
-            leafOnly,
             disabled,
             size,
             searchAutoFocus,
             placeholder,
             maxTagCount,
-            checkRelation,
             showRestTagsPopover,
             restTagsPopoverProps,
             searchPosition,
@@ -1254,17 +1251,8 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
             preventScroll
         } = this.props;
         const {
-            keyEntities,
-            checkedKeys,
             inputValue,
-            realCheckedKeys,
         } = this.state;
-        let keyList = [];
-        if (checkRelation === 'related') {
-            keyList = normalizeKeyList(checkedKeys, keyEntities, leafOnly, true);
-        } else if (checkRelation === 'unRelated') {
-            keyList = [...realCheckedKeys];
-        }
         // auto focus search input divide into two parts
         // 1. filterTreeNode && searchPosition === strings.SEARCH_POSITION_TRIGGER
         //    Implemented by passing autofocus to the underlying input's autofocus
@@ -1280,7 +1268,7 @@ class TreeSelect extends BaseComponent<TreeSelectProps, TreeSelectState> {
                 onInputChange={v => this.search(v)}
                 ref={this.tagInputRef}
                 placeholder={placeholder}
-                value={keyList}
+                value={triggerRenderKeys}
                 inputValue={inputValue}
                 size={size}
                 showRestTagsPopover={showRestTagsPopover}

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-webpack-plugin",
-    "version": "2.60.1",
+    "version": "2.61.0-beta.0",
     "description": "",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",

+ 153 - 153
sitemap.xml

@@ -2,22 +2,22 @@
 <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     <url>
         <loc>https://juejin.cn/post/7267418854124699702</loc>
-        <lastmod>2024-06-19T03:10:52.172Z</lastmod>
+        <lastmod>2024-06-18T12:09:36.424Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://medium.com/front-end-weekly/how-we-test-semi-design-component-libraries-64b854f63b65</loc>
-        <lastmod>2024-06-19T03:10:53.074Z</lastmod>
+        <lastmod>2024-06-18T12:09:35.359Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://mp.weixin.qq.com/s/noHoWRuA25PgqFNcurhIUA</loc>
-        <lastmod>2024-06-19T03:10:55.190Z</lastmod>
+        <lastmod>2024-06-18T12:09:38.497Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://mp.weixin.qq.com/s/O3js-SZDNPEOjGxh-aAkbw</loc>
-        <lastmod>2024-06-19T03:11:11.266Z</lastmod>
+        <lastmod>2024-06-18T12:09:38.135Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
@@ -382,9 +382,9 @@
     </url>
     <url>
         <loc>https://semi.design/design/zh-CN/handbook/tools</loc>
-        <lastmod>2024-04-26T09:30:27.147Z</lastmod>
+        <lastmod>2024-06-18T12:09:42.475Z</lastmod>
         <changefreq>weekly</changefreq>
-        <scm>1.0.0.133</scm>
+        <scm>1.0.0.130</scm>
     </url>
     <url>
         <loc>https://semi.design/design/zh-CN/input/button</loc>
@@ -436,7 +436,7 @@
     </url>
     <url>
         <loc>https://semi.design/design/zh-CN/input/rating</loc>
-        <lastmod>2024-06-19T03:10:59.497Z</lastmod>
+        <lastmod>2024-06-18T12:09:42.451Z</lastmod>
         <changefreq>weekly</changefreq>
         <scm>1.0.0.133</scm>
     </url>
@@ -526,9 +526,9 @@
     </url>
     <url>
         <loc>https://semi.design/design/zh-CN/navigation/tree</loc>
-        <lastmod>2024-04-17T09:31:24.104Z</lastmod>
+        <lastmod>2024-06-18T12:09:43.633Z</lastmod>
         <changefreq>weekly</changefreq>
-        <scm>1.0.0.133</scm>
+        <scm>1.0.0.130</scm>
     </url>
     <url>
         <loc>https://semi.design/design/zh-CN/show/avatar</loc>
@@ -790,77 +790,77 @@
     </url>
     <url>
         <loc>https://semi.design/en-US/basic/divider</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/basic/grid</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/basic/icon</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/basic/layout</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/basic/space</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/basic/tokens</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/basic/typography</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/feedback/banner</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/feedback/notification</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/feedback/popconfirm</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/feedback/progress</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/feedback/skeleton</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/feedback/spin</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/feedback/toast</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/autocomplete</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
@@ -870,362 +870,362 @@
     </url>
     <url>
         <loc>https://semi.design/en-US/input/cascader</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/checkbox</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/datepicker</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/form</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/input</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/inputnumber</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/radio</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/rating</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/select</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/slider</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/switch</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/taginput</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/timepicker</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/transfer</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/treeselect</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/input/upload</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/navigation/anchor</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/navigation/backtop</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/navigation/breadcrumb</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/navigation/navigation</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/navigation/pagination</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/navigation/steps</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/navigation/tabs</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/navigation/tree</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/other/configprovider</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/other/locale</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/avatar</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/badge</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/calendar</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/card</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/carousel</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/collapse</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/collapsible</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/descriptions</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/dropdown</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/empty</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/highlight</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/image</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/list</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/modal</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/overflowlist</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/popover</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/scrolllist</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/sidesheet</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/table</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/tag</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/timeline</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/show/tooltip</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/start/accessibility</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/start/changelog</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/start/customize-theme</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/start/dark-mode</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/start/faq</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/start/getting-started</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/start/introduction</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/start/overview</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/en-US/start/update-to-v2</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/basic/divider</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/basic/grid</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/basic/icon</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/basic/layout</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/basic/space</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/basic/tokens</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/basic/typography</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/banner</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/notification</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/popconfirm</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/progress</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/skeleton</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/spin</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/feedback/toast</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/autocomplete</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
@@ -1235,287 +1235,287 @@
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/cascader</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/checkbox</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/datepicker</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/form</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/input</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/inputnumber</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/radio</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/rating</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/select</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/slider</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/switch</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/taginput</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/timepicker</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/transfer</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/treeselect</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/input/upload</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/anchor</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/backtop</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/breadcrumb</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/navigation</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/pagination</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/steps</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/tabs</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/navigation/tree</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/other/configprovider</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/other/locale</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/avatar</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/badge</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/calendar</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/card</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/carousel</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/collapse</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/collapsible</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/descriptions</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/dropdown</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/empty</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/highlight</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/image</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/list</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/modal</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/overflowlist</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/popover</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/scrolllist</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/sidesheet</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/table</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/tag</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/timeline</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/show/tooltip</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/start/accessibility</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/start/changelog</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/start/customize-theme</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/start/dark-mode</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/start/faq</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/start/getting-started</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/start/introduction</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/start/overview</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
     <url>
         <loc>https://semi.design/zh-CN/start/update-to-v2</loc>
-        <lastmod>2024-06-19T03:07:12.000Z</lastmod>
+        <lastmod>2024-06-18T12:04:08.000Z</lastmod>
         <changefreq>weekly</changefreq>
     </url>
 </urlset>

+ 92 - 0
yarn.lock

@@ -1534,11 +1534,25 @@
     "@douyinfe/semi-animation-styled" "2.23.2"
     classnames "^2.2.6"
 
+"@douyinfe/[email protected]":
+  version "2.59.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.59.1.tgz#66ef6a26a0064c4785d2a69877c0c939d5b55a88"
+  integrity sha512-TImalOl13V8QeHEOOAdwqQbwt3F59lwAFBcKvsymqnquWhAyBhOXTRaaIZSziv4sKGn1gXD7Cs7N5fBBY+pMog==
+  dependencies:
+    "@douyinfe/semi-animation" "2.59.1"
+    "@douyinfe/semi-animation-styled" "2.59.1"
+    classnames "^2.2.6"
+
 "@douyinfe/[email protected]":
   version "2.23.2"
   resolved "https://registry.npmjs.org/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.23.2.tgz#f18bc074515441c297cc636ed98521e249d093c9"
   integrity sha512-cKaA1yGHPF76Rx7EZDZicj+1oX1su2wnqb/UGFOTquAwqWmkTfgQ+EKxCd/N704WH+RtmGf4xbrJKpBvvcEdSQ==
 
+"@douyinfe/[email protected]":
+  version "2.59.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.59.1.tgz#af8db2ebaae4fa1ed948ffd5596563e50663e67c"
+  integrity sha512-41MtvA+FJCnPoWjY1RWLz3vkJ4HP42QriUUJ+ZmRMQYUQo0dXuE2uSCa47pMaMLAvEHoY0CFoIUnYUGzdonSjw==
+
 "@douyinfe/[email protected]":
   version "2.12.0"
   resolved "https://registry.npmjs.org/@douyinfe/semi-animation/-/semi-animation-2.12.0.tgz#51fe52d3911c2591a80a6e9fe96e6809c1511f13"
@@ -1554,6 +1568,13 @@
   dependencies:
     bezier-easing "^2.1.0"
 
+"@douyinfe/[email protected]":
+  version "2.59.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.59.1.tgz#2b2ddacb87ff29ccb0b198f1641f3229db1d306e"
+  integrity sha512-vrYRe9aT5FhNl/ghjLGE4L3T29Y1nuvKQPqptde5XMhPkpGkH3A/4IjO8dXvfipdXdhZGcXyWYbtL9gknsm+rQ==
+  dependencies:
+    bezier-easing "^2.1.0"
+
 "@douyinfe/[email protected]":
   version "2.33.1"
   resolved "https://registry.npmjs.org/@douyinfe/semi-foundation/-/semi-foundation-2.33.1.tgz#1dfe6233e35a4ed768cb580b0c9a677d1c34ffba"
@@ -1568,6 +1589,21 @@
     memoize-one "^5.2.1"
     scroll-into-view-if-needed "^2.2.24"
 
+"@douyinfe/[email protected]":
+  version "2.59.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.59.1.tgz#e7d9fb01f99a3a786c1726f8fa67af7e9f07c919"
+  integrity sha512-t2CXa/5zLiBimHnmF1v/UWZgUoE9rB7SFR2qi649xyahpLQx6H9ahrbo6oqwa8GDLnSWravTeSNlvoI8Z8if7Q==
+  dependencies:
+    "@douyinfe/semi-animation" "2.59.1"
+    async-validator "^3.5.0"
+    classnames "^2.2.6"
+    date-fns "^2.29.3"
+    date-fns-tz "^1.3.8"
+    fast-copy "^3.0.1 "
+    lodash "^4.17.21"
+    memoize-one "^5.2.1"
+    scroll-into-view-if-needed "^2.2.24"
+
 "@douyinfe/[email protected]", "@douyinfe/semi-icons@latest":
   version "2.33.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.33.1.tgz#8e2871d9bc0ab7e12df74e3c71802d53d69b7425"
@@ -1575,11 +1611,30 @@
   dependencies:
     classnames "^2.2.6"
 
+"@douyinfe/[email protected]":
+  version "2.59.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.59.1.tgz#de5695f4d01061b5ca35807ca6a9dfd652b276ff"
+  integrity sha512-XJ30Fsbnkq0T9Z7exYKiI+GM8q3Zee1gO28H5CckA0dFHxnpNyJ4QazzTuTj5dSXAZq3uYXakmNB8wDAgV9d9A==
+  dependencies:
+    classnames "^2.2.6"
+
+"@douyinfe/semi-icons@^2.0.0":
+  version "2.60.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.60.1.tgz#944de131585d4747f831cf4864ceb0d41d7d34c9"
+  integrity sha512-f6DxiYKUJaTaNnMFFIweQlKjTt5+m6SGZb09Z/2b9wtfqdulXTYMrmXXeQOzfLvTE4QFZb0CfR9DsCfiH6oyxQ==
+  dependencies:
+    classnames "^2.2.6"
+
 "@douyinfe/[email protected]":
   version "2.33.1"
   resolved "https://registry.npmjs.org/@douyinfe/semi-illustrations/-/semi-illustrations-2.33.1.tgz#530ab851f4dc32a52221c4067c778c800b9b55d7"
   integrity sha512-tTTUN8QwnQiF++sk4VBNzfkG87aYZ4iUeqk2ys8/ymVUmCZQ7y46ys020GO1MfPHRR47OMFPI82FVcH1WQtE3g==
 
+"@douyinfe/[email protected]":
+  version "2.59.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.59.1.tgz#2874a681805dbad83b2024496cfe58b46396eef0"
+  integrity sha512-EzbM+FAV862tdXNIbVwoSCfGLeIcT37/Vzm+q9cfx+DYMBqeQEQcH5qLOpRGFc4Hf796bxJtGfE/jFmDC2xdDw==
+
 "@douyinfe/[email protected]":
   version "2.23.2"
   resolved "https://registry.npmjs.org/@douyinfe/semi-scss-compile/-/semi-scss-compile-2.23.2.tgz#30884bb194ee9ae1e81877985e5663c3297c1ced"
@@ -1653,6 +1708,38 @@
   dependencies:
     glob "^7.1.6"
 
+"@douyinfe/[email protected]":
+  version "2.59.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.59.1.tgz#8afca4da013fab673377ca06147ea463f717e45b"
+  integrity sha512-XuB6jf5Ob0ufyFJ2xKpdNTbB0/LM8Fe9hWOIMEmux2eiHwnMzCQvfrOHphCy/g1+TbGlGK9N6kJnCyUKP+U/AQ==
+
+"@douyinfe/semi-ui@^2.0.0":
+  version "2.59.1"
+  resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.59.1.tgz#6784816155f0d81d47ac65d6203a4c7540409a33"
+  integrity sha512-zF5Fe0d8NrNxR8tVoMiuhlnp5J6/5PvnV0kr0iBOsqW89peboF0HPNg/lr3DvzoPpCWCN++bZZAFdMAB/WGplQ==
+  dependencies:
+    "@dnd-kit/core" "^6.0.8"
+    "@dnd-kit/sortable" "^7.0.2"
+    "@dnd-kit/utilities" "^3.2.1"
+    "@douyinfe/semi-animation" "2.59.1"
+    "@douyinfe/semi-animation-react" "2.59.1"
+    "@douyinfe/semi-foundation" "2.59.1"
+    "@douyinfe/semi-icons" "2.59.1"
+    "@douyinfe/semi-illustrations" "2.59.1"
+    "@douyinfe/semi-theme-default" "2.59.1"
+    async-validator "^3.5.0"
+    classnames "^2.2.6"
+    copy-text-to-clipboard "^2.1.1"
+    date-fns "^2.29.3"
+    date-fns-tz "^1.3.8"
+    fast-copy "^3.0.1 "
+    lodash "^4.17.21"
+    prop-types "^15.7.2"
+    react-resizable "^3.0.5"
+    react-window "^1.8.2"
+    scroll-into-view-if-needed "^2.2.24"
+    utility-types "^3.10.0"
+
 "@douyinfe/semi-ui@latest":
   version "2.33.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.33.1.tgz#3234ca96eb3560b8299bc9750fbe59446522d9bb"
@@ -11666,6 +11753,11 @@ eslint-plugin-react@^7.20.6, eslint-plugin-react@^7.24.0:
     semver "^6.3.0"
     string.prototype.matchall "^4.0.8"
 
+eslint-plugin-semi-design@^2.33.0:
+  version "2.60.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-semi-design/-/eslint-plugin-semi-design-2.60.1.tgz#f93c328835ba7379dd55e264852d6e67bcde20d0"
+  integrity sha512-7rUY4P7gUkhU7q/xdjMa+yrFPmO1y5yEUG1v+7MPfe4sP6uo8qtf96VrIhCN+OAeHvN6jlgEIpuoRBt4d/gJDA==
+
 eslint-rule-composer@^0.3.0:
   version "0.3.0"
   resolved "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"