Browse Source

Merge branch 'release'

zhangyumei.0319 2 years ago
parent
commit
7227c4cfbe
76 changed files with 2246 additions and 666 deletions
  1. 65 65
      content/input/cascader/index.md
  2. 2 2
      content/input/datepicker/index-en-US.md
  3. 2 2
      content/input/datepicker/index.md
  4. 509 253
      content/input/form/index-en-US.md
  5. 42 31
      content/input/form/index.md
  6. 75 0
      content/input/transfer/index-en-US.md
  7. 75 0
      content/input/transfer/index.md
  8. 15 1
      content/other/locale/index-en-US.md
  9. 14 1
      content/other/locale/index.md
  10. 14 0
      content/start/changelog/index-en-US.md
  11. 15 0
      content/start/changelog/index.md
  12. 11 5
      cypress/integration/slider.spec.js
  13. 1 1
      lerna.json
  14. 1 1
      packages/semi-animation-react/package.json
  15. 1 1
      packages/semi-animation-styled/package.json
  16. 1 1
      packages/semi-animation/package.json
  17. 1 1
      packages/semi-eslint-plugin/package.json
  18. 1 1
      packages/semi-foundation/datePicker/foundation.ts
  19. 13 3
      packages/semi-foundation/datePicker/inputFoundation.ts
  20. 2 2
      packages/semi-foundation/datePicker/monthsGridFoundation.ts
  21. 1 1
      packages/semi-foundation/form/form.scss
  22. 1 1
      packages/semi-foundation/package.json
  23. 6 1
      packages/semi-foundation/slider/slider.scss
  24. 13 9
      packages/semi-foundation/slider/variables.scss
  25. 1 1
      packages/semi-icons/package.json
  26. 1 1
      packages/semi-illustrations/package.json
  27. 1 1
      packages/semi-next/package.json
  28. 1 1
      packages/semi-scss-compile/package.json
  29. 1 1
      packages/semi-theme-default/package.json
  30. 1 1
      packages/semi-ui/cascader/_story/cascader.stories.jsx
  31. 1 0
      packages/semi-ui/datePicker/__test__/datePicker.test.js
  32. 60 0
      packages/semi-ui/datePicker/_story/v2/FeatInsetInputProps.tsx
  33. 1 0
      packages/semi-ui/datePicker/_story/v2/index.js
  34. 9 5
      packages/semi-ui/datePicker/dateInput.tsx
  35. 3 3
      packages/semi-ui/datePicker/datePicker.tsx
  36. 1 0
      packages/semi-ui/datePicker/index.tsx
  37. 6 5
      packages/semi-ui/datePicker/quickControl.tsx
  38. 83 2
      packages/semi-ui/form/_story/DynamicField/arrayFieldDemo.jsx
  39. 186 0
      packages/semi-ui/form/_story/InputGroup/groupProps.jsx
  40. 1 56
      packages/semi-ui/form/_story/Layout/layoutDemo.jsx
  41. 21 27
      packages/semi-ui/form/_story/form.stories.jsx
  42. 18 4
      packages/semi-ui/form/group.tsx
  43. 269 40
      packages/semi-ui/locale/_story/locale.stories.jsx
  44. 2 3
      packages/semi-ui/locale/interface.ts
  45. 6 7
      packages/semi-ui/locale/source/ar.ts
  46. 3 4
      packages/semi-ui/locale/source/de.ts
  47. 4 5
      packages/semi-ui/locale/source/en_GB.ts
  48. 4 5
      packages/semi-ui/locale/source/en_US.ts
  49. 3 4
      packages/semi-ui/locale/source/es.ts
  50. 3 4
      packages/semi-ui/locale/source/fr.ts
  51. 3 4
      packages/semi-ui/locale/source/id_ID.ts
  52. 3 4
      packages/semi-ui/locale/source/it.ts
  53. 3 4
      packages/semi-ui/locale/source/ja_JP.ts
  54. 3 4
      packages/semi-ui/locale/source/ko_KR.ts
  55. 3 4
      packages/semi-ui/locale/source/ms_MY.ts
  56. 177 0
      packages/semi-ui/locale/source/nl_NL.ts
  57. 178 0
      packages/semi-ui/locale/source/pl_PL.ts
  58. 3 4
      packages/semi-ui/locale/source/pt_BR.ts
  59. 6 7
      packages/semi-ui/locale/source/ro.ts
  60. 7 8
      packages/semi-ui/locale/source/ru_RU.ts
  61. 175 0
      packages/semi-ui/locale/source/sv_SE.ts
  62. 3 4
      packages/semi-ui/locale/source/th_TH.ts
  63. 2 3
      packages/semi-ui/locale/source/tr_TR.ts
  64. 3 4
      packages/semi-ui/locale/source/vi_VN.ts
  65. 3 4
      packages/semi-ui/locale/source/zh_CN.ts
  66. 3 4
      packages/semi-ui/locale/source/zh_TW.ts
  67. 7 7
      packages/semi-ui/package.json
  68. 11 7
      packages/semi-ui/pagination/index.tsx
  69. 0 5
      packages/semi-ui/slider/__test__/__snapshots__/slider.test.js.snap
  70. 0 14
      packages/semi-ui/slider/__test__/slider.test.js
  71. 0 4
      packages/semi-ui/slider/index.tsx
  72. 5 1
      packages/semi-ui/tooltip/index.tsx
  73. 37 1
      packages/semi-ui/transfer/_story/transfer.stories.jsx
  74. 35 4
      packages/semi-ui/transfer/index.tsx
  75. 1 1
      packages/semi-webpack/package.json
  76. 4 1
      src/templates/scope.js

+ 65 - 65
content/input/cascader/index.md

@@ -1161,7 +1161,7 @@ import { Cascader, Typography } from '@douyinfe/semi-ui';
             placeholder="请选择所在地区"
             bottomSlot={
                 <div style={slotStyle}>
-                    <Text>找不相关选项?</Text>
+                    <Text>找不相关选项?</Text>
                     <Text link>去新建</Text>
                 </div>
             }
@@ -1698,62 +1698,62 @@ function Demo() {
 
 ### Cascader
 
-| 属性      | 说明     | 类型    | 默认值  | 版本    |
-| ---------| ---------| -------| -----  |--------|
-| arrowIcon | 自定义右侧下拉箭头 Icon,当 showClear 开关打开且当前有选中值时,hover 会优先显示 clear icon | ReactNode | - | 1.15.0 |
-| autoAdjustOverflow | 是否自动调整下拉框展开方向,用于边缘遮挡时自动调整展开方向 | boolean | true | - |
-| autoMergeValue | 设置自动合并 value。具体而言是,开启后,当某个父节点被选中时,value 将不包括该节点的子孙节点。不支持动态切换 | boolean | true | 1.28.0  |
-| bottomSlot | 底部插槽 | ReactNode | - | 1.27.0 |
-| changeOnSelect | 是否允许选择非叶子节点 | boolean | false | - |
-| className | 选择框的 className 属性 | string | - | - |
-| clearIcon | 可用于自定义清除按钮, showClear为true时有效 | ReactNode | - | 2.25.0  |
-| defaultOpen | 设置是否默认打开下拉菜单  | boolean | false | - |
-| defaultValue | 指定默认选中的条目  | string\|number\|CascaderData\|(string\|number\|CascaderData)[]| - | - |
-| disabled | 是否禁用 | boolean | false | -  |
-| displayProp | 设置回填选项显示的属性值 | string  | `label` | - |
-| displayRender | 设置回填格式 | (selected: string[] \| Entity, idx?: number) => ReactNode | selected => selected.join('/') | - |
-| dropdownMargin | 下拉菜单计算溢出时的增加的冗余值,详见[issue#549](https://github.com/DouyinFE/semi-design/issues/549),作用同 Tooltip margin  | object\|number | - | 2.25.0 |
-| dropdownClassName | 下拉菜单的 className 属性 | string | - | - |
-| dropdownStyle | 下拉菜单的样式 | object | - | - |
-| emptyContent | 当搜索无结果时展示的内容 | ReactNode | `暂无数据` | - |
-| filterLeafOnly |  搜索结果是否只展示叶子结点路径 | boolean  | true | 1.26.0 |
-| filterRender | 自定义渲染筛选后的选项 | (props: FilterRenderProps) => ReactNode; | - | 2.28.0 |
-| filterSorter | 对筛选后的选项进行排序 | (first: CascaderData, second: CascaderData, inputValue: string) => number | - | 2.28.0 |
-| filterTreeNode | 设置筛选,默认用 treeNodeFilterProp 的值作为要筛选的 TreeNode 的属性值, data 参数自 v2.28.0 开始提供 | ((inputValue: string, treeNodeString: string, data?: CascaderData) => boolean) \| boolean | false | - |
-| getPopupContainer | 指定父级 DOM,下拉框将会渲染至该 DOM 中,自定义需要设置 position: relative |() => HTMLElement|() => document.body | - |
-| insetLabel | 前缀标签别名,主要用于 Form | ReactNode | - | 0.28.0 |
-| leafOnly | 多选时设置 value 只包含叶子节点,即显示的 Tag 和 onChange 的 value 参数只包含叶子节点。不支持动态切换 | boolean | false | 2.2.0 |
-| loadData | 异步加载数据,需要返回一个Promise | (selectOptions: CascaderData[]) => Promise< void > |- | 1.8.0 |
-| max| 多选时,限制多选选中的数量,超出 max 后将触发 onExceed 回调 | number | - | 1.28.0  |
-| maxTagCount| 多选时,标签的最大展示数量,超出后将以 +N 形式展示| number | - | 1.28.0 |
-| motion | 设置下拉框弹出的动画 |boolean| true |-|
-| mouseEnterDelay | 鼠标移入后,延迟显示下拉框的时间,单位毫秒 | number | 50 | - |
-| mouseLeaveDelay | 鼠标移出后,延迟消失下拉框的时间,单位毫秒 | number | 50 | -   |
-| multiple | 设置多选 | boolean | false | 1.28.0 |
-| placeholder | 选择框默认文字 | string | - | - |
-| position | 方向,可选值:`top`,`topLeft`,`topRight`,`left`,`leftTop`,`leftBottom`,`right`,`rightTop`,`rightBottom`,`bottom`,`bottomLeft`,`bottomRight` | string | `bottom` | 2.16.0 |
-| prefix | 前缀标签 | ReactNode | - | 0.28.0 |
-| preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean | - | 2.15.0 |
-|restTagsPopoverProps |Popover 的配置属性,可以控制 position、zIndex、trigger 等,具体参考[Popover](/zh-CN/show/popover#API%20%E5%8F%82%E8%80%83) |PopoverProps | {} | 1.28.0 |
-| searchPlaceholder | 搜索框默认文字 | string | - | - |
-| separator | 自定义分隔符,包括:搜索时显示在下拉框的内容以及单选时回显到 Trigger 的内容的分隔符  | string |  `/`  | 2.2.0 |
-| showClear |  是否展示清除按钮 | boolean  | false | 0.35.0  |
-| showNext| 设置展开 Dropdown 子菜单的方式,可选: `click`、`hover` | string | `click` | 1.29.0 |
-| showRestTagsPopover| 当超过 maxTagCount,hover 到 +N 时,是否通过 Popover 显示剩余内容| boolean |false| 1.28.0 |
-| size | 选择框大小,可选 `large`,`small`,`default` | string | `default` | - |
-| stopPropagation | 是否阻止下拉框上的点击事件冒泡 | boolean | true | - |
-| disableStrictly | 设置是否开启严格禁用。开启后,当节点是 disabled 的时候,则不能通过子级或者父级的关系改变选中状态 | boolean | false | 1.32.0 |
-| style | 选择框的样式 | CSSProperties | -  | - |
-| suffix | 后缀标签 | ReactNode  | -   | 0.28.0 |
-| topSlot | 顶部插槽 | ReactNode | - | 1.27.0  |
-| treeData | 展示数据,具体属性参考 [CascaderData](#CascaderData) | CascaderData[] | [] | - |
-| treeNodeFilterProp | 搜索时输入项过滤对应的 CascaderData 属性 | string | `label` | - |
-| triggerRender | 自定义触发器渲染方法  | (triggerRenderData: object) => ReactNode | - | 0.34.0 |
-| validateStatus | trigger 的校验状态,仅影响展示样式。可选: default、error、warning | string | `default` | - |
-| value |(受控)选中的条目 | string\|number\|CascaderData\|(string\|number\|CascaderData)[] | - | - |
-| zIndex | 下拉菜单的 zIndex | number | 1030 | - |
-| enableLeafClick | 多选时,是否启动点击叶子节点选项触发勾选 | boolean | false | 2.2.0 |
-| onBlur | 失焦 Cascader 的回调 | (e: MouseEvent) => void | - | - |
+| 属性                 | 说明                                                                                                                                      | 类型                                                                                      | 默认值                         | 版本   |
+|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|--------------------------------|--------|
+| arrowIcon            | 自定义右侧下拉箭头 Icon,当 showClear 开关打开且当前有选中值时,hover 会优先显示 clear icon                                                 | ReactNode                                                                                 | -                              | 1.15.0 |
+| autoAdjustOverflow   | 是否自动调整下拉框展开方向,用于边缘遮挡时自动调整展开方向                                                                                 | boolean                                                                                   | true                           | -      |
+| autoMergeValue       | 设置自动合并 value。具体而言是,开启后,当某个父节点被选中时,value 将不包括该节点的子孙节点。不支持动态切换                                   | boolean                                                                                   | true                           | 1.28.0 |
+| bottomSlot           | 底部插槽                                                                                                                                  | ReactNode                                                                                 | -                              | 1.27.0 |
+| changeOnSelect       | 是否允许选择非叶子节点                                                                                                                    | boolean                                                                                   | false                          | -      |
+| className            | 选择框的 className 属性                                                                                                                   | string                                                                                    | -                              | -      |
+| clearIcon            | 可用于自定义清除按钮, showClear为true时有效                                                                                               | ReactNode                                                                                 | -                              | 2.25.0 |
+| defaultOpen          | 设置是否默认打开下拉菜单                                                                                                                  | boolean                                                                                   | false                          | -      |
+| defaultValue         | 指定默认选中的条目                                                                                                                        | string\|number\|CascaderData\|(string\|number\|CascaderData)[]                            | -                              | -      |
+| disabled             | 是否禁用                                                                                                                                  | boolean                                                                                   | false                          | -      |
+| displayProp          | 设置回填选项显示的属性值                                                                                                                  | string                                                                                    | `label`                        | -      |
+| displayRender        | 设置回填格式                                                                                                                              | (selected: string[] \| Entity, idx?: number) => ReactNode                                 | selected => selected.join('/') | -      |
+| dropdownMargin       | 下拉菜单计算溢出时的增加的冗余值,详见[issue#549](https://github.com/DouyinFE/semi-design/issues/549),作用同 Tooltip margin                | object\|number                                                                            | -                              | 2.25.0 |
+| dropdownClassName    | 下拉菜单的 className 属性                                                                                                                 | string                                                                                    | -                              | -      |
+| dropdownStyle        | 下拉菜单的样式                                                                                                                            | object                                                                                    | -                              | -      |
+| emptyContent         | 当搜索无结果时展示的内容                                                                                                                  | ReactNode                                                                                 | `暂无数据`                     | -      |
+| filterLeafOnly       | 搜索结果是否只展示叶子结点路径                                                                                                            | boolean                                                                                   | true                           | 1.26.0 |
+| filterRender         | 自定义渲染筛选后的选项                                                                                                                    | (props: FilterRenderProps) => ReactNode;                                                  | -                              | 2.28.0 |
+| filterSorter         | 对筛选后的选项进行排序                                                                                                                    | (first: CascaderData, second: CascaderData, inputValue: string) => number                 | -                              | 2.28.0 |
+| filterTreeNode       | 设置筛选,默认用 treeNodeFilterProp 的值作为要筛选的 TreeNode 的属性值, data 参数自 v2.28.0 开始提供                                       | ((inputValue: string, treeNodeString: string, data?: CascaderData) => boolean) \| boolean | false                          | -      |
+| getPopupContainer    | 指定父级 DOM,下拉框将会渲染至该 DOM 中,自定义需要设置 position: relative                                                                  | () => HTMLElement                                                                         | () => document.body            | -      |
+| insetLabel           | 前缀标签别名,主要用于 Form                                                                                                                | ReactNode                                                                                 | -                              | 0.28.0 |
+| leafOnly             | 多选时设置 value 只包含叶子节点,即显示的 Tag 和 onChange 的 value 参数只包含叶子节点。不支持动态切换                                       | boolean                                                                                   | false                          | 2.2.0  |
+| loadData             | 异步加载数据,需要返回一个Promise                                                                                                          | (selectOptions: CascaderData[]) => Promise< void >                                        | -                              | 1.8.0  |
+| max                  | 多选时,限制多选选中的数量,超出 max 后将触发 onExceed 回调                                                                                 | number                                                                                    | -                              | 1.28.0 |
+| maxTagCount          | 多选时,标签的最大展示数量,超出后将以 +N 形式展示                                                                                          | number                                                                                    | -                              | 1.28.0 |
+| motion               | 设置下拉框弹出的动画                                                                                                                      | boolean                                                                                   | true                           | -      |
+| mouseEnterDelay      | 鼠标移入后,延迟显示下拉框的时间,单位毫秒                                                                                                  | number                                                                                    | 50                             | -      |
+| mouseLeaveDelay      | 鼠标移出后,延迟消失下拉框的时间,单位毫秒                                                                                                  | number                                                                                    | 50                             | -      |
+| multiple             | 设置多选                                                                                                                                  | boolean                                                                                   | false                          | 1.28.0 |
+| placeholder          | 选择框默认文字                                                                                                                            | string                                                                                    | -                              | -      |
+| position             | 方向,可选值:`top`,`topLeft`,`topRight`,`left`,`leftTop`,`leftBottom`,`right`,`rightTop`,`rightBottom`,`bottom`,`bottomLeft`,`bottomRight` | string                                                                                    | `bottom`                       | 2.16.0 |
+| prefix               | 前缀标签                                                                                                                                  | ReactNode                                                                                 | -                              | 0.28.0 |
+| preventScroll        | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法                                                                      | boolean                                                                                   | -                              | 2.15.0 |
+| restTagsPopoverProps | Popover 的配置属性,可以控制 position、zIndex、trigger 等,具体参考[Popover](/zh-CN/show/popover#API%20%E5%8F%82%E8%80%83)                    | PopoverProps                                                                              | {}                             | 1.28.0 |
+| searchPlaceholder    | 搜索框默认文字                                                                                                                            | string                                                                                    | -                              | -      |
+| separator            | 自定义分隔符,包括:搜索时显示在下拉框的内容以及单选时回显到 Trigger 的内容的分隔符                                                         | string                                                                                    | `/`                            | 2.2.0  |
+| showClear            | 是否展示清除按钮                                                                                                                          | boolean                                                                                   | false                          | 0.35.0 |
+| showNext             | 设置展开 Dropdown 子菜单的方式,可选: `click`、`hover`                                                                                      | string                                                                                    | `click`                        | 1.29.0 |
+| showRestTagsPopover  | 当超过 maxTagCount,hover 到 +N 时,是否通过 Popover 显示剩余内容                                                                           | boolean                                                                                   | false                          | 1.28.0 |
+| size                 | 选择框大小,可选 `large`,`small`,`default`                                                                                                 | string                                                                                    | `default`                      | -      |
+| stopPropagation      | 是否阻止下拉框上的点击事件冒泡                                                                                                            | boolean                                                                                   | true                           | -      |
+| disableStrictly      | 设置是否开启严格禁用。开启后,当节点是 disabled 的时候,则不能通过子级或者父级的关系改变选中状态                                             | boolean                                                                                   | false                          | 1.32.0 |
+| style                | 选择框的样式                                                                                                                              | CSSProperties                                                                             | -                              | -      |
+| suffix               | 后缀标签                                                                                                                                  | ReactNode                                                                                 | -                              | 0.28.0 |
+| topSlot              | 顶部插槽                                                                                                                                  | ReactNode                                                                                 | -                              | 1.27.0 |
+| treeData             | 展示数据,具体属性参考 [CascaderData](#CascaderData)                                                                                       | CascaderData[]                                                                            | []                             | -      |
+| treeNodeFilterProp   | 搜索时输入项过滤对应的 CascaderData 属性                                                                                                  | string                                                                                    | `label`                        | -      |
+| triggerRender        | 自定义触发器渲染方法                                                                                                                      | (triggerRenderData: object) => ReactNode                                                  | -                              | 0.34.0 |
+| validateStatus       | trigger 的校验状态,仅影响展示样式。可选: default、error、warning                                                                             | string                                                                                    | `default`                      | -      |
+| value                | (受控)选中的条目                                                                                                                          | string\|number\|CascaderData\|(string\|number\|CascaderData)[]                            | -                              | -      |
+| zIndex               | 下拉菜单的 zIndex                                                                                                                         | number                                                                                    | 1030                           | -      |
+| enableLeafClick      | 多选时,是否启动点击叶子节点选项触发勾选                                                                                                   | boolean                                                                                   | false                          | 2.2.0  |
+| onBlur               | 失焦 Cascader 的回调                                                                                                                      | (e: MouseEvent) => void                                                                   | -                              | -      |
 | onChange | 选中树节点时调用此函数,默认返回选中项 path 的 value 数组 | (value: string\|number\| CascaderData |(string\|number\|CascaderData)[]) => void | - | - |
 | onChangeWithObject | 是否将选中项 option 的其他属性作为回调。设为 true 时,onChange 的入参类型会从 string/number 变为 TreeNode。此时如果是受控,也需要把 value 设置成 CascaderData 类型,且必须含有 value 的键值,defaultValue 同理 | boolean | false | 1.16.0 |
 | onClear| showClear 为 true 时,点击清空按钮触发的回调 | () => void | - | 1.29.0 |
@@ -1767,14 +1767,14 @@ function Demo() {
 
 ### CascaderData
 
-| 属性      | 说明                  | 类型            | 默认值 |
-| -------- | --------------------- | -------------- | ----- |
-| children | 子节点                 | CascaderData[]     | -     |
-| disabled | 不可选状态 **>=0.35.0** | boolean        | -     |
-| isLeaf   | 叶子节点                | boolean        | -     |
-| label    | 展示的文本(必填)       | ReactNode       | -     |
-| loading  | 正在加载                | boolean        | -     |
-| value    | 属性值(必填)           | string\|number | -     |
+| 属性     | 说明                    | 类型           | 默认值 |
+|----------|-----------------------|----------------|--------|
+| children | 子节点                  | CascaderData[] | -      |
+| disabled | 不可选状态 **>=0.35.0** | boolean        | -      |
+| isLeaf   | 叶子节点                | boolean        | -      |
+| label    | 展示的文本(必填)        | ReactNode      | -      |
+| loading  | 正在加载                | boolean        | -      |
+| value    | 属性值(必填)            | string\|number | -      |
 
 ## Accessibility
 

+ 2 - 2
content/input/datepicker/index-en-US.md

@@ -872,7 +872,7 @@ function Demo() {
 | autoSwitchDate     | When the year and month are changed through the left and right buttons and the drop-down menu at the top of the panel, the date is automatically switched. Only valid for `date` type. | boolean   | true    | **1.13.0** |
 | bottomSlot         | Render the bottom extra area                                                                                                                                                           | ReactNode |         | **1.22.0** |
 | className          | Class name                                                                                                                                                                             | string    | -       |            |
-| clearIcon | Can be used to customize the clear button, valid when showClear is true | ReactNode |  |**2.25.0** |
+| clearIcon          | Can be used to customize the clear button, valid when showClear is true                                                                                                                | ReactNode |         | **2.25.0** |
 | defaultOpen        | Panel displays or hides by default                                                                                                                                                     | boolean   | false   |            |
 | defaultPickerValue | Default panel date                                                                                                                                                                     | ValueType |         |            |
 | defaultValue       | Default value                                                                                                                                                                            | ValueType                                                                                                                                                                                                    |                                             |                           |  |
@@ -888,7 +888,7 @@ function Demo() {
 | format             | Date string format displayed in the input box                                                                                                                                             | string                                                                                                                                                                                                    | Corresponding to type: For details, see [Date and Time Format](#Date%20and%20Time%20Format) |                           |
 | getPopupContainer | Specifies the parent DOM, and the bullet layer will be rendered to the DOM, you need to set 'position: relative` | function():HTMLElement | () = > document.body |
 | inputReadOnly      | Is the text box readonly                                                                                                                                                                  | boolean                                                                                                                                                                                                   | false                                                                                 |                           |
-| insetInput        | Whether the input box is embedded in the panel                                                                                                                                            | boolean                                                                                                                                                                                                   | false                                                                                 | **2.7.0**                          |
+| insetInput        | Whether the input box is embedded in the panel. InsetInputProps type supported after v2.29                                                                                                                                | boolean  \| <ApiType detail='{ placeholder?: { dateStart?: string; dateEnd?: string; timeStart?: string; timeEnd?: string } }'>InsetInputProps</ApiType>                                                                                                                                                                                                | false                                                                                 | **2.7.0**                          |
 | inputStyle         | Input box style                                                                                                                                                                           | object                                                                                                                                                                                                    |                                                                                       |                           |
 | insetLabel         | Prefix label, lower priority than `prefix`                                                                                                                                                | string\|ReactNode                                                                                                                                                                                         |                                                                                       |                           |
 | max                | When multiple is set to true, the number of selected, non-pass or value is null\|undefined, unlimited.                                                                                     | number                                                                                                                                                                                                    | -                                                                                     |                           |

+ 2 - 2
content/input/datepicker/index.md

@@ -835,7 +835,7 @@ function Demo() {
 | autoSwitchDate     | 通过面板上方左右按钮、下拉菜单更改年月时,自动切换日期。仅对 date type 生效。 | boolean   | true    | **1.13.0** |
 | bottomSlot         | 渲染底部额外区域                                                          | ReactNode |         | **1.22.0** |
 | className          | 类名                                                                      | string    | -       |            |
-| clearIcon | 可用于自定义清除按钮, showClear为true时有效 | ReactNode |   | **2.25.0**|
+| clearIcon          | 可用于自定义清除按钮, showClear为true时有效                               | ReactNode |         | **2.25.0** |
 | defaultOpen        | 面板默认显示或隐藏                                                        | boolean   | false   |            |
 | defaultPickerValue | 默认面板日期                                                              | ValueType |         |            |
 | defaultValue       | 默认值                                                                    | ValueType |         |            |
@@ -851,7 +851,7 @@ function Demo() {
 | format | 在输入框内展现的日期串格式 | string | 与 type 对应:详见[日期时间格式](#日期时间格式) |  |
 | getPopupContainer | 指定父级 DOM,弹层将会渲染至该 DOM 中,自定义需要设置 `position: relative` | function():HTMLElement | () => document.body |  |
 | hideDisabledOptions | 隐藏禁止选择的时间 | boolean | false |  |
-| insetInput | 面板中是否嵌入输入框 | boolean | false |  | **2.7.0**
+| insetInput | 面板中是否嵌入输入框,InsetInputProps 类型 v2.29 支持  | boolean \| <ApiType detail='{ placeholder?: { dateStart?: string; dateEnd?: string; timeStart?: string; timeEnd?: string } }'>InsetInputProps</ApiType>  | false | **2.7.0** |
 | inputReadOnly | 文本框是否 readonly | boolean | false |  |
 | inputStyle | 输入框样式 | object |  |  |
 | insetLabel | 前缀标签,优先级低于 `prefix` | string\|ReactNode |  |  |

File diff suppressed because it is too large
+ 509 - 253
content/input/form/index-en-US.md


+ 42 - 31
content/input/form/index.md

@@ -183,11 +183,6 @@ import { Form } from '@douyinfe/semi-ui';
 
 ### 已支持的表单控件
 
-> Form.TreeSelect、Form.Cascader、Form.Rating 在 v0.22.0 及之后的版本开始提供;  
-> Form.AutoComplete 在 v0.28.0 及之后的版本开始提供  
-> Form.Upload 在 v1.0.0 及之后的版本开始提供  
-> Form.TagInput 在 v1.21.0 及之后的版本开始提供  
-
 ```jsx live=true dir="column"
 import React from 'react';
 import { Form, Col, Row, Button } from '@douyinfe/semi-ui';
@@ -234,11 +229,8 @@ class BasicDemoWithInit extends React.Component {
                 ]
             }
         };
-        this.getFormApi = this.getFormApi.bind(this);
     }
 
-    getFormApi(formApi) { this.formApi = formApi; }
-
     render() {
         const { Section, Input, InputNumber, AutoComplete, Select, TreeSelect, Cascader, DatePicker, TimePicker, TextArea, CheckboxGroup, Checkbox, RadioGroup, Radio, Slider, Rating, Switch, TagInput } = Form;
         const { initValues } = this.state;
@@ -278,7 +270,6 @@ class BasicDemoWithInit extends React.Component {
 
         return (
             <Form
-                getFormApi={this.getFormApi}
                 initValues={initValues}
                 style={{ padding: 10, width: '100%' }}
                 onValueChange={(v)=>console.log(v)}
@@ -408,8 +399,8 @@ class BasicDemoWithInit extends React.Component {
                                 { type: 'boolean' },
                                 { required: true, message: '必须选择是否独占 ' }
                             ]}>
-                                <Radio value={true}>是</Radio>
-                                <Radio value={false}>否</Radio>
+                                <Radio value={1}>是</Radio>
+                                <Radio value={0}>否</Radio>
                             </RadioGroup>
                         </Col>
                     </Row>
@@ -948,7 +939,7 @@ import { Form } from '@douyinfe/semi-ui';
                 validateStatus={validateStatus}
                 helpText={helpText}
                 extraText={
-                    <div 
+                    <div
                         style={{
                             color: 'var(--semi-color-link)',
                             fontSize: 14,
@@ -1506,6 +1497,7 @@ import { Form, Button } from '@douyinfe/semi-ui';
 
 针对动态增删的数组类表单项,我们提供了 ArrayField 作用域来简化 add/remove 的操作  
 ArrayField 自带了 add、remove、addWithInitValue 等 api 用来执行新增行,删除行,新增带有初始值的行等操作  
+ArrayField 详细的 API请查阅下方 [ArrayField Props](#arrayfield-props)
 注意:ArrayField 的 initValue 类型必须是数组
 
 ```jsx live=true dir="column" hideInDSM
@@ -1517,15 +1509,15 @@ class ArrayFieldDemo extends React.Component {
     constructor() {
         super();
         this.state = {
-            menu: [
-                { name: '脸部贴纸', type: '2D' },
-                { name: '前景贴纸', type: '3D' },
+            data: [
+                { name: 'Semi D2C', role: 'Engineer' },
+                { name: 'Semi C2D', role: 'Designer' },
             ]
         };
     }
 
     render() {
-        let { menu } = this.state;
+        let { data } = this.state;
         const ComponentUsingFormState = () => {
             const formState = useFormState();
             return (
@@ -1533,30 +1525,38 @@ class ArrayFieldDemo extends React.Component {
             );
         };
         return (
-            <Form style={{ width: 500 }} labelPosition='left' labelWidth='220px' allowEmpty>
-                <ArrayField field='effects' initValue={menu}>
+            <Form style={{ width: 800 }} labelPosition='left' labelWidth='100px' allowEmpty>
+                <ArrayField field='rules' initValue={data}>
                     {({ add, arrayFields, addWithInitValue }) => (
                         <React.Fragment>
-                            <Button onClick={add} icon={<IconPlusCircle />} theme='light'>新增空白行</Button>
-                            <Button icon={<IconPlusCircle />} onClick={() => {addWithInitValue({ name: '自定义贴纸', type: '2D' });}} style={{ marginLeft: 8 }}>新增带有初始值的行</Button>
+                            <Button onClick={add} icon={<IconPlusCircle />} theme='light'>Add new line</Button>
+                            <Button icon={<IconPlusCircle />} onClick={() => {addWithInitValue({ name: 'Semi DSM', type: 'Designer' });}} style={{ marginLeft: 8 }}>Add new line with init value</Button>
                             {
                                 arrayFields.map(({ field, key, remove }, i) => (
                                     <div key={key} style={{ width: 1000, display: 'flex' }}>
                                         <Form.Input
                                             field={`${field}[name]`}
-                                            label={`特效类型:(${field}.name`}
+                                            label={`${field}.name`}
                                             style={{ width: 200, marginRight: 16 }}
                                         >
                                         </Form.Input>
                                         <Form.Select
-                                            field={`${field}[type]`}
-                                            label={`素材类型:(${field}.type)`}
-                                            style={{ width: 90 }}
+                                            field={`${field}[role]`}
+                                            label={`${field}.role`}
+                                            style={{ width: 120 }}
+                                            optionList={[
+                                                { label: 'Engineer', value: 'Engineer' },
+                                                { label: 'Designer', value: 'Designer' },
+                                            ]}
                                         >
-                                            <Form.Select.Option value='2D'>2D</Form.Select.Option>
-                                            <Form.Select.Option value='3D'>3D</Form.Select.Option>
                                         </Form.Select>
-                                        <Button type='danger' theme='borderless' icon={<IconMinusCircle />} onClick={remove} style={{ margin: 12 }}></Button>
+                                        <Button
+                                            type='danger'
+                                            theme='borderless'
+                                            icon={<IconMinusCircle />}
+                                            onClick={remove}
+                                            style={{ margin: 12 }}
+                                        />
                                     </div>
                                 ))
                             }
@@ -2127,9 +2127,20 @@ const { Label } = Form;
 | width     | label 宽度               | number/string    |        |  |
 | optional  | 是否自动在text后追加"(可选)"文字标识(根据Locale配置的不同语言自动切换相同语义文本)。当该项为true时,required的\*号将不再展示。若当表单项多数均为必填时,仅强调可选项会更使得整体视觉更简洁  | boolean    | false | v2.18.0 |
 
-## Form.Slot
+## Form.InputGroup
+
+| 属性             | 说明                                                      | 类型                     | 默认值 | 版本 |
+| ---------------- | --------------------------------------------------------- | ------------------------ |--- |--- |
+| className        | 样式类名                                                  | string                   | |
+| style            | 内联样式                                                  | object                   ||
+| label            | InputGroup 的 label 标签文本                      |  Label \| string                 | |
+| labelPosition    | 该表单控件的 label 位置,可选'top'/'left'/'inset'。在 Form 与 InputGroup 同时传入时,以 InputGroup props为准 | string     | 'top'|
+| extraText        | 额外的提示信息,当需要错误信息和提示文案同时出现时,可以使用这个,位于 errorMessage 后 | ReactNode | | v2.29.0 |
+| extraTextPosition| 控制extraText的显示位置,可选`middle`(垂直方向以Label、extraText、Group的顺序显示)、`bottom` (垂直方向以Label、Group、extraText的顺序显示)| string | 'bottom' | v2.29.0|
 
-> Form.Slot 在 v0.27.0 开始提供
+当 extraTextPositon 为 middle,且 labelPosition 为 left时。由于 extraText允许为 ReactNode,内容高度不定,Label将不再确保能与 Field / InputGroup 中的首行文本对齐。 
+
+## Form.Slot
 
 ```jsx
 import { Form } from '@douyinfe/semi-ui';
@@ -2147,8 +2158,6 @@ const { Slot } = Form;
 
 ## Form.ErrorMessage
 
-> Form.ErrorMessage 在 v0.27.0 开始提供
-
 ```jsx
 import { Form } from '@douyinfe/semi-ui';
 const { ErrorMessage } = Form;
@@ -2165,6 +2174,8 @@ const { ErrorMessage } = Form;
 | showValidateIcon | 是否自动加上 validateStatus 对应的 icon                       | boolean                  |
 | validateStatus   | 信息所属的校验状态,可选 default/error/warning/success(success一般建议与default样式相同) | string                  |
 
+
+
 ## withFieldOption
 
 | key               | 描述                                                                                                                                                                                                                                          | 默认值     |

+ 75 - 0
content/input/transfer/index-en-US.md

@@ -350,6 +350,79 @@ import { IconHandle, IconClose } from '@douyinfe/semi-icons';
 };
 ```
 
+### Custom rendering header information in panel
+
+Semi has provided `renderSourceHeader` and `renderSelectedHeader` parameter allows users to customize the header information of the left and right panels since version 2.29.0.   
+`renderSourceHeader: (props: SourceHeaderProps) => ReactNode`   
+`renderSelectedHeader: (props: SelectedHeaderProps) => ReactNode`   
+The parameter types are as follows:
+
+```ts
+type SourceHeaderProps = {
+    num: number; // The total number of data or the number of filtered results
+    showButton: boolean; // Whether to show select all/unselect all buttons
+    allChecked: boolean; // Whether the current data has been selected
+    onAllClick: () => void // Function that should be called after clicking the select/unselect all button
+}
+
+type SelectedHeaderProps = {
+    num: number; // The total number of selected data
+    showButton: boolean; // Whether to show the clear button
+    onClear: () => void // Function that should be called after clicking the clear button
+}
+```
+
+The example is as follows:
+
+```jsx live=true dir="column"
+import React from 'react';
+import { Transfer, Button } from '@douyinfe/semi-ui';
+
+() => {
+    const data = Array.from({ length: 30 }, (v, i) => {
+        return {
+            label: `Item ${i}`,
+            value: i,
+            disabled: false,
+            key: i,
+        };
+    });
+
+    const renderSourceHeader = (props) => {
+        const { num, showButton, allChecked, onAllClick } = props;
+        return <div style={{ margin: '10px 0 0 10px', height: 24, display: 'flex', alignItems: 'center' }}>
+            <span>Total {num} items</span>
+            {showButton && <Button
+                theme="borderless"
+                type="tertiary"
+                size="small" 
+                onClick={onAllClick}>{ allChecked ? 'Unselect all' : 'Select all' }</Button>}
+        </div>;
+    };
+
+    const renderSelectedHeader = (props) => {
+        const { num, showButton, onClear } = props;
+        return <div style={{ margin: '10px 0 0 10px', height: 24, display: 'flex', alignItems: 'center' }}>
+            <span>{num} items selected</span>
+            {showButton && <Button
+                theme="borderless"
+                type="tertiary"
+                size="small"
+                onClick={onClear}>Clear</Button>}
+        </div>;
+    };
+
+    return (
+        <Transfer 
+            style={{ width: 568, height: 416 }}
+            dataSource={data}
+            renderSourceHeader={renderSourceHeader}
+            renderSelectedHeader={renderSelectedHeader}
+        />
+    );
+};
+```
+
 ### Fully custom rendering
 
 Semi provides `renderSourcePanel` and `renderSelectedPanel` input parameters, allowing you to completely customize the rendering structure of the left and right panels
@@ -931,8 +1004,10 @@ import { Transfer } from '@douyinfe/semi-ui';
 | onDeselect | Callback when unchecking | (item: Item) => void | | |
 | onSearch | Called when the input content of the search box changes | (inputValue: string) => void | | |
 | onSelect | Callback when checked | (item: Item) => void | | |
+| renderSelectedHeader | Customize the rendering of the header information on the right panel | (props: SelectedHeaderProps) => ReactNode |  | 2.29.0 |
 | renderSelectedItem | Customize the rendering of a single selected item on the right | (item: {onRemove, sortableHandle} & Item) => ReactNode | | |
 | renderSelectedPanel | Customize the rendering of the selected panel on the right | (selectedPanelProps) => ReactNode | | 1.11.0 |
+| renderSourceHeader | Customize the rendering of the header information on the left panel | (props: SourceHeaderProps) => ReactNode |  | 2.29.0 |
 | renderSourceItem | Customize the rendering of a single candidate item on the left | (item: {onChange, checked} & Item) => ReactNode | | |
 | renderSourcePanel | Customize the rendering of the left candidate panel | (sourcePanelProps) => ReactNode | | 1.11.0 |
 | showPath | When the type is `treeList`, control whether the selected item on the right shows the selection path | boolean | false | 1.20.0 |

+ 75 - 0
content/input/transfer/index.md

@@ -352,6 +352,79 @@ import { IconHandle, IconClose } from '@douyinfe/semi-icons';
 };
 ```
 
+### 自定义渲染面板头部信息
+
+Semi 自 2.29.0 版本提供 `renderSourceHeader`, `renderSelectedHeader` 参数允许用户自定义渲染左右两个面板的头部信息。   
+`renderSourceHeader: (props: SourceHeaderProps) => ReactNode`   
+`renderSelectedHeader: (props: SelectedHeaderProps) => ReactNode`   
+参数类型如下:
+
+```ts
+type SourceHeaderProps = {
+    num: number; // 数据总数或筛选结果数目
+    showButton: boolean; // 是否展示全选/取消全选按钮
+    allChecked: boolean; // 当前数据是否已全选
+    onAllClick: () => void // 点击全选/取消全选按钮后应调用的函数
+}
+
+type SelectedHeaderProps = {
+    num: number; // 已选中数据总数
+    showButton: boolean; // 是否展示清空按钮
+    onClear: () => void // 点击清空按钮后应调用的函数
+}
+```
+
+使用示例如下
+
+```jsx live=true dir="column"
+import React from 'react';
+import { Transfer, Button } from '@douyinfe/semi-ui';
+
+() => {
+    const data = Array.from({ length: 30 }, (v, i) => {
+        return {
+            label: `选项名称 ${i}`,
+            value: i,
+            disabled: false,
+            key: i,
+        };
+    });
+
+    const renderSourceHeader = (props) => {
+        const { num, showButton, allChecked, onAllClick } = props;
+        return <div style={{ margin: '10px 0 0 10px', height: 24, display: 'flex', alignItems: 'center' }}>
+            <span>共 {num} 项</span>
+            {showButton && <Button
+                theme="borderless"
+                type="tertiary"
+                size="small" 
+                onClick={onAllClick}>{ allChecked ? '取消全选' : '全选' }</Button>}
+        </div>;
+    };
+
+    const renderSelectedHeader = (props) => {
+        const { num, showButton, onClear } = props;
+        return <div style={{ margin: '10px 0 0 10px', height: 24, display: 'flex', alignItems: 'center' }}>
+            <span>{num} 项已选</span>
+            {showButton && <Button
+                theme="borderless"
+                type="tertiary"
+                size="small"
+                onClick={onClear}>清空</Button>}
+        </div>;
+    };
+
+    return (
+        <Transfer
+            style={{ width: 568, height: 416 }}
+            dataSource={data}
+            renderSourceHeader={renderSourceHeader}
+            renderSelectedHeader={renderSelectedHeader}
+        />
+    );
+};
+```
+
 ### 完全自定义渲染
 
 Semi 提供了 `renderSourcePanel`、`renderSelectedPanel` 入参,允许你完全自定义左右侧两个面板的渲染结构  
@@ -933,8 +1006,10 @@ import { Transfer } from '@douyinfe/semi-ui';
 | onDeselect | 取消勾选时的回调 | (item: Item) => void | |  |
 | onSearch | 搜索框输入内容变化时调用 | (inputValue: string) => void | |  |
 | onSelect | 勾选时的回调 | (item: Item) => void | |  |
+| renderSelectedHeader | 自定义右侧面板头部信息的渲染 | (props: SelectedHeaderProps) => ReactNode |  | 2.29.0 |
 | renderSelectedItem | 自定义右侧单个已选项的渲染 | (item: { onRemove, sortableHandle } & Item) => ReactNode |  |  |
 | renderSelectedPanel | 自定义右侧已选面板的渲染 | (selectedPanelProps) => ReactNode |  | 1.11.0 |
+| renderSourceHeader | 自定义左侧面板头部信息的渲染 | (props: SourceHeaderProps) => ReactNode |  | 2.29.0 |
 | renderSourceItem | 自定义左侧单个候选项的渲染 | (item: { onChange, checked } & Item) => ReactNode |  |  |
 | renderSourcePanel | 自定义左侧候选面板的渲染 | (sourcePanelProps) => ReactNode |  | 1.11.0 |
 | showPath | 当 type 为`treeList`时,控制右侧选中项是否显示选择路径 | boolean | false | 1.20.0 |

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

@@ -23,6 +23,8 @@ brief: Internationalized components to provide multilingual support for Semi com
 | v2.2.0     | Spanish: es       |
 | v2.15.0     | Italian: it、French:fr、German:de   |
 | v2.21.0     | Romanian: ro   |
+| v2.29.0     | Swedish: sv_SE、 Polish: pl_PL、Dutch: nl_NL |
+
 
 ## Components supported
 
@@ -48,6 +50,9 @@ import th_TH from '@douyinfe/semi-ui/lib/es/locale/source/th_TH';
 import tr_TR from '@douyinfe/semi-ui/lib/es/locale/source/tr_TR';
 import pt_BR from '@douyinfe/semi-ui/lib/es/locale/source/pt_BR';
 import zh_TW from '@douyinfe/semi-ui/lib/es/locale/source/zh_TW';
+import sv_SE from '@douyinfe/semi-ui/lib/es/locale/source/sv_SE';
+import pl_PL from '@douyinfe/semi-ui/lib/es/locale/source/pl_PL';
+import nl_NL from '@douyinfe/semi-ui/lib/es/locale/source/nl_NL';
 import ar from '@douyinfe/semi-ui/lib/es/locale/source/ar';
 import es from '@douyinfe/semi-ui/lib/es/locale/source/es';
 import it from '@douyinfe/semi-ui/lib/es/locale/source/it';
@@ -186,6 +191,9 @@ import th_TH from '@douyinfe/semi-ui/lib/es/locale/source/th_TH';
 import tr_TR from '@douyinfe/semi-ui/lib/es/locale/source/tr_TR';
 import pt_BR from '@douyinfe/semi-ui/lib/es/locale/source/pt_BR';
 import zh_TW from '@douyinfe/semi-ui/lib/es/locale/source/zh_TW';
+import sv_SE from '@douyinfe/semi-ui/lib/es/locale/source/sv_SE';
+import pl_PL from '@douyinfe/semi-ui/lib/es/locale/source/pl_PL';
+import nl_NL from '@douyinfe/semi-ui/lib/es/locale/source/nl_NL';
 import ar from '@douyinfe/semi-ui/lib/es/locale/source/ar';
 import es from '@douyinfe/semi-ui/lib/es/locale/source/es';
 import it from '@douyinfe/semi-ui/lib/es/locale/source/it';
@@ -218,6 +226,9 @@ class I18nDemo extends React.Component {
             'ms_MY': ms_MY,
             'th_TH': th_TH,
             'tr_TR': tr_TR,
+            'sv_SE': sv_SE,
+            'pl_PL': pl_PL,
+            'nl_NL': nl_NL,
             es,
             de,
             it,
@@ -303,7 +314,7 @@ class I18nDemo extends React.Component {
             return (
                 <>
                     <h5>Pagination</h5>
-                    <Pagination total={100} showTotal showSizeChanger style={style} />
+                    <Pagination total={100} showTotal showSizeChanger style={style} showQuickJumper/>
                     <h5>Modal</h5>
 
                     <div style={style}>
@@ -424,6 +435,9 @@ class I18nDemo extends React.Component {
                         <Select.Option value='it'>Italian</Select.Option>
                         <Select.Option value='fr'>French</Select.Option>
                         <Select.Option value='ro'>Romanian</Select.Option>
+                        <Select.Option value='sv_SE'>Swedish</Select.Option>
+                        <Select.Option value='pl_PL'>Polish</Select.Option>
+                        <Select.Option value='nl_NL'>Dutch</Select.Option>
                     </Select>
                 </div>
                 <LocaleProvider locale={locale}>

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

@@ -22,6 +22,7 @@ brief: 国际化组件,为 Semi 组件提供多语言支持
 | v2.2.0     | 西班牙语: es       |
 | v2.15.0     | 意大利语: it、法语:fr、德语:de   |
 | v2.21.0     | 罗马尼亚语: ro   |
+| v2.29.0     | 瑞典语: sv_SE、波兰语: pl_PL 、荷兰语: nl_NL |
 ## 已支持组件
 
 > DatePicker、TimePicker、Modal、Pagination、Select、Table、Cascader、Calendar、TreeSelect、List、Typography、Transfer、Nav、Upload、Form、Navigation、Image
@@ -46,6 +47,9 @@ import th_TH from '@douyinfe/semi-ui/lib/es/locale/source/th_TH';
 import tr_TR from '@douyinfe/semi-ui/lib/es/locale/source/tr_TR';
 import pt_BR from '@douyinfe/semi-ui/lib/es/locale/source/pt_BR';
 import zh_TW from '@douyinfe/semi-ui/lib/es/locale/source/zh_TW';
+import sv_SE from '@douyinfe/semi-ui/lib/es/locale/source/sv_SE';
+import pl_PL from '@douyinfe/semi-ui/lib/es/locale/source/pl_PL';
+import nl_NL from '@douyinfe/semi-ui/lib/es/locale/source/nl_NL';
 import ar from '@douyinfe/semi-ui/lib/es/locale/source/ar';
 import es from '@douyinfe/semi-ui/lib/es/locale/source/es';
 import it from '@douyinfe/semi-ui/lib/es/locale/source/it';
@@ -189,6 +193,9 @@ import th_TH from '@douyinfe/semi-ui/lib/es/locale/source/th_TH';
 import tr_TR from '@douyinfe/semi-ui/lib/es/locale/source/tr_TR';
 import pt_BR from '@douyinfe/semi-ui/lib/es/locale/source/pt_BR';
 import zh_TW from '@douyinfe/semi-ui/lib/es/locale/source/zh_TW';
+import sv_SE from '@douyinfe/semi-ui/lib/es/locale/source/sv_SE';
+import pl_PL from '@douyinfe/semi-ui/lib/es/locale/source/pl_PL';
+import nl_NL from '@douyinfe/semi-ui/lib/es/locale/source/nl_NL';
 import es from '@douyinfe/semi-ui/lib/es/locale/source/es';
 import it from '@douyinfe/semi-ui/lib/es/locale/source/it';
 import de from '@douyinfe/semi-ui/lib/es/locale/source/de';
@@ -223,6 +230,9 @@ class I18nDemo extends React.Component {
             'pt_BR': pt_BR,
             'zh_TW': zh_TW,
             'es': es,
+            'sv_SE': sv_SE,
+            'pl_PL': pl_PL,
+            'nl_NL': nl_NL,
             de,
             it,
             fr,
@@ -307,7 +317,7 @@ class I18nDemo extends React.Component {
             return (
                 <>
                     <h5>Pagination</h5>
-                    <Pagination total={100} showTotal showSizeChanger style={style} />
+                    <Pagination total={100} showTotal showSizeChanger style={style} showQuickJumper />
                     <h5>Modal</h5>
 
                     <div style={style}>
@@ -430,6 +440,9 @@ class I18nDemo extends React.Component {
                         <Select.Option value='it'>意大利语</Select.Option>
                         <Select.Option value='fr'>法语</Select.Option>
                         <Select.Option value='ro'>罗马尼亚语</Select.Option>
+                        <Select.Option value='sv_SE'>瑞典语</Select.Option>
+                        <Select.Option value='pl_PL'>波兰语</Select.Option>
+                        <Select.Option value='nl_NL'>荷兰语</Select.Option>
                     </Select>
                 </div>
                 <LocaleProvider locale={locale}>

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

@@ -16,6 +16,20 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
 
 ---
 
+#### 🎉 2.29.0-beta.0 (2023-02-06)
+- 【feat】
+    - Form.InputGroup support extraText, extraTextPosition,  [#1313 ](https://github.com/DouyinFE/semi-design/issues/1313)
+    - DatePicker insetInput supports passing placeholder  [#1343](https://github.com/DouyinFE/semi-design/issues/1343)
+    - Transfer added renderSourceHeader and renderSelectedHeader APIs to allow users to customize the header information of the left and right panels [#1403](https://github.com/DouyinFE/semi-design/issues/1403)
+    - Locale add Swedish: sv_SE、 Polish: pl_PL、Dutch: nl_NL [#1410](https://github.com/DouyinFE/semi-design/issues/1410)
+- 【fix】
+    - Fix the problem that the DatePicker panel is not updated after entering the date  [#1398](https://github.com/DouyinFE/semi-design/issues/1398)
+    - fix when the visible prop changes, tooltips whose trigger is not hover/focus also delay showing/hiding [@marshcat0](https://github.com/marshcat0)
+    - Optimize the problem of displaying singular and plural text in Russian, Arabic, and Romanian, involving components Pagination, Transfer, and Calendar [#1411](https://github.com/DouyinFE/semi-design/pull/1411)
+- 【docs】
+    - Improve Form english document 
+- 【Design Token】
+    - Slider add ` $spacing-slider_handle-translateY`、`$spacing-slider_vertical_handle-translateX`、`$spacing-slider_dot-translateX`、`$spacing-slider_vertical_dot-translateY` Token,which is used to control the horizontal and vertical state handles and value scale lines horizontal and vertical offset [#1391](https://github.com/DouyinFE/semi-design/pull/1391)
 #### 🎉 2.28.1 (2023-01-31)
 - 【Fix】
     - Fix DatePicker panel shifting bug when selecting date range [#1221](https://github.com/DouyinFE/semi-design/issues/1221)

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

@@ -16,6 +16,21 @@ Semi 版本号遵循 **Semver** 规范(主版本号-次版本号-修订版本
 
 ---
 
+#### 🎉 2.29.0-beta.0 (2023-02-06)
+- 【Feat】
+    - Form.InputGroup 支持配置 extraText, extraTextPosition,对齐 Field Component [#1313](https://github.com/DouyinFE/semi-design/issues/1313)
+    - DatePicker insetInput 输入框支持传入 placeholder [#1343](https://github.com/DouyinFE/semi-design/issues/1343)
+    - Transfer 新增 renderSourceHeader,renderSelectedHeader 支持用户能够自定义左右面板头部信息 [#1403](https://github.com/DouyinFE/semi-design/issues/1403)
+    - Locale 增加瑞典语: sv_SE、波兰语: pl_PL 、荷兰语: nl_NL支持 [#1410](https://github.com/DouyinFE/semi-design/issues/1410)
+- 【Fix】
+    - 修复 DatePicker 输入日期后面板未更新问题 [#1398](https://github.com/DouyinFE/semi-design/issues/1398)
+    - 修复 visible 属性变化时,trigger 不为 hover 和 focus 的 tooltip 也延迟了展示/隐藏的问题 [@marshcat0](https://github.com/marshcat0)
+    - 优化 俄语、阿拉伯语、罗马尼亚语种下单复数文本显示问题,涉及组件 Pagination、Transfer、Calendar [#1411](https://github.com/DouyinFE/semi-design/pull/1411)
+- 【Docs】
+    - 优化 Form 组件英文文档描述 及 Demo 
+- 【Design Token】
+    - Slider 新增` $spacing-slider_handle-translateY`、`$spacing-slider_vertical_handle-translateX`、`$spacing-slider_dot-translateX`、`$spacing-slider_vertical_dot-translateY` Token,用于控制水平和垂直状态 把手和数值刻度线的水平和垂直偏移 [#1391](https://github.com/DouyinFE/semi-design/pull/1391)
+  
 #### 🎉 2.28.1 (2023-01-31)
 - 【Fix】
     - 修复 DatePicker 选择时间范围时面板移动问题 [#1221](https://github.com/DouyinFE/semi-design/issues/1221)

+ 11 - 5
cypress/integration/slider.spec.js

@@ -38,7 +38,7 @@ describe('slider', () => {
         // test knob slide
         cy.get(sliderHandleSelector)
             .trigger('mousedown')
-            .trigger('mousemove', { pageX: 600, pageY:0 })
+            .trigger('mousemove', { pageX: 600, pageY: 0 })
             .trigger('mouseup', { force: true });
         
         cy.get(sliderHandleSelector).should(($button) => {
@@ -59,7 +59,7 @@ describe('slider', () => {
         // test knob slide (pageX 300 = 32%)
         cy.get(sliderHandleSelector)
             .trigger('mousedown')
-            .trigger('mousemove', { pageX: 300, pageY:0 })
+            .trigger('mousemove', { pageX: 300, pageY: 0 })
             .trigger('mouseup', { force: true });
         
         // left 32% = 247.68px;
@@ -98,13 +98,13 @@ describe('slider', () => {
         // test left knob slide
         cy.get(sliderHandleSelector).eq(0)
             .trigger('mousedown')
-            .trigger('mousemove', { pageX: 100, pageY:0 })
+            .trigger('mousemove', { pageX: 100, pageY: 0 })
             .trigger('mouseup', { force: true });
 
         // test right knob slide
         cy.get(sliderHandleSelector).eq(1)
             .trigger('mousedown')
-            .trigger('mousemove', { pageX: 600, pageY:0 })
+            .trigger('mousemove', { pageX: 600, pageY: 0 })
             .trigger('mouseup', { force: true });
 
         cy.get(sliderHandleSelector).should((handles) => {
@@ -134,7 +134,7 @@ describe('slider', () => {
         // test knob slide
         cy.get(sliderHandleSelector)
             .trigger('mousedown')
-            .trigger('mousemove', { pageX: 0, pageY:600 })
+            .trigger('mousemove', { pageX: 0, pageY: 600 })
             .trigger('mouseup', { force: true });
         
         cy.get(sliderHandleSelector).should(($button) => {
@@ -206,4 +206,10 @@ describe('slider', () => {
         cy.get('.semi-slider-handle').eq(2).type('{Home}');
         cy.get('.semi-slider-handle').eq(2).should('have.attr', 'aria-valuenow', '0');
     });  
+
+    it('should show tooltip when hovering slider handler', () => {
+        cy.visit('http://127.0.0.1:6006/iframe.html?id=slider--horizontal-slider&args=&viewMode=story');
+        cy.get('.semi-slider-handle').eq(0).trigger('mouseover');
+        cy.get('.semi-slider-handle-tooltip').eq(0).should('have.text', '0');
+    });
 });

+ 1 - 1
lerna.json

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

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation-react",
-  "version": "2.28.2",
+  "version": "2.29.0-beta.0",
   "description": "motion library for semi-ui-react",
   "keywords": [
     "motion",

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-animation-styled",
-  "version": "2.28.2",
+  "version": "2.29.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.28.2",
+  "version": "2.29.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.28.2",
+  "version": "2.29.0-beta.0",
   "description": "semi ui eslint plugin",
   "keywords": [
     "semi",

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

@@ -168,7 +168,7 @@ export interface DatePickerFoundationProps extends ElementProps, RenderProps, Ev
     dateFnsLocale?: any;
     localeCode?: string;
     rangeSeparator?: string;
-    insetInput?: boolean;
+    insetInput?: DateInputFoundationProps['insetInput'];
     preventScroll?: boolean
 }
 

+ 13 - 3
packages/semi-foundation/datePicker/inputFoundation.ts

@@ -38,9 +38,19 @@ export interface DateInputElementProps {
     prefix?: any
 }
 
+export interface InsetInputProps {
+    placeholder?: {
+        dateStart?: string;
+        dateEnd?: string;
+        timeStart?: string;
+        timeEnd?: string
+    }
+    // showClear?: boolean
+}
+
 export interface DateInputFoundationProps extends DateInputElementProps, DateInputEventHandlerProps {
     [x: string]: any;
-    value?: BaseValueType[];
+    value?: Date[];
     disabled?: boolean;
     type?: Type;
     showClear?: boolean;
@@ -51,7 +61,7 @@ export interface DateInputFoundationProps extends DateInputElementProps, DateInp
     prefixCls?: string;
     rangeSeparator?: string;
     panelType?: PanelType;
-    insetInput?: boolean;
+    insetInput?: boolean | InsetInputProps;
     insetInputValue?: InsetInputValue;
     density?: typeof strings.DENSITY_SET[number];
     defaultPickerValue?: ValueType
@@ -81,7 +91,7 @@ export interface InsetInputChangeProps {
     insetInputValue: InsetInputValue
 }
 
-export interface DateInputAdapter extends DefaultAdapter {
+export interface DateInputAdapter extends DefaultAdapter<DateInputFoundationProps, Record<string, any>> {
     updateIsFocusing: (isFocusing: boolean) => void;
     notifyClick: DateInputFoundationProps['onClick'];
     notifyChange: DateInputFoundationProps['onChange'];

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

@@ -20,7 +20,7 @@ import { includes, isSet, isEqual, isFunction } from 'lodash';
 import { zonedTimeToUtc } from '../utils/date-fns-extra';
 import { getDefaultFormatTokenByType } from './_utils/getDefaultFormatToken';
 import isNullOrUndefined from '../utils/isNullOrUndefined';
-import { BaseValueType, PresetPosition, ValueType } from './foundation';
+import { BaseValueType, DateInputFoundationProps, PresetPosition, ValueType } from './foundation';
 import { MonthDayInfo } from './monthFoundation';
 import { ArrayElement } from '../utils/type';
 
@@ -90,7 +90,7 @@ export interface MonthsGridFoundationProps extends MonthsGridElementProps {
     isAnotherPanelHasOpened?: (currentRangeInput: 'rangeStart' | 'rangeEnd') => boolean;
     focusRecordsRef?: any;
     triggerRender?: (props: Record<string, any>) => any;
-    insetInput: boolean;
+    insetInput: DateInputFoundationProps['insetInput'];
     presetPosition?: PresetPosition;
     renderQuickControls?: any;
     renderDateInput?: any

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

@@ -257,11 +257,11 @@ $rating: #{$prefix}-rating;
         margin-bottom: 0;
         padding-top: $spacing-form_field_group_vertical-paddingTop;
         padding-bottom: $spacing-form_field_group_vertical-paddingBottom;
+        overflow: hidden;
     }
 }
 
 .#{$form}-field-group {
-
     &[x-label-pos="top"] {
     }
 

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-foundation",
-    "version": "2.28.2",
+    "version": "2.29.0-beta.0",
     "description": "",
     "scripts": {
         "build:lib": "node ./scripts/compileLib.js",

+ 6 - 1
packages/semi-foundation/slider/slider.scss

@@ -57,7 +57,7 @@ $module: #{$prefix}-slider;
         border-radius: 50%;
         cursor: pointer;
         transition: background-color $transition_duration_slider_handle-bg $transition_function-slider-handle-bg $transition_delay-slider_handle-bg;
-        transform: scale($transform_scale-slider_handle);
+        transform: $transform_scale-slider_handle translateX(-50%) translateY($spacing-slider_handle-translateY);
         &:focus-visible {
             outline: $width-slider_handle-focus solid $color-slider_handle-focus;
         }
@@ -101,6 +101,7 @@ $module: #{$prefix}-slider;
         //border: 1px solid $color-control-bg-default;
         border-radius: 50%;
         cursor: pointer;
+        transform: translateX($spacing-slider_dot-translateX);
     }
 
     &-dot-active {
@@ -196,6 +197,10 @@ $module: #{$prefix}-slider;
     .#{$module}-handle {
         margin-top: $spacing-slider_vertical_handle-marginTop;
         margin-left: $spacing-slider_vertical_handle-marginLeft;
+        transform: $transform_scale-slider_handle translateY(-50%) translateX($spacing-slider_vertical_handle-translateX);
+    }
+    .#{$module}-dot{
+        transform: translateY($spacing-slider_vertical_dot-translateY);
     }
 }
 

+ 13 - 9
packages/semi-foundation/slider/variables.scss

@@ -18,26 +18,30 @@ $color-slider_handle-focus: var(--semi-color-primary-light-active); // 圆形按
 
 // Spacing
 $spacing-slider-paddingX: 13px; // 滑动条整体水平内边距
-$spacing-slider-paddingY: 0; // 滑动条整体垂直内边距
-$spacing-slider-margin: 0; // 滑动条整体外边距
-$spacing-slider_rail-margin: 0; // 滑动条轨道外边距
-$spacing-slider_rail-padding: 0; // 滑动条轨道内边距
+$spacing-slider-paddingY: 0px; // 滑动条整体垂直内边距
+$spacing-slider-margin: 0px; // 滑动条整体外边距
+$spacing-slider_rail-margin: 0px; // 滑动条轨道外边距
+$spacing-slider_rail-padding: 0px; // 滑动条轨道内边距
 $spacing-slider_rail-top: 14px; // 滑动条未填充轨道顶部距离
 $spacing-slider_handle-marginTop: 4px; // 滑动条圆形按钮顶部外边距
 $spacing-slider_track-top: 14px; // 滑动条已填充轨道顶部距离
 $spacing-slider_tooltip-top: -40px; // 滑动条工具提示顶部距离
 $spacing-slider_dot-top: 14px; // 滑动条圆形刻度点顶部距离
 $spacing-slider_marks-top: 23px; // 滑动条刻度标签顶部距离
-$spacing-slider_marks-left: 0; // 滑动条刻度标签左侧距离
+$spacing-slider_marks-left: 0px; // 滑动条刻度标签左侧距离
 $spacing-slider_boundary-top: 30px;
-$spacing-slider_boundary_min-left: 0;
-$spacing-slider_boundary_max-right: 0;
+$spacing-slider_boundary_min-left: 0px;
+$spacing-slider_boundary_max-right: 0px;
 $spacing-slider_vertical_marks-marginTop: -30px; // 垂直滑动条刻度标签顶部外边距
 $spacing-slider_vertical_marks-marginLeft: 29px; // 垂直滑动条刻度标签左侧外边距
 $spacing-slider_vertical_marks-reverse-marginLeft: -26px; // 垂直滑动条刻度标签左侧外边距(标签在左侧时)
-$spacing-slider_vertical_rail-top: 0; // 垂直滑动条轨道顶部距离
-$spacing-slider_vertical_handle-marginTop: 0; // 垂直滑动条原型按钮顶部外边距
+$spacing-slider_vertical_rail-top: 0px; // 垂直滑动条轨道顶部距离
+$spacing-slider_vertical_handle-marginTop: 0px; // 垂直滑动条原型按钮顶部外边距
 $spacing-slider_vertical_handle-marginLeft: -10px; // 垂直滑动条原型按钮左侧外边距
+$spacing-slider_handle-translateY: 0px; //  水平滑动条原型按钮垂直偏移量
+$spacing-slider_vertical_handle-translateX: 0px; // 垂直滑动条原型按钮水平偏移量
+$spacing-slider_dot-translateX: 0px; //  水平滑动条圆形刻度点水平偏移量
+$spacing-slider_vertical_dot-translateY: 0px; // 垂直滑动条圆形刻度点垂直偏移量
 
 // Radius
 $radius-slider_rail: var(--semi-border-radius-small); // 滚动条未填充轨道圆角

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-icons",
-  "version": "2.28.2",
+  "version": "2.29.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.28.2",
+  "version": "2.29.0-beta.0",
   "description": "semi illustrations",
   "keywords": [
     "semi",

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-next",
-    "version": "2.28.2",
+    "version": "2.29.0-beta.0",
     "description": "Plugin that support Semi Design in Next.js",
     "author": "伍浩威 <[email protected]>",
     "homepage": "",

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

@@ -1,6 +1,6 @@
 {
   "name": "@douyinfe/semi-scss-compile",
-  "version": "2.28.2",
+  "version": "2.29.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.28.2",
+    "version": "2.29.0-beta.0",
     "description": "semi-theme-default",
     "keywords": [
         "semi-theme",

+ 1 - 1
packages/semi-ui/cascader/_story/cascader.stories.jsx

@@ -1095,7 +1095,7 @@ export const CascaderWithSlot = () => {
         topSlot={<Text style={slotStyle}>选择地区</Text>}
         bottomSlot={
           <div style={slotStyle}>
-            <Text>找不相关选项?</Text>
+            <Text>找不相关选项?</Text>
             <Text link>去新建</Text>
           </div>
         }

+ 1 - 0
packages/semi-ui/datePicker/__test__/datePicker.test.js

@@ -58,6 +58,7 @@ describe(`DatePicker`, () => {
 
         const elem = mount(<DatePicker motion={motion} defaultOpen={open} defaultValue={defaultValue} />);
 
+        await sleep();
         expect(document.querySelectorAll(popupSelector).length).toBe(1);
 
         // document.body.click();

+ 60 - 0
packages/semi-ui/datePicker/_story/v2/FeatInsetInputProps.tsx

@@ -0,0 +1,60 @@
+import React from 'react';
+import { DatePicker, Space, Button } from '../../../index';
+import { DatePickerProps } from '../../index';
+
+/**
+ * Test with Chromatic
+ */
+export default function App() {
+    const spacing = [200, 400];
+    const [density, setDensity] = React.useState(false);
+    // const defaultValue = '2022-01-15';
+    // const defaultRangeValue = ['2022-12-10', '2022-01-15'];
+
+    const props: DatePickerProps = {
+        defaultOpen: true,
+        motion: false,
+        density: density ? 'compact' : 'default',
+        defaultPickerValue: '2022-01-15',
+        insetInput: {
+            placeholder: {
+                dateStart: '开始日期',
+                dateEnd: '开始日期',
+                timeStart: '开始时间',
+                timeEnd: '结束时间',
+            },
+        }
+    };
+
+    const handleToggleDensity = () => {
+        setDensity(!density);
+    };
+
+    return (
+        <div style={{ height: '200vh' }}>
+            <div style={{ marginBottom: 12 }}>
+                <Space>
+                    <Button onClick={handleToggleDensity}>
+                        {`小尺寸=${density}`}
+                    </Button>
+                </Space>
+            </div>
+            <Space wrap spacing={spacing}>
+                <DatePicker placeholder='选择单个日期' {...props} />
+                <DatePicker placeholder='选择月' {...props} type='month' />
+                <DatePicker placeholder='选择日期时间' {...props} type='dateTime' />
+                <DatePicker placeholder='选择日期范围' {...props} type='dateRange' />
+                <DatePicker placeholder='选择日期时间范围' {...props} type='dateTimeRange' />
+            </Space>
+        </div>
+    );
+}
+
+App.parameters = {
+    chromatic: {
+        disableSnapshot: false,
+        delay: 3000,
+        viewports: [1800]
+    },
+};
+App.storyName = 'insetInputProps';

+ 1 - 0
packages/semi-ui/datePicker/_story/v2/index.js

@@ -13,3 +13,4 @@ export { default as DisabledRange } from './DisabledRange';
 export { default as FixDisabledMonth } from './FixDisabledMonth';
 export { default as FixRangePanelShift } from './FixRangePanelShift';
 export { default as InsetInputControlled } from './InsetInputControlled';
+export { default as FeatInsetInputProps } from './FeatInsetInputProps';

+ 9 - 5
packages/semi-ui/datePicker/dateInput.tsx

@@ -5,6 +5,7 @@
 import React from 'react';
 import cls from 'classnames';
 import PropTypes from 'prop-types';
+import { get } from 'lodash';
 
 import DateInputFoundation, {
     DateInputAdapter,
@@ -12,6 +13,7 @@ import DateInputFoundation, {
     RangeType,
     InsetInputChangeProps,
     InsetInputChangeFoundationProps,
+    InsetInputProps
 } from '@douyinfe/semi-foundation/datePicker/inputFoundation';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/datePicker/constants';
 import { noop } from '@douyinfe/semi-foundation/utils/function';
@@ -62,7 +64,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
         rangeInputStartRef: PropTypes.object,
         rangeInputEndRef: PropTypes.object,
         rangeSeparator: PropTypes.string,
-        insetInput: PropTypes.bool,
+        insetInput: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
         insetInputValue: PropTypes.object,
         defaultPickerValue: PropTypes.oneOfType([
             PropTypes.string,
@@ -313,10 +315,12 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
             rangeInputStartRef,
             rangeInputEndRef,
             density,
+            insetInput,
         } = this.props;
 
         const _isRangeType = type.includes('Range');
         const newInsetInputValue = this.foundation.getInsetInputValue({ value, insetInputValue });
+        const { dateStart, dateEnd, timeStart, timeEnd } = get(insetInput, 'placeholder', {}) as InsetInputProps['placeholder'];
         const { datePlaceholder, timePlaceholder } = this.foundation.getInsetInputPlaceholder();
 
         const insetInputWrapperCls = `${prefixCls}-inset-input-wrapper`;
@@ -327,7 +331,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
                 <InsetDateInput
                     forwardRef={rangeInputStartRef}
                     insetInputValue={newInsetInputValue}
-                    placeholder={datePlaceholder}
+                    placeholder={dateStart ?? datePlaceholder}
                     valuePath={'monthLeft.dateInput'}
                     onChange={this.handleInsetInputChange}
                     onFocus={e => handleInsetDateFocus(e, 'rangeStart')}
@@ -335,7 +339,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
                 <InsetTimeInput
                     disabled={!newInsetInputValue.monthLeft.dateInput}
                     insetInputValue={newInsetInputValue}
-                    placeholder={timePlaceholder}
+                    placeholder={timeStart ?? timePlaceholder}
                     type={type}
                     valuePath={'monthLeft.timeInput'}
                     onChange={this.handleInsetInputChange}
@@ -347,7 +351,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
                         <InsetDateInput
                             forwardRef={rangeInputEndRef}
                             insetInputValue={newInsetInputValue}
-                            placeholder={datePlaceholder}
+                            placeholder={dateEnd ?? datePlaceholder}
                             valuePath={'monthRight.dateInput'}
                             onChange={this.handleInsetInputChange}
                             onFocus={e => handleInsetDateFocus(e, 'rangeEnd')}
@@ -355,7 +359,7 @@ export default class DateInput extends BaseComponent<DateInputProps, {}> {
                         <InsetTimeInput
                             disabled={!newInsetInputValue.monthRight.dateInput}
                             insetInputValue={newInsetInputValue}
-                            placeholder={timePlaceholder}
+                            placeholder={timeEnd ?? timePlaceholder}
                             type={type}
                             valuePath={'monthRight.timeInput'}
                             onChange={this.handleInsetInputChange}

+ 3 - 3
packages/semi-ui/datePicker/datePicker.tsx

@@ -489,7 +489,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
         const { insetInput, dateFnsLocale, density, type, format, rangeSeparator, defaultPickerValue } = this.props;
         const { insetInputValue, value } = this.state;
 
-        const insetInputProps = {
+        const props = {
             dateFnsLocale,
             format,
             insetInputValue,
@@ -505,7 +505,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
             defaultPickerValue
         };
 
-        return insetInput ? <DateInput {...insetInputProps} insetInput={true} /> : null;
+        return insetInput ? <DateInput {...props} insetInput={insetInput} /> : null;
     }
 
     handleOpenPanel = () => this.foundation.openPanel();
@@ -603,7 +603,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
             format,
             multiple,
             validateStatus,
-            inputReadOnly: inputReadOnly || insetInput,
+            inputReadOnly: inputReadOnly || Boolean(insetInput),
             // onClick: this.handleOpenPanel,
             onBlur: this.handleInputBlur,
             onFocus: this.handleInputFocus,

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

@@ -27,6 +27,7 @@ export type { MonthProps } from './month';
 export type { MonthsGridProps } from './monthsGrid';
 export type { QuickControlProps } from './quickControl';
 export type { YearAndMonthProps } from './yearAndMonth';
+export type { InsetInputProps } from '@douyinfe/semi-foundation/datePicker/inputFoundation';
 
 export default forwardStatics(
     React.forwardRef<DatePicker, DatePickerProps>((props, ref) => {

+ 6 - 5
packages/semi-ui/datePicker/quickControl.tsx

@@ -7,6 +7,7 @@ import Button from '../button/index';
 import Typography from '../typography/index';
 import { noop } from '@douyinfe/semi-foundation/utils/function';
 import { PresetsType, PresetType } from '@douyinfe/semi-foundation/datePicker/foundation';
+import { DateInputFoundationProps } from '@douyinfe/semi-foundation/datePicker/inputFoundation';
 
 const prefixCls = cssClasses.PREFIX;
 const { Text } = Typography;
@@ -16,7 +17,7 @@ export interface QuickControlProps {
     presetPosition: typeof strings.PRESET_POSITION_SET[number];
     onPresetClick: (preset: PresetType, e: React.MouseEvent) => void;
     type: string;
-    insetInput: boolean
+    insetInput: DateInputFoundationProps['insetInput']
 }
 
 class QuickControl extends PureComponent<QuickControlProps> {
@@ -25,7 +26,7 @@ class QuickControl extends PureComponent<QuickControlProps> {
         presetPosition: PropTypes.oneOf(strings.PRESET_POSITION_SET),
         onPresetClick: PropTypes.func,
         type: PropTypes.string,
-        insetInput: PropTypes.bool
+        insetInput: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
     };
 
     static defaultProps = {
@@ -74,15 +75,15 @@ class QuickControl extends PureComponent<QuickControlProps> {
         }
         return (
             <div className={wrapperCls} x-insetinput={insetInput ? "true" : "false"}>
-                { !isPanelTopAndBottom && <div className={headerCls}>快捷选择</div>}
+                {!isPanelTopAndBottom && <div className={headerCls}>快捷选择</div>}
                 <div className={contentWrapperCls}>
                     <div className={contentCls}>
                         {presets.map((item, index) => {
                             const _item: PresetType = typeof item === 'function' ? item() : item;
                             return (
-                                <Button size="small" type="primary" onClick={e => onPresetClick(_item, e)} key={index}>         
+                                <Button size="small" type="primary" onClick={e => onPresetClick(_item, e)} key={index}>
                                     <div className={itemCls}>
-                                        <Text 
+                                        <Text
                                             ellipsis={{ showTooltip: true }}
                                             className={ellipsisCls}
                                         >

+ 83 - 2
packages/semi-ui/form/_story/DynamicField/arrayFieldDemo.jsx

@@ -168,6 +168,16 @@ class ArrayFieldWithInitValue extends React.Component {
         this.change = this.change.bind(this);
     }
 
+    componentDidMount() {
+        this.formApi.setValues({
+            effects: [
+                { name: "脸部贴纸", type: "2D" },
+                { name: "前景贴纸", type: "3D" },
+            ],
+            test: 'fff'
+        })
+    }
+
     change = () => {
         let number = this.formApi.getValue('number');
         let newData = Array.from({ length: 2 }, (v, i) => ({
@@ -193,7 +203,7 @@ class ArrayFieldWithInitValue extends React.Component {
                 {/* <Button onClick={this.setValues}>setValues</Button> */}
                 <Row>
                     <Col span={12}>
-                        <ArrayField field="effects" initValue={effects}>
+                        <ArrayField field="effects">
                             {({ add, arrayFields }) => (
                                 <React.Fragment>
                                     <Button onClick={add} type="primary">Add</Button>
@@ -317,4 +327,75 @@ class ArrayFieldDemo extends React.Component {
     }
 }
 
-export { ArrayFieldCollapseDemo, ArrayFieldDemo, ArrayFieldWithFormInitValues, ArrayFieldWithInitValue };
+
+class ArrayFieldSetValues extends React.Component {
+    constructor() {
+        super();
+        this.state = {
+            data: {
+            },
+        };
+        this.getFormApi = this.getFormApi.bind(this);
+    }
+
+    componentDidMount() {
+        debugger
+        this.formApi.setValues({
+            effects: [
+                { name: "脸部贴纸", type: "2D" },
+                { name: "前景贴纸", type: "3D" },
+            ],
+            test: 'fff'
+        })
+    }
+
+    getFormApi(formApi) {
+        this.formApi = formApi;
+    }
+
+    render() {
+        return (
+            <Form
+                // onChange={(...v) => console.log(v)}
+                onValueChange={(...v) => console.log(v)}
+                getFormApi={this.getFormApi}>
+                <Row>
+                    <Col span={12}>
+                        <ArrayField field="effects">
+                            {({ add, arrayFields }) => (
+                                <React.Fragment>
+                                    <Button onClick={add} type="primary">Add</Button>
+                                    {
+                                        arrayFields.map(({ field, key, remove }, i) => (
+                                            <div key={key}>
+                                                <Input
+                                                    field={`${field}[name]`}
+                                                    label={`${field}.name`}
+                                                    // initValue='test'
+                                                />
+                                                <Input
+                                                    field={`${field}[time]`}
+                                                    label={`${field}.time`}
+                                                />
+                                                <Button type="danger" onClick={remove}>remove</Button>
+                                            </div>
+                                        ))
+                                    }
+                                </React.Fragment>
+                            )}
+                        </ArrayField>
+                        <Form.Input field="test" label="Test" />
+                        <Space>
+                            <Button htmlType='reset'>Reset</Button>
+                        </Space>
+                    </Col>
+                    <Col span={12}>
+                        <ComponentUsingFormState />
+                    </Col>
+                </Row>
+            </Form>
+        );
+    }
+}
+
+export { ArrayFieldCollapseDemo, ArrayFieldDemo, ArrayFieldWithFormInitValues, ArrayFieldWithInitValue, ArrayFieldSetValues };

+ 186 - 0
packages/semi-ui/form/_story/InputGroup/groupProps.jsx

@@ -0,0 +1,186 @@
+import React, { useState, useLayoutEffect, Component } from 'react';
+import { storiesOf } from '@storybook/react';
+import { Button, Modal, TreeSelect, Row, Col, Avatar, Icon, Select as BasicSelect,
+    Form,
+    useFormState,
+    useFormApi,
+    useFieldApi,
+    useFieldState,
+    withFormState,
+    withFormApi,
+    withField,
+    ArrayField,
+    AutoComplete,
+    Collapse } from '../../../index';
+
+
+const InputGroupDemo = () => {
+
+    const selectProps = {
+        style: { width: '100px' },
+        placeholder: '国家',
+        field: 'country',
+        rules: [
+            { required: true }
+        ]
+    };
+    return (
+        <>
+            <Form onSubmit={(values) => console.log(values)} labelPosition='top' style={{ width: 600 }}>
+                <Form.InputGroup label={{ text: 'Movie', required: true }} labelPosition="left" style={{ width: 500 }}>
+                    <Form.Select {...selectProps} field='area'>
+                        <Form.Select.Option value="crime">+86</Form.Select.Option>
+                        <Form.Select.Option value="comedy">+1</Form.Select.Option>
+                        <Form.Select.Option value="tragedy">+83</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input placeholder="手机号码" style={{ width: 100 }} field="phone" noLabel rules={[{ required: true }]} />
+                    <Form.InputNumber placeholder="评分" style={{ width: 140 }} field="MovieScore" noLabel />
+                </Form.InputGroup>
+                <Form.InputGroup label={{ text: (<span>手机号码</span>), required: true }} labelPosition='top' extraText='i am extraText of Form.InputGroup'>
+                    <Form.Select style={{ width: 150 }} field='phonePrefix1' initValue='+86' rules={[{ required: true }]} showClear>
+                        <Form.Select.Option value='+1'>美国+1</Form.Select.Option>
+                        <Form.Select.Option value='+852'>香港+852</Form.Select.Option>
+                        <Form.Select.Option value='+86'>中国+86</Form.Select.Option>
+                        <Form.Select.Option value='+81'>日本+81</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input initValue='18912345678' style={{ width: 250 }} field='phoneNumber1' trigger={['mount', 'change']} validate={val => 'always errors'} showClear />
+                </Form.InputGroup>
+            
+                <Form.InputGroup
+                    label={{ text: (<span>手机号码</span>), required: true }}
+                    labelPosition='top' 
+                    extraTextPosition='middle'
+                    extraText='i am extraText of Form.InputGroup'>
+                    <Form.Select style={{ width: 150 }} field='phonePrefix1' initValue='+86' rules={[{ required: true }]} showClear>
+                        <Form.Select.Option value='+1'>美国+1</Form.Select.Option>
+                        <Form.Select.Option value='+852'>香港+852</Form.Select.Option>
+                        <Form.Select.Option value='+86'>中国+86</Form.Select.Option>
+                        <Form.Select.Option value='+81'>日本+81</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input initValue='18912345678' style={{ width: 250 }} field='phoneNumber1' trigger={['mount', 'change']} validate={val => 'always errors'} showClear />
+                </Form.InputGroup>
+
+                <Form.InputGroup label={{ text: (<span>手机号码</span>), required: true }} labelPosition='left' style={{ width: 400 }}>
+                    <Form.Select style={{ width: 150 }} field='phonePrefix2' initValue='+86' rules={[{ required: true }]} showClear>
+                        <Form.Select.Option value='+1'>美国+1</Form.Select.Option>
+                        <Form.Select.Option value='+852'>香港+852</Form.Select.Option>
+                        <Form.Select.Option value='+86'>中国+86</Form.Select.Option>
+                        <Form.Select.Option value='+81'>日本+81</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input initValue='18912345678' style={{ width: 250 }} field='phoneNumber2' showClear />
+                </Form.InputGroup>
+
+                <Form.InputGroup
+                    label={{ text: (<span>手机号码</span>), required: true }}
+                    labelPosition='left'
+                    extraTextPosition='bottom'
+                    extraText='i am extraText of Form.InputGroup'
+                    style={{ width: 400 }}
+                >
+                    <Form.Select style={{ width: 150 }} field='phonePrefix3' initValue='+86' rules={[{ required: true }]} showClear>
+                        <Form.Select.Option value='+1'>美国+1</Form.Select.Option>
+                        <Form.Select.Option value='+852'>香港+852</Form.Select.Option>
+                        <Form.Select.Option value='+86'>中国+86</Form.Select.Option>
+                        <Form.Select.Option value='+81'>日本+81</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input style={{ width: 250 }} field='phoneNumber3' trigger={['mount', 'change']} validate={val => 'always errors'} showClear />
+                </Form.InputGroup>
+
+                <Form.InputGroup
+                    label={{ text: (<span>手机号码</span>), required: true }}
+                    labelPosition='left'
+                    extraTextPosition='middle'
+                    extraText='i am extraText of Form.InputGroup'
+                    style={{ width: 400 }}
+                >
+                    <Form.Select style={{ width: 150 }} field='phonePrefix4' initValue='+86' rules={[{ required: true }]} showClear>
+                        <Form.Select.Option value='+1'>美国+1</Form.Select.Option>
+                        <Form.Select.Option value='+852'>香港+852</Form.Select.Option>
+                        <Form.Select.Option value='+86'>中国+86</Form.Select.Option>
+                        <Form.Select.Option value='+81'>日本+81</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input style={{ width: 250 }} field='phoneNumber4' trigger={['mount', 'change']} validate={val => 'always errors'} rules={[{ required: true }]} showClear />
+                </Form.InputGroup>
+
+                <Button htmlType='submit'>提交</Button>
+            </Form>
+
+            <Form 
+                labelCol={{ span: 2 }}
+                wrapperCol={{ span: 22 }}
+            >
+                <Form.InputGroup
+                    label={{ text: (<span>手机号码</span>), required: true }}
+                    labelPosition='left'
+                    extraTextPosition='bottom'
+                    extraText='i am extraText of Form.InputGroup'
+                    style={{ width: 400 }}
+                >
+                    <Form.Select style={{ width: 150 }} field='phonePrefix3' initValue='+86' rules={[{ required: true }]} showClear>
+                        <Form.Select.Option value='+1'>美国+1</Form.Select.Option>
+                        <Form.Select.Option value='+852'>香港+852</Form.Select.Option>
+                        <Form.Select.Option value='+86'>中国+86</Form.Select.Option>
+                        <Form.Select.Option value='+81'>日本+81</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input style={{ width: 250 }} field='phoneNumber3' trigger={['mount', 'change']} validate={val => 'always errors'} showClear />
+                </Form.InputGroup>
+
+                <Form.InputGroup
+                    label={{ text: (<span>手机号码</span>), required: true }}
+                    labelPosition='left'
+                    extraTextPosition='middle'
+                    extraText='i am extraText of Form.InputGroup'
+                    style={{ width: 400 }}
+                >
+                    <Form.Select style={{ width: 150 }} field='phonePrefix4' initValue='+86' rules={[{ required: true }]} showClear>
+                        <Form.Select.Option value='+1'>美国+1</Form.Select.Option>
+                        <Form.Select.Option value='+852'>香港+852</Form.Select.Option>
+                        <Form.Select.Option value='+86'>中国+86</Form.Select.Option>
+                        <Form.Select.Option value='+81'>日本+81</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input style={{ width: 250 }} field='phoneNumber4' trigger={['mount', 'change']} validate={val => 'always errors'} rules={[{ required: true }]} showClear />
+                </Form.InputGroup>
+            </Form>
+
+            <Form 
+                labelCol={{ span: 4 }}
+                wrapperCol={{ span: 20 }}
+            >
+                <Form.InputGroup
+                    label={{ text: (<span>手机号码</span>), required: true }}
+                    labelPosition='top'
+                    extraTextPosition='bottom'
+                    extraText='i am extraText of Form.InputGroup'
+                    style={{ width: 400 }}
+                >
+                    <Form.Select style={{ width: 150 }} field='phonePrefix3' initValue='+86' rules={[{ required: true }]} showClear>
+                        <Form.Select.Option value='+1'>美国+1</Form.Select.Option>
+                        <Form.Select.Option value='+852'>香港+852</Form.Select.Option>
+                        <Form.Select.Option value='+86'>中国+86</Form.Select.Option>
+                        <Form.Select.Option value='+81'>日本+81</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input style={{ width: 250 }} field='phoneNumber3' trigger={['mount', 'change']} validate={val => 'always errors'} showClear />
+                </Form.InputGroup>
+
+                <Form.InputGroup
+                    label={{ text: (<span>手机号码</span>), required: true }}
+                    labelPosition='top'
+                    extraTextPosition='middle'
+                    extraText='i am extraText of Form.InputGroup'
+                    style={{ width: 400 }}
+                >
+                    <Form.Select style={{ width: 150 }} field='phonePrefix4' initValue='+86' rules={[{ required: true }]} showClear>
+                        <Form.Select.Option value='+1'>美国+1</Form.Select.Option>
+                        <Form.Select.Option value='+852'>香港+852</Form.Select.Option>
+                        <Form.Select.Option value='+86'>中国+86</Form.Select.Option>
+                        <Form.Select.Option value='+81'>日本+81</Form.Select.Option>
+                    </Form.Select>
+                    <Form.Input style={{ width: 250 }} field='phoneNumber4' trigger={['mount', 'change']} validate={val => 'always errors'} rules={[{ required: true }]} showClear />
+                </Form.InputGroup>
+            </Form>
+        </>
+    );
+};
+
+
+export { InputGroupDemo };

+ 1 - 56
packages/semi-ui/form/_story/Layout/layoutDemo.jsx

@@ -165,61 +165,6 @@ class InsetLabelDemo extends React.Component {
     }
 }
 
-
-
-class GroupFormDemo extends React.Component {
-
-    constructor() {
-        super();
-        this.saveFormApi = this.saveFormApi.bind(this);
-        this.manualSubmit = this.manualSubmit.bind(this);
-    }
-    saveFormApi(formApi) {
-        this.formApi = formApi;
-    }
-
-    manualSubmit() {
-        this.formApi.submitForm();
-    }
-
-    render() {
-        const selectProps = {
-            style: { width: '100px' },
-            placeholder: '国家',
-            field: 'country',
-            rules: [
-                { required: true }
-            ]
-        };
-        return (
-            <>
-                <Form onSubmit={values => console.log(values)} labelPosition="left" getFormApi={this.saveFormApi}>
-                    <InputGroup label={{ text: 'Movie', required: true }} labelPosition="left">
-                        <Select {...selectProps}>
-                            <Select.Option value="crime">+86</Select.Option>
-                            <Select.Option value="comedy">+1</Select.Option>
-                            <Select.Option value="tragedy">+83</Select.Option>
-                        </Select>
-                        <Input placeholder="手机号码" style={{ width: 100 }} field="phone" noLabel rules={[{ required: true }]} />
-                        <InputNumber placeholder="评分" style={{ width: 140 }} field="MovieScore" noLabel />
-                    </InputGroup>
-                    <Input field="name" trigger="blur" rules={[{ required: true }]} />
-                    <Input field="familyName[0].before" trigger="blur" />
-                    <Input field="familyName[0].after" trigger="blur" />
-                    <Select field="Sex">
-                        <Option value="female">female</Option>
-                        <Option value="male">male</Option>
-                    </Select>
-
-                    <Button htmlType="submit">提交</Button>
-                    <Button onClick={this.manualSubmit}>手动提交</Button>
-                </Form>
-            </>
-        );
-    }
-}
-
-
 class LayoutForm extends React.Component {
     constructor() {
         super();
@@ -349,4 +294,4 @@ class LayoutForm extends React.Component {
     }
 }
 
-export { LayoutDemo, InsetLabelDemo, GroupFormDemo };
+export { LayoutDemo, InsetLabelDemo };

+ 21 - 27
packages/semi-ui/form/_story/form.stories.jsx

@@ -1,27 +1,9 @@
 import React, { useState, useLayoutEffect, useEffect, useRef } from 'react';
 import {
   Button,
-  Modal,
-  TreeSelect,
-  Row,
-  Col,
-  Avatar,
-  Tabs,
-  TabPane,
-  Badge,
-  Notification,
 } from '../../index';
 import {
   Form,
-  useFormState,
-  useFormApi,
-  useFieldApi,
-  useFieldState,
-  withFormState,
-  withFormApi,
-  withField,
-  ArrayField,
-  Icon,
 } from '../../index';
 import { BasicDemoWithInit, LinkFieldForm, DifferentDeclareUsage } from './demo';
 const {
@@ -49,7 +31,7 @@ import {
 } from './Hook/hookDemo';
 
 // layout
-import { LayoutDemo, InsetLabelDemo, GroupFormDemo } from './Layout/layoutDemo';
+import { LayoutDemo, InsetLabelDemo } from './Layout/layoutDemo';
 import { AssistComponent } from './Layout/slotDemo';
 import { ModalFormDemo } from './Layout/modalFormDemo';
 
@@ -74,12 +56,16 @@ import { UpdateDemo, RuleupdateDemo } from './FieldProps/rulesUpdateDemo';
 import { FieldRefDemo } from './FieldProps/fieldRef';
 import { LableOptionalDemo } from './FieldProps/labelOptional';
 
+// form inputGroup
+import { InputGroupDemo } from './InputGroup/groupProps';
+
 // arrayField
 import {
   ArrayFieldCollapseDemo,
   ArrayFieldDemo,
   ArrayFieldWithFormInitValues,
   ArrayFieldWithInitValue,
+  ArrayFieldSetValues,
 } from './DynamicField/arrayFieldDemo';
 import { NestArrayField } from './DynamicField/nestArrayField';
 import { ArrayDemo } from './FormApi/arrayDemo';
@@ -96,6 +82,7 @@ import { FieldPathWithArrayDemo } from './Debug/bugDemo';
 import ChildDidMount from './Debug/childDidMount';
 export { default as FormSubmit } from './FormSubmit';
 
+
 export default {
   title: 'Form'
 }
@@ -114,12 +101,6 @@ BasicDemo.story = {
   name: 'BasicDemo',
 };
 
-export const LayoutFormInputGroup = () => <GroupFormDemo />;
-
-LayoutFormInputGroup.story = {
-  name: 'Layout-Form.InputGroup',
-};
-
 export const LayoutFormWrapperColLabelCol = () => <LayoutDemo />;
 
 LayoutFormWrapperColLabelCol.story = {
@@ -213,6 +194,11 @@ ArrayFieldWithArrayFieldInitValue.story = {
   name: 'ArrayField-with arrayField initValue',
 };
 
+export const ArrayFieldSetValuesDemo = () => <ArrayFieldSetValues />;
+ArrayFieldSetValuesDemo.story ={
+  name: 'ArrayField-setValues didMount'
+}
+
 export const ArrayFieldNestUsage = () => <NestArrayField />;
 
 ArrayFieldNestUsage.story = {
@@ -249,10 +235,11 @@ ValidateUseRules.story = {
   name: 'Validate-use rules',
 };
 
+
+export const RaceAsync = () => <RaceAsyncDemo />;
 RaceAsyncDemo.story = {
   name: 'Validate - race async'
 }
-export const RaceAsync = () => <RaceAsyncDemo />;
 
 export const HooksUseFormApi = () => <UseFormApiDemo />;
 
@@ -340,7 +327,6 @@ export const FiledPropHelpTextExtraTextExtraTextPosition = () => (
   <>
     <HelpAndExtra />
     <ExtraPositionDemo />
-    <GroupFormDemo />
   </>
 );
 
@@ -388,6 +374,14 @@ FieldPropRef.story = {
   name: 'Field Prop-ref',
 };
 
+
+export const GroupProp = () => <InputGroupDemo />
+
+GroupProp.story = {
+  name: 'InputGroup Prop - basic'
+};
+
+
 const InitEmptyStringDemo = () => {
   return (
     <Form allowEmpty>

+ 18 - 4
packages/semi-ui/form/group.tsx

@@ -21,7 +21,9 @@ interface GroupErrorProps {
 }
 export interface InputGroupProps extends BacisInputGroupProps {
     label?: LabelProps;
-    labelPosition?: 'left' | 'top'
+    labelPosition?: 'left' | 'top';
+    extraText?: React.ReactNode;
+    extraTextPosition?: 'bottom' | 'middle'
 }
 
 const prefix = cssClasses.PREFIX;
@@ -58,7 +60,7 @@ class FormInputGroup extends Component<InputGroupProps> {
     }
 
     render() {
-        const { children, label, ...rest } = this.props;
+        const { children, label, extraText, extraTextPosition, ...rest } = this.props;
         const updater = this.context;
         const formProps = updater.getFormProps(['labelPosition', 'labelWidth', 'labelAlign', 'showValidateIcon', 'wrapperCol', 'labelCol']);
         const labelPosition = this.props.labelPosition || formProps.labelPosition;
@@ -94,6 +96,14 @@ class FormInputGroup extends Component<InputGroupProps> {
         );
         const groupErrorContent = (<GroupError fieldSet={groupFieldSet} showValidateIcon={formProps.showValidateIcon} isInInputGroup />);
 
+        const extraCls = classNames(`${prefix}-field-extra`, {
+            [`${prefix}-field-extra-string`]: typeof extraText === 'string',
+            [`${prefix}-field-extra-middle`]: extraTextPosition === 'middle',
+            [`${prefix}-field-extra-bottom`]: extraTextPosition === 'bottom',
+        });
+
+        const extraContent = extraText ? <div className={extraCls} x-semi-prop="extraText">{extraText}</div> : null;
+
         let content: any;
 
         switch (true) {
@@ -102,7 +112,9 @@ class FormInputGroup extends Component<InputGroupProps> {
                     <>
                         {labelContent}
                         <div>
+                            {extraTextPosition === 'middle' ? extraContent : null}
                             {inputGroupContent}
+                            {extraTextPosition === 'bottom' ? extraContent : null}
                             {groupErrorContent}
                         </div>
                     </>
@@ -118,7 +130,9 @@ class FormInputGroup extends Component<InputGroupProps> {
                             </Col>
                         </div>
                         <Col {...wrapperCol}>
+                            {extraTextPosition === 'middle' ? extraContent : null}
                             {inputGroupContent}
+                            {extraTextPosition === 'bottom' ? extraContent : null}
                             {groupErrorContent}
                         </Col>
                     </>
@@ -131,7 +145,9 @@ class FormInputGroup extends Component<InputGroupProps> {
                             {labelContent}
                         </Col>
                         <Col {...wrapperCol}>
+                            {extraTextPosition === 'middle' ? extraContent : null}
                             {inputGroupContent}
+                            {extraTextPosition === 'bottom' ? extraContent : null}
                             {groupErrorContent}
                         </Col>
                     </>
@@ -141,8 +157,6 @@ class FormInputGroup extends Component<InputGroupProps> {
                 break;
         }
 
-
-
         return (
             <div x-label-pos={labelPosition} className={groupCls}>
                 {content}

+ 269 - 40
packages/semi-ui/locale/_story/locale.stories.jsx

@@ -1,5 +1,5 @@
 import React from 'react';
-import { useState } from 'react';
+import { useState, useMemo } from 'react';
 import {
   Modal,
   Pagination,
@@ -8,7 +8,9 @@ import {
   Select,
   Button,
   Cascader,
-  LocaleProvider
+  LocaleProvider,
+  ConfigProvider,
+  Pagination, Modal, Button, Select, DatePicker, TreeSelect, Table, TimePicker, List, Calendar, Typography, Transfer, ImagePreview, Image, Form, Nav
 } from '../../index';
 
 import zh_CN from '@douyinfe/semi-ui/locale/source/zh_CN';
@@ -18,6 +20,23 @@ import ko_KR from '@douyinfe/semi-ui/locale/source/ko_KR';
 import ja_JP from '@douyinfe/semi-ui/locale/source/ja_JP';
 import ru_RU from '@douyinfe/semi-ui/locale/source/ru_RU';
 import vi_VN from '@douyinfe/semi-ui/locale/source/vi_VN';
+import ar from '@douyinfe/semi-ui/locale/source/ar';
+import id_ID from '@douyinfe/semi-ui/locale/source/id_ID';
+import ms_MY from '@douyinfe/semi-ui/locale/source/ms_MY';
+import th_TH from '@douyinfe/semi-ui/locale/source/th_TH';
+import tr_TR from '@douyinfe/semi-ui/locale/source/tr_TR';
+import pt_BR from '@douyinfe/semi-ui/locale/source/pt_BR';
+import zh_TW from '@douyinfe/semi-ui/locale/source/zh_TW';
+import sv_SE from '@douyinfe/semi-ui/locale/source/sv_SE';
+import pl_PL from '@douyinfe/semi-ui/locale/source/pl_PL';
+import nl_NL from '@douyinfe/semi-ui/locale/source/nl_NL';
+import es from '@douyinfe/semi-ui/locale/source/es';
+import it from '@douyinfe/semi-ui/locale/source/it';
+import de from '@douyinfe/semi-ui/locale/source/de';
+import fr from '@douyinfe/semi-ui/locale/source/fr';
+import ro from '@douyinfe/semi-ui/locale/source/ro';
+import { IconUser, IconSemiLogo, IconStar } from '@douyinfe/semi-icons';
+
 
 const { Option } = Select;
 
@@ -110,44 +129,6 @@ const I18nComponent = () => {
   );
 };
 
-class I18nDemo extends React.Component {
-  constructor(props) {
-    super(props);
-    this.state = {
-      locale: zh_CN,
-    };
-    this.onLanguageChange = this.onLanguageChange.bind(this);
-  }
-
-  onLanguageChange(code) {
-    let language = {
-      zh_CN: zh_CN,
-      en_GB: en_GB,
-      ko_KR: ko_KR,
-      ja_JP: ja_JP,
-    };
-    this.setState({ locale: language[code] });
-  }
-
-  render() {
-    const { locale } = this.state;
-    return (
-      <>
-        <Select onChange={this.onLanguageChange}>
-          <Option value="zh_CN">中文</Option>
-          <Option value="en_GB">英语(英)</Option>
-          <Option value="ja_JP">日语</Option>
-          <Option value="ko_KR">韩语</Option>
-        </Select>
-        <hr />
-        <LocaleProvider locale={locale}>
-          <I18nComponent />
-        </LocaleProvider>
-      </>
-    );
-  }
-}
-
 export const LocaleZhCn = () => (
   <LocaleProvider locale={zh_CN}>
     <I18nComponent />
@@ -218,4 +199,252 @@ LocaleViVn.story = {
   name: 'Locale vi_VN',
 };
 
+class I18nDemo extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+            locale: zh_CN,
+            localeCode: 'zh_CN',
+        };
+        this.onLanguageChange = this.onLanguageChange.bind(this);
+    }
+
+    onLanguageChange(code) {
+        let language = {
+            'zh_CN': zh_CN,
+            'en_GB': en_GB,
+            'ko_KR': ko_KR,
+            'ja_JP': ja_JP,
+            'ar': ar,
+            'vi_VN': vi_VN,
+            'ru_RU': ru_RU,
+            'id_ID': id_ID,
+            'ms_MY': ms_MY,
+            'th_TH': th_TH,
+            'tr_TR': tr_TR,
+            'pt_BR': pt_BR,
+            'zh_TW': zh_TW,
+            'es': es,
+            'sv_SE': sv_SE,
+            'pl_PL': pl_PL,
+            'nl_NL': nl_NL,
+            de,
+            it,
+            fr,
+            ro
+        };
+        this.setState({ locale: language[code], localeCode: code });
+    }
+
+    render() {
+        const { locale, localeCode } = this.state;
+        const treeData = [
+            {
+                label: 'Asia',
+                value: 'asia',
+                key: '1',
+                children: [
+                    {
+                        label: 'China',
+                        value: 'china',
+                        key: '1-0',
+                        children: [
+                            { label: 'Beijing', value: 'beijing', key: '1-0-0' },
+                            { label: 'Shanghai', value: 'shanghai', key: '1-0-1' },
+                        ],
+                    },
+                    {
+                        label: 'Japan',
+                        value: 'japan',
+                        key: '1-1',
+                        children: [ { label: 'Osaka', value: 'osaka', key: '1-1-0' } ]
+                    },
+                ]
+            }
+        ];
+        const I18nComponent = () => {
+            const [modalVisible, setModalVisible] = useState(false);
+            const columns = useMemo(() => [
+                {
+                    title: 'Name',
+                    width: 250,
+                    dataIndex: 'name',
+                },
+                {
+                    title: 'Age',
+                    width: 150,
+                    dataIndex: 'age',
+                },
+                {
+                    title: 'Address',
+                    dataIndex: 'address',
+                },
+            ]);
+            const dataSource = useMemo(() => {
+                const data = [];
+                for (let i = 0; i < 46; i++) {
+                    data.push({
+                        key: '' + i,
+                        name: `Bytedance ${i}`,
+                        age: 32,
+                        address: `Beijing, Haidian. Zhichun Road ${i}`,
+                    });
+                }
+                return data;
+            });
+            const transferData = useMemo(() => {
+                return Array.from({ length: 100 }, (v, i) => {
+                    return {
+                        label: `选项名称 ${i}`,
+                        value: i,
+                        disabled: false,
+                        key: i,
+                    };
+                });
+            });
+            const srcList = useMemo(() => ([
+                "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+                "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+                "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+                "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/colorful.jpg",
+            ]), []);
+            const style = { margin: 10 };
+            return (
+                <>
+                    <h5>Pagination</h5>
+                    <Pagination total={100} showTotal showSizeChanger style={style} />
+                    <h5>Modal</h5>
+
+                    <div style={style}>
+                        <Button onClick={() => setModalVisible(true)}>
+                            Show Modal
+                        </Button>
+                        <Modal
+                            title="Modal"
+                            visible={modalVisible}
+                            onOk={() => setModalVisible(false)}
+                            onCancel={() => setModalVisible(false)}
+                        >
+                            <p>This is the content of a basic modal.</p>
+                            <p>More content...</p>
+                        </Modal>
+                    </div>
+                    <h5>Select & Cascader</h5>
+                    <div style={style}>
+                        <Select filter style={{ width: '180px' }}>
+                            <Select.Option value='abc'>abc</Select.Option>
+                            <Select.Option value='vigo' disabled>vigo</Select.Option>
+                            <Select.Option value='hotsoon'>hotsoon</Select.Option>
+                        </Select>
+                        <Cascader
+                            style={{ width: 300, margin: 10 }}
+                            treeData={treeData}
+                            filterTreeNode
+                            insetLabel='Cascader'
+                        />
+                    </div>
+                    <h5>DatePicker</h5>
+                    <DatePicker style={{ ...style, width: 250 }} />
+                    <DatePicker style={{ ...style, width: 300 }} type='dateTime' />
+                    <DatePicker style={{ ...style, width: 300 }} type='dateRange' />
+                    <DatePicker style={{ ...style, width: 450 }} type='dateTimeRange' />
+                    <h5>TimePicker</h5>
+                    <TimePicker style={style} />
+                    <TimePicker use12Hours style={style} /><br/><br/>
+                    <h5>TreeSelect</h5>
+                    <TreeSelect
+                        style={{ ...style, width: 300 }}
+                        dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
+                        treeData={treeData}
+                        filterTreeNode
+                    />
+                    <h5>Table</h5>
+                    <Table columns={columns} dataSource={dataSource} scroll={{ y: 320 }} />
+                    <h5>Table - Empty</h5>
+                    <Table columns={columns} dataSource={[]} scroll={{ y: 320 }} />
+                    <h5>List - Empty</h5>
+                    <List header={<div>List</div>} dataSource={[]}/>
+                    <h5>Calendar</h5>
+                    <Calendar mode='month' />
+                    <h5>Typography - Copyable</h5>
+                    <Typography.Paragraph copyable>Click to copy text.</Typography.Paragraph>
+                    <h5>Typography - Collapsible</h5>
+                    <Typography.Paragraph ellipsis={{ rows: 3, expandable: true, collapsible: true }} style={{ width: 300 }}>
+                        支持展开和折叠:Semi Design 是由互娱社区前端团队与 UED 团队共同设计开发并维护的设计系统。设计系统包含设计语言以及一整套可复用的前端组件,帮助设计师与开发者更容易地打造高质量的、用户体验一致的、符合设计规范的 Web 应用。
+                    </Typography.Paragraph>
+                    <h5>Transfer</h5>
+                    <Transfer
+                        style={{ width: 568, height: 416 }}
+                        dataSource={transferData}
+                    />
+                    <h5>Image</h5>
+                    <ImagePreview showTooltip>
+                        {srcList.map((src, index) => {
+                            return (
+                                <Image 
+                                    key={index} 
+                                    src={src} 
+                                    width={200} 
+                                    alt={`lamp${index + 1}`} 
+                                    style={{ marginRight: 5 }}
+                                />
+                            );
+                        })}
+                    </ImagePreview>
+                    <h5>Form</h5>
+                    <Form layout='horizontal' onValueChange={values=>console.log(values)}>
+                        <Form.Input field='UserName' label={{ text: '角色', optional: true }} style={{ width: 200 }} />
+                    </Form>
+                    <h5>Navigation</h5>
+                    <Nav
+                        bodyStyle={{ height: 320 }}
+                        items={[
+                            { itemKey: 'user', text: '用户管理', icon: <IconUser /> },
+                            { itemKey: 'union', text: '活动管理', icon: <IconStar /> },
+                        ]}
+                        header={{
+                            logo: <IconSemiLogo style={{ height: '36px', fontSize: 36 }} />,
+                            text: 'Semi 数据后台'
+                        }}
+                        footer={{
+                            collapseButton: true,
+                        }}
+                    />
+                </>
+            );
+        };
+        return (
+            <>
+                <div style={{ borderBottom: '1px solid var(--semi-color-border)', paddingBottom: 20 }}>
+                    <Select onChange={this.onLanguageChange} insetLabel='切换语言' style={{ width: 250 }} defaultValue='zh_CN'>
+                        <Select.Option value='zh_CN'>简体中文</Select.Option>
+                        <Select.Option value='en_GB'>英语(英)</Select.Option>
+                        <Select.Option value='ja_JP'>日语</Select.Option>
+                        <Select.Option value='ko_KR'>韩语</Select.Option>
+                        <Select.Option value='ar'>阿拉伯语</Select.Option>
+                        <Select.Option value='vi_VN'>越南语</Select.Option>
+                        <Select.Option value='ru_RU'>俄罗斯语</Select.Option>
+                        <Select.Option value='id_ID'>印尼语</Select.Option>
+                        <Select.Option value='ms_MY'>马来语</Select.Option>
+                        <Select.Option value='th_TH'>泰语</Select.Option>
+                        <Select.Option value='tr_TR'>土耳其语</Select.Option>
+                        <Select.Option value='pt_BR'>葡萄牙语(巴西)</Select.Option>
+                        <Select.Option value='zh_TW'>繁体中文</Select.Option>
+                        <Select.Option value='es'>西班牙语</Select.Option>
+                        <Select.Option value='de'>德语</Select.Option>
+                        <Select.Option value='it'>意大利语</Select.Option>
+                        <Select.Option value='fr'>法语</Select.Option>
+                        <Select.Option value='ro'>罗马尼亚语</Select.Option>
+                    </Select>
+                </div>
+                <LocaleProvider locale={locale}>
+                    <ConfigProvider direction={localeCode === 'ar' ? 'rtl' : 'ltr'} locale={locale}>
+                        <I18nComponent />
+                    </ConfigProvider>
+                </LocaleProvider>
+            </>
+        );
+    }
+}
+
 export const Locale = () => <I18nDemo />;

+ 2 - 3
packages/semi-ui/locale/interface.ts

@@ -4,11 +4,10 @@ export interface Locale {
     code: string;
     dateFnsLocale: dateFnsLocale;
     Pagination: {
-        item: string;
-        page: string;
         pageSize: string;
         total: string;
-        jumpTo: string
+        jumpTo: string;
+        page: string
     };
     Modal: {
         confirm: string;

+ 6 - 7
packages/semi-ui/locale/source/ar.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'ar',
     dateFnsLocale: arSA,
     Pagination: {
-        item: 'بند',
-        pageSize: ' العناصر / الصفحة',
+        pageSize: 'عدد العناصر في كل صفحة : ${pageSize} ',
+        total: 'مجموع الصفحات: ${total}',
+        jumpTo: 'اقفز إلى',
         page: ' الصفحات',
-        total: '',
-        jumpTo: 'اقفز إلى'
     },
     Modal: {
         confirm: 'تؤكد',
@@ -117,7 +116,7 @@ const local: Locale = {
         AM: '${time} صباح',
         PM: '${time} في الظهيرة',
         datestring: '',
-        remaining: '${remained} أكثر',
+        remaining: 'الكمية المتبقية: ${remained}',
     },
     Upload: {
         mainText: 'انقر لتحميل الملف أو اسحب الملف إلى هنا',
@@ -147,8 +146,8 @@ const local: Locale = {
         clear: 'واضح',
         selectAll: 'اختر الكل',
         clearSelectAll: 'إلغاء تحديد الكل',
-        total: 'مجموع ${total} العناصر',
-        selected: '${total} العناصر المحدد',
+        total: "الكمية الإجمالية: ${total}",
+        selected: "الكمية المحددة: ${total}"
     },
     Form: {
         optional: '(اختياري)',

+ 3 - 4
packages/semi-ui/locale/source/de.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'de',
     dateFnsLocale: de,
     Pagination: {
-        item: 'Artikel',
-        pageSize: ' Artikel / Seite',
+        pageSize: '${pageSize} Artikel / Seite',
+        total: '${total} Seiten',
+        jumpTo: 'Springen zu',
         page: ' Seiten',
-        total: '',
-        jumpTo: 'Springen zu'
     },
     Modal: {
         confirm: 'Bestätigen',

+ 4 - 5
packages/semi-ui/locale/source/en_GB.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'en-GB',
     dateFnsLocale: enGB,
     Pagination: {
-        item: 'item',
-        pageSize: ' items / page',
-        page: ' pages',
-        total: '',
-        jumpTo: 'Jump to'
+        pageSize: '${pageSize} items / page',
+        total: '${total} pages',
+        jumpTo: 'Jump to',
+        page: ' page',
     },
     Modal: {
         confirm: 'Confirm',

+ 4 - 5
packages/semi-ui/locale/source/en_US.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'en-US',
     dateFnsLocale: enUS,
     Pagination: {
-        item: 'item',
-        pageSize: ' items / page',
-        page: ' pages',
-        total: '',
-        jumpTo: 'Jump to'
+        pageSize: '${pageSize} items / page',
+        total: '${total} pages',
+        jumpTo: 'Jump to',
+        page: ' page',
     },
     Modal: {
         confirm: 'Confirm',

+ 3 - 4
packages/semi-ui/locale/source/es.ts

@@ -10,11 +10,10 @@ const locale: Locale = {
     code: 'es',
     dateFnsLocale: es,
     Pagination: {
-        item: 'objeto',
-        pageSize: ' objetos / página',
-        page: ' páginas',
-        total: '',
+        pageSize: '${pageSize} objetos / página',
+        total: '${total} páginas',
         jumpTo: 'Ir a',
+        page: ' páginas',
     },
     Modal: {
         confirm: 'Aceptar',

+ 3 - 4
packages/semi-ui/locale/source/fr.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'fr',
     dateFnsLocale: fr,
     Pagination: {
-        item: 'article',
-        pageSize: ' articles/page',
+        pageSize: '${pageSize} articles/page',
+        total: '${total} pages',
+        jumpTo: 'Sauter à',
         page: ' pages',
-        total: '',
-        jumpTo: 'Sauter à'
     },
     Modal: {
         confirm: 'Confirmer',

+ 3 - 4
packages/semi-ui/locale/source/id_ID.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'id-ID',
     dateFnsLocale: id,
     Pagination: {
-        item: 'item',
-        pageSize: ' item / halaman',
+        pageSize: '${pageSize} item / halaman',
+        total: '${total} halaman',
+        jumpTo: 'Langsung ke',
         page: ' halaman',
-        total: '',
-        jumpTo: 'Langsung ke'
     },
     Modal: {
         confirm: 'Konfirmasi',

+ 3 - 4
packages/semi-ui/locale/source/it.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'it',
     dateFnsLocale: it,
     Pagination: {
-        item: 'elemento',
-        pageSize: ' elementi / pagine',
+        pageSize: '${pageSize} elementi / pagine',
+        total: '${total} pagine',
+        jumpTo: 'Vai a',
         page: ' pagine',
-        total: '',
-        jumpTo: 'Vai a'
     },
     Modal: {
         confirm: 'Conferma',

+ 3 - 4
packages/semi-ui/locale/source/ja_JP.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'ja-JP',
     dateFnsLocale: ja,
     Pagination: {
-        item: '個数',
+        pageSize: '${pageSize} 個数 / ページ',
+        total: '合計 ${total} ページ',
+        jumpTo: 'ページへ',
         page: 'ページ',
-        pageSize: '個数 / ページ',
-        total: '合計',
-        jumpTo: 'ページへ'
     },
     Modal: {
         confirm: '確認する',

+ 3 - 4
packages/semi-ui/locale/source/ko_KR.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'ko-KR',
     dateFnsLocale: ko,
     Pagination: {
-        item: '기사',
+        pageSize: '${pageSize} 기사 / 페이지',
+        total: '${total} 페이지',
+        jumpTo: '이동',
         page: '페이지',
-        pageSize: '기사 / 페이지',
-        total: '',
-        jumpTo: '이동'
     },
     Modal: {
         confirm: '확인',

+ 3 - 4
packages/semi-ui/locale/source/ms_MY.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'ms-MY',
     dateFnsLocale: ms,
     Pagination: {
-        item: 'item',
-        pageSize: ' items / halaman',
+        pageSize: ' ${pageSize} items / halaman',
+        total: '${total} halaman',
+        jumpTo: 'Lompat ke',
         page: ' halaman',
-        total: '',
-        jumpTo: 'Lompat ke'
     },
     Modal: {
         confirm: 'Sahkan',

+ 177 - 0
packages/semi-ui/locale/source/nl_NL.ts

@@ -0,0 +1,177 @@
+
+import { nl } from 'date-fns/locale';
+import { Locale } from '../interface';
+
+/**
+ * [i18n-Dutch (pl_PL)]
+ * 荷兰语
+ *
+ */
+
+const local: Locale = {
+    code: 'pl_PL',
+    dateFnsLocale: nl, // locale code to dateFns locale
+    Pagination: {
+        page: 'pagina',
+        pageSize: 'Artikelen per pagina: ${pageSize}',
+        total: "Totaal ${total} pagina's",
+        jumpTo: 'Ga naar'
+    },
+    Modal: {
+        confirm: 'Bevestigen',
+        cancel: 'Annuleren',
+    },
+    TimePicker: {
+        placeholder: {
+            time: 'Tijd selecteren',
+            timeRange: 'Selecteer een tijdbereik',
+        },
+        begin: 'Begintijd',
+        end: 'Eindtijd',
+        hour: '',
+        minute: '',
+        second: '',
+        AM: '',
+        PM: '',
+    },
+    DatePicker: {
+        placeholder: {
+            date: 'Datum selecteren',
+            dateTime: 'Datum en tijd selecteren',
+            dateRange: ['Begindatum', 'Einddatum'],
+            dateTimeRange: ['Begindatum', 'Einddatum'],
+        },
+        footer: {
+            confirm: 'Bevestigen',
+            cancel: 'Annuleren',
+        },
+        selectDate: 'Datum selecteren',
+        selectTime: 'Tijd selecteren',
+        year: '',
+        month: '',
+        day: '',
+        monthText: '${month} ${year}',
+        months: {
+            1: 'jan',
+            2: 'feb',
+            3: 'mrt',
+            4: 'apr',
+            5: 'mei',
+            6: 'jun',
+            7: 'jul',
+            8: 'aug',
+            9: 'sep',
+            10: 'okt',
+            11: 'nov',
+            12: 'dec',
+        },
+        fullMonths: {
+            1: 'januari',
+            2: 'februari',
+            3: 'maart',
+            4: 'april',
+            5: 'mei',
+            6: 'juni',
+            7: 'juli',
+            8: 'augustus',
+            9: 'september',
+            10: 'oktober',
+            11: 'november',
+            12: 'december',
+        },
+        weeks: {
+            Mon: 'ma',
+            Tue: 'di',
+            Wed: 'wo',
+            Thu: 'do',
+            Fri: 'vr',
+            Sat: 'za',
+            Sun: 'zo',
+        },
+        localeFormatToken: {
+            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+        },
+    },
+    Navigation: {
+        collapseText: 'Zijbalk verbergen',
+        expandText: 'Zijbalk weergeven',
+    },
+    Popconfirm: {
+        confirm: 'Bevestigen',
+        cancel: 'Annuleren',
+    },
+    Table: {
+        emptyText: 'Geen resultaten gevonden',
+        pageText: '${currentStart} tot ${currentEnd} van ${total} wordt weergegeven',
+    },
+    Select: {
+        emptyText: 'Geen resultaten gevonden',
+        createText: 'Maken',
+    },
+    Cascader: {
+        emptyText: 'Geen resultaten gevonden',
+    },
+    Tree: {
+        emptyText: 'Geen resultaten gevonden',
+        searchPlaceholder: 'Zoeken',
+    },
+    List: {
+        emptyText: 'Geen resultaten gevonden',
+    },
+    Calendar: {
+        allDay: 'Hele dag',
+        AM: '${time} AM',
+        PM: '${time} PM',
+        datestring: '',
+        remaining: ' nog ${remained}',
+    },
+    Upload: {
+        mainText: 'Klik om een bestand te selecteren of sleep het hierheen om te uploaden',
+        illegalTips: 'Dit type bestand wordt niet ondersteund',
+        legalTips: 'Loslaten om te beginnen met uploaden',
+        retry: 'Opnieuw proberen',
+        replace: 'Bestand vervangen',
+        clear: 'Wissen',
+        selectedFiles: 'Geselecteerde bestanden',
+        illegalSize: 'Onjuiste bestandsgrootte',
+        fail: 'Kan niet uploaden',
+    },
+    TreeSelect: {
+        searchPlaceholder: 'Zoeken',
+    },
+    Typography: {
+        copy: 'Kopiëren',
+        copied: 'Gekopieerd',
+        expand: 'Meer weergeven',
+        collapse: 'Verbergen',
+    },
+    Transfer: {
+        emptyLeft: 'Geen gegevens',
+        emptySearch: 'Geen zoekresultaten',
+        emptyRight: 'Geselecteerde items verschijnen hier. Selecteer een item links',
+        placeholder: 'Zoeken',
+        clear: 'Wissen',
+        selectAll: 'Alles selecteren',
+        clearSelectAll: 'Alle selecties opheffen',
+        total: '${total} stuks in totaal',
+        selected: '${total} artikelen geselecteerd',
+    },
+    Form: {
+        optional: 'Optioneel',
+    },
+    Image: {
+        preview: 'Voorbeeld',
+        loading: 'Laden',
+        loadError: 'Kan niet laden',
+        prevTip: 'Vorige',
+        nextTip: 'Volgende',
+        zoomInTip: 'Inzoomen',
+        zoomOutTip: 'Uitzoomen',
+        rotateTip: 'Draaien',
+        downloadTip: 'Downloaden',
+        adaptiveTip: 'Adaptieve weergave',
+        originTip: 'Standaardweergave',
+    },
+};
+
+export default local;

+ 178 - 0
packages/semi-ui/locale/source/pl_PL.ts

@@ -0,0 +1,178 @@
+
+
+import { pl } from 'date-fns/locale';
+import { Locale } from '../interface';
+
+/**
+ * [i18n-Poland (pl_PL)]
+ * 波兰语
+ *
+ */
+
+const local: Locale = {
+    code: 'pl_PL',
+    dateFnsLocale: pl, // locale code to dateFns locale
+    Pagination: {
+        pageSize: 'Pozycje na stronie: ${pageSize}',
+        total: 'Razem stron: ${total}',
+        jumpTo: 'Przejdź do',
+        page: 'stron',
+    },
+    Modal: {
+        confirm: 'Potwierdź',
+        cancel: 'Anuluj',
+    },
+    TimePicker: {
+        placeholder: {
+            time: 'Wybierz czas',
+            timeRange: 'Wybierz przedział czasowy',
+        },
+        begin: 'Czas rozpoczęcia',
+        end: 'Czas zakończenia',
+        hour: '',
+        minute: '',
+        second: '',
+        AM: '',
+        PM: '',
+    },
+    DatePicker: {
+        placeholder: {
+            date: 'Wybierz datę',
+            dateTime: 'Wybierz datę i godzinę',
+            dateRange: ['Data rozpoczęcia', 'Data zakończenia'],
+            dateTimeRange: ['Data rozpoczęcia', 'Data zakończenia'],
+        },
+        footer: {
+            confirm: 'Potwierdź',
+            cancel: 'Anuluj',
+        },
+        selectDate: 'Wybierz datę',
+        selectTime: 'Wybierz godzinę',
+        year: '',
+        month: '',
+        day: '',
+        monthText: '${month} ${year}',
+        months: {
+            1: 'Sty',
+            2: 'Lut',
+            3: 'Mar',
+            4: 'Kwi',
+            5: 'Maj',
+            6: 'Cze',
+            7: 'Lip',
+            8: 'Sie',
+            9: 'Wrz',
+            10: 'Paź',
+            11: 'Lis',
+            12: 'Gru',
+        },
+        fullMonths: {
+            1: 'styczeń',
+            2: 'luty',
+            3: 'marzec',
+            4: 'kwiecień',
+            5: 'maj',
+            6: 'czerwiec',
+            7: 'lipiec',
+            8: 'sierpień',
+            9: 'wrzesień',
+            10: 'październik',
+            11: 'listopad',
+            12: 'grudzień',
+        },
+        weeks: {
+            Mon: 'Po',
+            Tue: 'Wt',
+            Wed: 'Śr',
+            Thu: 'Cz',
+            Fri: 'Pt',
+            Sat: 'So',
+            Sun: 'Nd',
+        },
+        localeFormatToken: {
+            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+        },
+    },
+    Navigation: {
+        collapseText: 'Ukryj pasek boczny',
+        expandText: 'Pokaż pasek boczny',
+    },
+    Popconfirm: {
+        confirm: 'Potwierdź',
+        cancel: 'Anuluj',
+    },
+    Table: {
+        emptyText: 'Nie znaleziono żadnych wyników',
+        pageText: 'Wyświetlanie od ${currentStart} do ${currentEnd} z ${total}',
+    },
+    Select: {
+        emptyText: 'Nie znaleziono żadnych wyników',
+        createText: 'Utwórz',
+    },
+    Cascader: {
+        emptyText: 'Nie znaleziono żadnych wyników',
+    },
+    Tree: {
+        emptyText: 'Nie znaleziono żadnych wyników',
+        searchPlaceholder: 'Wyszukaj',
+    },
+    List: {
+        emptyText: 'Nie znaleziono żadnych wyników',
+    },
+    Calendar: {
+        allDay: 'Cały dzień',
+        AM: '${time} AM',
+        PM: '${time} PM',
+        datestring: '',
+        remaining: 'jeszcze ${remained}',
+    },
+    Upload: {
+        mainText: 'Kliknij, aby wybrać plik, lub przeciągnij go tutaj, aby go przesłać.',
+        illegalTips: 'Ten typ pliku jest nieobsługiwany.',
+        legalTips: 'Zwolnij, aby rozpocząć przesyłanie.',
+        retry: 'Spróbuj ponownie',
+        replace: 'Zastąp plik',
+        clear: 'Wyczyść',
+        selectedFiles: 'Wybrane pliki',
+        illegalSize: 'Nieprawidłowy rozmiar pliku',
+        fail: 'Nie można przesłać',
+    },
+    TreeSelect: {
+        searchPlaceholder: 'Wyszukaj',
+    },
+    Typography: {
+        copy: 'Kopiuj',
+        copied: 'Skopiowano',
+        expand: 'Pokaż więcej',
+        collapse: 'Ukryj',
+    },
+    Transfer: {
+        emptyLeft: 'Brak danych',
+        emptySearch: 'Brak wyników wyszukiwania',
+        emptyRight: 'Tutaj pojawią się wybrane przedmioty. Wybierz przedmiot z lewej strony',
+        placeholder: 'Wyszukaj',
+        clear: 'Wyczyść',
+        selectAll: 'Zaznacz wszystkie',
+        clearSelectAll: 'Usuń zaznaczenie wszystkich',
+        total: 'Całkowita ilość: ${total}',
+        selected: 'Wybrana ilość: ${total}', 
+    },
+    Form: {
+        optional: '(Opcjonalnie)',
+    },
+    Image: {
+        preview: 'Podgląd',
+        loading: 'Zgrywanie',
+        loadError: 'Nie można zgrać',
+        prevTip: 'Wstecz',
+        nextTip: 'Dalej',
+        zoomInTip: 'Powiększ',
+        zoomOutTip: 'Pomniejsz',
+        rotateTip: 'Obróć',
+        downloadTip: 'Pobierz',
+        adaptiveTip: 'Dostosowywanie ekranu',
+        originTip: 'Wyświetlacz domyślny',
+    },
+};
+
+export default local;

+ 3 - 4
packages/semi-ui/locale/source/pt_BR.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'pt-BR',
     dateFnsLocale: ptBR,
     Pagination: {
-        item: 'artigo',
+        pageSize: '${pageSize} artigo /página',
+        total: 'Total ${total} página',
+        jumpTo: 'Pule para',
         page: 'página',
-        pageSize: 'artigo /página',
-        total: 'Total',
-        jumpTo: 'Pule para'
     },
     Modal: {
         confirm: 'OK',

+ 6 - 7
packages/semi-ui/locale/source/ro.ts

@@ -7,11 +7,10 @@ export default {
     code: 'ro',
     dateFnsLocale: ro,
     Pagination: {
-        item: 'articol',
-        pageSize: 'articole/pagină',
-        page: ' pagini',
-        total: '',
+        pageSize: 'Articole pe pagină: ${pageSize}',
+        total: 'Total pagini: ${total}',
         jumpTo: 'Treci la',
+        page: 'pagini',
     },
     Modal: {
         confirm: 'Confirmă',
@@ -118,7 +117,7 @@ export default {
         AM: '${time} AM',
         PM: '${time} PM',
         datestring: '',
-        remaining: '${remained} plus',
+        remaining: 'Cantitate ramasa: ${remained}',
     },
     Upload: {
         mainText: 'Dă clic pentru a descărca fișierul sau trage fișierul aici',
@@ -148,8 +147,8 @@ export default {
         clear: 'Șterge',
         selectAll: 'Selectează toate',
         clearSelectAll: 'Deselectează toate',
-        total: 'Total ${total} articole',
-        selected: '${total} articole selectate',
+        total: 'Total articole: ${total} ',
+        selected: 'articole selectate: ${total}',
     },
     Form: {
         optional: '(opțional)',

+ 7 - 8
packages/semi-ui/locale/source/ru_RU.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'ru-RU',
     dateFnsLocale: ru,
     Pagination: {
-        item: 'элемент',
-        pageSize: 'элементов / страницы',
-        page: ' страницы',
-        total: 'общее',
-        jumpTo: 'Прыгать в'
+        pageSize: 'Элементов на странице: ${pageSize}',
+        total: 'Всего страниц: ${total}',
+        jumpTo: 'Прыгать в',
+        page: ' страницы'
     },
     Modal: {
         confirm: 'подтвердить',
@@ -120,7 +119,7 @@ const local: Locale = {
         AM: '${time} утро',
         PM: '${time} после',
         datestring: '',
-        remaining: '${remained} еще',
+        remaining: 'ставшееся количество: ${remained} ',
     },
     Upload: {
         mainText: 'Нажмите, чтобы загрузить файл или перетащите файл сюда',
@@ -150,8 +149,8 @@ const local: Locale = {
         clear: 'Очистить',
         selectAll: 'Выбрать все',
         clearSelectAll: 'Снять выделение',
-        total: 'Всего ${total} элементов',
-        selected: 'Выбрано ${total} элементов',
+        total: 'Всего элементов: ${total} ',
+        selected: 'Выбрано элементов: ${total} ',
     },
     Form: {
         optional: '(по желанию)',

+ 175 - 0
packages/semi-ui/locale/source/sv_SE.ts

@@ -0,0 +1,175 @@
+import { sv } from 'date-fns/locale';
+import { Locale } from '../interface';
+
+/**
+ * [i18n-Swedish (sv_SE)]
+ * 瑞典语
+ */
+
+const local: Locale = {
+    code: 'sv_SE',
+    dateFnsLocale: sv, 
+    Pagination: {
+        pageSize: '${pageSize} artiklar/sida',
+        total: 'Totalt ${total} sidor',
+        jumpTo: 'Gå till',
+        page: 'sida',
+    },
+    Modal: {
+        confirm: 'Bekräfta',
+        cancel: 'Avbryt',
+    },
+    TimePicker: {
+        placeholder: {
+            time: 'Välj tid',
+            timeRange: 'Välj ett tidsintervall',
+        },
+        begin: 'Starttid',
+        end: 'Sluttid',
+        hour: '',
+        minute: '',
+        second: '',
+        AM: '',
+        PM: '',
+    },
+    DatePicker: {
+        placeholder: {
+            date: 'Välj datum',
+            dateTime: 'Välj datum och tid',
+            dateRange: ['Startdatum', 'Slutdatum'],
+            dateTimeRange: ['Startdatum', 'Slutdatum'],
+        },
+        footer: {
+            confirm: 'Bekräfta',
+            cancel: 'Avbryt',
+        },
+        selectDate: 'Välj datum',
+        selectTime: 'Välj tid',
+        year: '',
+        month: '',
+        day: '',
+        monthText: '${month} ${year}',
+        months: {
+            1: 'Jan',
+            2: 'Feb',
+            3: 'Mar',
+            4: 'Apr',
+            5: 'Maj',
+            6: 'Jun',
+            7: 'Jul',
+            8: 'Aug',
+            9: 'Sep',
+            10: 'Okt',
+            11: 'Nov',
+            12: 'Dec',
+        },
+        fullMonths: {
+            1: 'Januari',
+            2: 'Februari',
+            3: 'Mars',
+            4: 'April',
+            5: 'Maj',
+            6: 'Juni',
+            7: 'Juli',
+            8: 'Augusti',
+            9: 'September',
+            10: 'Oktober',
+            11: 'November',
+            12: 'December',
+        },
+        weeks: {
+            Mon: 'Mån',
+            Tue: 'Tis',
+            Wed: 'Ons',
+            Thu: 'Tor',
+            Fri: 'Fre',
+            Sat: 'Lör',
+            Sun: 'Sön',
+        },
+        localeFormatToken: {
+            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+        },
+    },
+    Navigation: {
+        collapseText: 'Dölj sidofält',
+        expandText: 'Visa sidofält',
+    },
+    Popconfirm: {
+        confirm: 'Bekräfta',
+        cancel: 'Avbryt',
+    },
+    Table: {
+        emptyText: 'Inga resultat hittades',
+        pageText: 'Visar ${currentStart} till ${currentEnd} av ${total}',
+    },
+    Select: {
+        emptyText: 'Inga resultat hittades',
+        createText: 'Skapa',
+    },
+    Cascader: {
+        emptyText: 'Inga resultat hittades',
+    },
+    Tree: {
+        emptyText: 'Inga resultat hittades',
+        searchPlaceholder: 'Sök',
+    },
+    List: {
+        emptyText: 'Inga resultat hittades',
+    },
+    Calendar: {
+        allDay: 'Hela dagen',
+        AM: '${time}',
+        PM: '${time}',
+        datestring: '',
+        remaining: '${remained} till',
+    },
+    Upload: {
+        mainText: 'Klicka för att välja en fil eller dra den hit för att ladda upp',
+        illegalTips: 'Den här filtypen stöds inte',
+        legalTips: 'Släpp för att börja ladda upp',
+        retry: 'Försök igen',
+        replace: 'Byt ut fil',
+        clear: 'Rensa',
+        selectedFiles: 'Valda filer',
+        illegalSize: 'Ogiltig filstorlek',
+        fail: 'Det gick inte att ladda upp',
+    },
+    TreeSelect: {
+        searchPlaceholder: 'Sök',
+    },
+    Typography: {
+        copy: 'Kopiera',
+        copied: 'Kopierad',
+        expand: 'Visa mer',
+        collapse: 'Dölj',
+    },
+    Transfer: {
+        emptyLeft: 'Inga data',
+        emptySearch: 'Inga sökresultat',
+        emptyRight: 'Valda objekt kommer att visas här. Välj ett objekt till vänster',
+        placeholder: 'Sök',
+        clear: 'Rensa',
+        selectAll: 'Markera alla',
+        clearSelectAll: 'Avmarkera alla',
+        total: '${total} artiklar totalt',
+        selected: 'Valde ${total} objekt',
+    },
+    Form: {
+        optional: '(Frivilligt)',
+    },
+    Image: {
+        preview: 'Förhandsgranskning',
+        loading: 'Läser in',
+        loadError: 'Det gick inte att läsa in',
+        prevTip: 'Föregående',
+        nextTip: 'Nästa',
+        zoomInTip: 'Zooma in',
+        zoomOutTip: 'Zooma ut',
+        rotateTip: 'Rotera',
+        downloadTip: 'Ladda ned',
+        adaptiveTip: 'Adaptiv visning',
+        originTip: 'Standardvisning',
+    },
+};
+
+export default local;

+ 3 - 4
packages/semi-ui/locale/source/th_TH.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'th-TH',
     dateFnsLocale: th,
     Pagination: {
-        item: 'บทความ',
+        pageSize: '${pageSize} บทความ / หน้า',
+        total: 'หน้าทั้งหมด ${total}',
+        jumpTo: 'ข้ามไปที่',
         page: 'หน้า',
-        pageSize: 'บทความ / หน้า',
-        total: 'ธรรมดา',
-        jumpTo: 'ข้ามไปที่'
     },
     Modal: {
         confirm: 'ตกลง',

+ 2 - 3
packages/semi-ui/locale/source/tr_TR.ts

@@ -5,10 +5,9 @@ const local: Locale = {
     code: 'tr-TR',
     dateFnsLocale: tr,
     Pagination: {
-        item: 'Makale',
         page: 'Sayfa',
-        pageSize: 'Makale / sayfa',
-        total: 'Toplam',
+        pageSize: '${pageSize} Makale / sayfa',
+        total: 'Toplam ${total} Sayfa',
         jumpTo: 'Atlamak'
     },
     Modal: {

+ 3 - 4
packages/semi-ui/locale/source/vi_VN.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'vi-VN',
     dateFnsLocale: vi,
     Pagination: {
-        item: 'Con số',
-        pageSize: ' Số / trang',
+        pageSize: '${pageSize} Số / trang',
+        total: 'Tổng cộng ${total} Số trang',
+        jumpTo: 'Chuyển đến',
         page: ' Số trang',
-        total: 'Tổng cộng',
-        jumpTo: 'Chuyển đến'
     },
     Modal: {
         confirm: 'Xác nhận',

+ 3 - 4
packages/semi-ui/locale/source/zh_CN.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'zh-CN',
     dateFnsLocale: zhCN, // locale code to dateFns locale
     Pagination: {
-        item: '条',
+        pageSize: '${pageSize} 条/页',
+        total: '共 ${total} 页',
+        jumpTo: '跳至',
         page: '页',
-        pageSize: '条/页',
-        total: '共',
-        jumpTo: '跳至'
     },
     Modal: {
         confirm: '确定',

+ 3 - 4
packages/semi-ui/locale/source/zh_TW.ts

@@ -5,11 +5,10 @@ const local: Locale = {
     code: 'zh-TW',
     dateFnsLocale: zhTW, // locale code to dateFns locale
     Pagination: {
-        item: '條',
+        pageSize: '${pageSize} 條/頁',
+        total: '共 ${total} 頁',
+        jumpTo: '跳至',
         page: '頁',
-        pageSize: '條/頁',
-        total: '共',
-        jumpTo: '跳至'
     },
     Modal: {
         confirm: '確定',

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

@@ -1,6 +1,6 @@
 {
     "name": "@douyinfe/semi-ui",
-    "version": "2.28.2",
+    "version": "2.29.0-beta.0",
     "description": "",
     "main": "lib/cjs/index.js",
     "module": "lib/es/index.js",
@@ -17,12 +17,12 @@
         "lib/*"
     ],
     "dependencies": {
-        "@douyinfe/semi-animation": "2.28.2",
-        "@douyinfe/semi-animation-react": "2.28.2",
-        "@douyinfe/semi-foundation": "2.28.2",
-        "@douyinfe/semi-icons": "2.28.2",
-        "@douyinfe/semi-illustrations": "2.28.2",
-        "@douyinfe/semi-theme-default": "2.28.2",
+        "@douyinfe/semi-animation": "2.29.0-beta.0",
+        "@douyinfe/semi-animation-react": "2.29.0-beta.0",
+        "@douyinfe/semi-foundation": "2.29.0-beta.0",
+        "@douyinfe/semi-icons": "2.29.0-beta.0",
+        "@douyinfe/semi-illustrations": "2.29.0-beta.0",
+        "@douyinfe/semi-theme-default": "2.29.0-beta.0",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "copy-text-to-clipboard": "^2.1.1",

+ 11 - 7
packages/semi-ui/pagination/index.tsx

@@ -261,13 +261,15 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
         if (!showSizeChanger) {
             return null;
         }
-        const pageSizeText = locale.pageSize;
+
         const newPageSizeOpts = this.foundation.pageSizeInOpts();
+
+        const pageSizeToken = locale.pageSize;
+        // Display pageSize in a specific language format order
         const options = newPageSizeOpts.map((size: number) => (
             <Option value={size} key={size}>
                 <span>
-                    {`${size} `}
-                    {pageSizeText}
+                    {pageSizeToken.replace('${pageSize}', size.toString())}
                 </span>
             </Option>
         ));
@@ -277,7 +279,7 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
                     aria-label="Page size selector"
                     onChange={newPageSize => this.foundation.changePageSize(newPageSize)}
                     value={pageSize}
-                    key={pageSizeText}
+                    key={pageSize}
                     position={popoverPosition || 'bottomRight'}
                     clickToHide
                     dropdownClassName={`${prefixCls}-select-dropdown`}
@@ -443,13 +445,15 @@ export default class Pagination extends BaseComponent<PaginationProps, Paginatio
         if (totalPageNum < 2 && hideOnSinglePage && !showSizeChanger) {
             return null;
         }
+
+        const totalNum = Math.ceil(total / pageSize);
+        const totalToken = locale.total.replace('${total}', totalNum.toString());
+
         return (
             <ul className={paginationCls} style={style}>
                 {showTotal ? (
                     <span className={showTotalCls}>
-                        {locale.total}
-                        {` ${Math.ceil(total / pageSize)} `}
-                        {locale.page}
+                        {totalToken}
                     </span>
                 ) : null}
                 {this.renderPrevBtn()}

+ 0 - 5
packages/semi-ui/slider/__test__/__snapshots__/slider.test.js.snap

@@ -1,5 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Slider should show tooltip when hovering slider handler 1`] = `null`;
-
-exports[`Slider should show tooltip when hovering slider handler 2`] = `null`;

+ 0 - 14
packages/semi-ui/slider/__test__/slider.test.js

@@ -86,20 +86,6 @@ describe('Slider', () => {
     //     expect(wrapper.state('currentValue')).toBe(54);
     // });
 
-    it('should show tooltip when hovering slider handler', () => {
-        const wrapper = mount(<Slider defaultValue={30} />);
-        wrapper
-            .find(`.${BASE_CLASS_PREFIX}-slider-handle`)
-            .at(0)
-            .simulate('mouseEnter');
-        expect(render(wrapper.find(`.${BASE_CLASS_PREFIX}-tooltip-wrapper`))).toMatchSnapshot();
-        wrapper
-            .find(`.${BASE_CLASS_PREFIX}-slider-handle`)
-            .at(0)
-            .simulate('mouseLeave');
-        expect(render(wrapper.find(`.${BASE_CLASS_PREFIX}-tooltip-wrapper`))).toMatchSnapshot();
-    });
-
     it('when hover into slider', () => {
         let wrapper = mount(<Slider showBoundary={true} />);
         wrapper

+ 0 - 4
packages/semi-ui/slider/index.tsx

@@ -296,7 +296,6 @@ export default class Slider extends BaseComponent<SliderProps, SliderState> {
             tipFormatter,
             range
         );
-        const transform = { top: 'translateY(-50%)', left: 'translateX(-50%)' };
         const minClass = cls(cssClasses.HANDLE, {
             [`${cssClasses.HANDLE}-clicked`]: chooseMovePos === 'min' && isDrag,
         });
@@ -328,7 +327,6 @@ export default class Slider extends BaseComponent<SliderProps, SliderState> {
                     style={{
                         [stylePos]: `${minPercent * 100}%`,
                         zIndex: chooseMovePos === 'min' && isDrag ? 2 : 1,
-                        transform: transform[stylePos],
                     }}
                     onMouseDown={e => {
                         this.foundation.onHandleDown(e, 'min');
@@ -385,7 +383,6 @@ export default class Slider extends BaseComponent<SliderProps, SliderState> {
                         style={{
                             [stylePos]: `${minPercent * 100}%`,
                             zIndex: chooseMovePos === 'min' ? 2 : 1,
-                            transform: transform[stylePos],
                         }}
                         onMouseDown={e => {
                             this.foundation.onHandleDown(e, 'min');
@@ -440,7 +437,6 @@ export default class Slider extends BaseComponent<SliderProps, SliderState> {
                         style={{
                             [stylePos]: `${maxPercent * 100}%`,
                             zIndex: chooseMovePos === 'max' ? 2 : 1,
-                            transform: transform[stylePos],
                         }}
                         onMouseDown={e => {
                             this.foundation.onHandleDown(e, 'max');

+ 5 - 1
packages/semi-ui/tooltip/index.tsx

@@ -496,7 +496,11 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
             "[Semi Tooltip] 'mouseLeaveDelay' cannot be less than 'mouseEnterDelay', which may cause the dropdown layer to not be hidden."
         );
         if (prevProps.visible !== this.props.visible) {
-            this.props.visible ? this.foundation.delayShow() : this.foundation.delayHide();
+            if (['hover', 'focus'].includes(this.props.trigger)) {
+                this.props.visible ? this.foundation.delayShow() : this.foundation.delayHide();
+            } else {
+                this.props.visible ? this.foundation.show() : this.foundation.hide();
+            }
         }
         if (!isEqual(prevProps.rePosKey, this.props.rePosKey)) {
             this.rePosition();

+ 37 - 1
packages/semi-ui/transfer/_story/transfer.stories.jsx

@@ -1,5 +1,5 @@
 import React, { useState, useRef } from 'react';
-import { Transfer, Button, Popover, SideSheet, Avatar, Checkbox, Tree, Input } from '../../index';
+import { Transfer, Button, Popover, SideSheet, Avatar, Checkbox, Tree, Input, Tag } from '../../index';
 import { omit, values } from 'lodash';
 import './transfer.scss';
 import { SortableContainer, SortableElement, sortableHandle } from 'react-sortable-hoc';
@@ -828,3 +828,39 @@ export const TransferInPopover = () => {
     </div>
   );
 }
+
+export const RenderHeader = () => {
+ 
+  const renderSourceHeader = (props) => {
+    const { num, showButton, allChecked, onAllClick } = props;
+    return <div style={{ margin: '10px 0 0 10px', height: 24, display: 'flex', alignItems: 'center' }}>
+      <span style={{ marginRight: 10 }} >共 {num} 项</span>
+      {showButton && <Button
+        theme="borderless"
+        type="tertiary"
+        size="small" 
+        onClick={onAllClick}>{ allChecked ? '取消全选' : '全选' }</Button>}
+    </div>;
+  };
+
+  const renderSelectedHeader = (props) => {
+    const { num, showButton, onClear } = props;
+    return <div style={{ margin: '10px 0 0 10px', height: 24, display: 'flex', alignItems: 'center' }}>
+      <span style={{ marginRight: 10 }}>{num} 项已选</span>
+      {showButton && <Button
+        theme="borderless"
+        type="tertiary"
+        size="small"
+        onClick={onClear}>清空</Button>}
+    </div>;
+  };
+
+  return (
+    <Transfer
+      style={{ width: 568, height: 416 }}
+      dataSource={data}
+      renderSourceHeader={renderSourceHeader}
+      renderSelectedHeader={renderSelectedHeader}
+    />
+  );
+};

+ 35 - 4
packages/semi-ui/transfer/index.tsx

@@ -2,7 +2,7 @@ import React from 'react';
 import cls from 'classnames';
 import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
 import PropTypes from 'prop-types';
-import { isEqual, noop, omit, isEmpty, isArray } from 'lodash';
+import { isEqual, noop, omit, isEmpty, isArray, pick } from 'lodash';
 import TransferFoundation, { TransferAdapter, BasicDataItem, OnSortEndProps } from '@douyinfe/semi-foundation/transfer/foundation';
 import { _generateDataByType, _generateSelectedItems } from '@douyinfe/semi-foundation/transfer/transferUtils';
 import { cssClasses, strings } from '@douyinfe/semi-foundation/transfer/constants';
@@ -115,7 +115,22 @@ interface HeaderConfig {
     allContent: string;
     onAllClick: () => void;
     type: string;
-    showButton: boolean
+    showButton: boolean;
+    num: number;
+    allChecked?: boolean
+}
+
+type SourceHeaderProps = {
+    num: number;
+    showButton: boolean;
+    allChecked: boolean;
+    onAllClick: () => void
+}
+
+type SelectedHeaderProps = {
+    num: number;
+    showButton: boolean;
+    onClear: () => void
 }
 
 export interface TransferState {
@@ -147,7 +162,9 @@ export interface TransferProps {
     renderSourceItem?: (item: RenderSourceItemProps) => React.ReactNode;
     renderSelectedItem?: (item: RenderSelectedItemProps) => React.ReactNode;
     renderSourcePanel?: (sourcePanelProps: SourcePanelProps) => React.ReactNode;
-    renderSelectedPanel?: (selectedPanelProps: SelectedPanelProps) => React.ReactNode
+    renderSelectedPanel?: (selectedPanelProps: SelectedPanelProps) => React.ReactNode;
+    renderSourceHeader?: (headProps: SourceHeaderProps) => React.ReactNode;
+    renderSelectedHeader?: (headProps: SelectedHeaderProps) => React.ReactNode
 }
 
 const prefixCls = cssClasses.PREFIX;
@@ -336,13 +353,24 @@ class Transfer extends BaseComponent<TransferProps, TransferState> {
     }
 
     renderHeader(headerConfig: HeaderConfig) {
-        const { disabled } = this.props;
+        const { disabled, renderSourceHeader, renderSelectedHeader } = this.props;
         const { totalContent, allContent, onAllClick, type, showButton } = headerConfig;
         const headerCls = cls({
             [`${prefixCls}-header`]: true,
             [`${prefixCls}-right-header`]: type === 'right',
             [`${prefixCls}-left-header`]: type === 'left',
         });
+
+        if (type === 'left' && typeof renderSourceHeader === 'function') {
+            const { num, showButton, allChecked, onAllClick } = headerConfig;
+            return renderSourceHeader({ num, showButton, allChecked, onAllClick });
+        }
+        
+        if (type === 'right' && typeof renderSelectedHeader === 'function') {
+            const { num, showButton, onAllClick: onClear } = headerConfig;
+            return renderSelectedHeader({ num, showButton, onClear });                 
+        }
+
         return (
             <div className={headerCls}>
                 <span className={`${prefixCls}-header-total`}>{totalContent}</span>
@@ -409,6 +437,8 @@ class Transfer extends BaseComponent<TransferProps, TransferState> {
             onAllClick: () => this.foundation.handleAll(leftContainesNotInSelected),
             type: 'left',
             showButton: type !== strings.TYPE_TREE_TO_LIST,
+            num: showNumber,
+            allChecked: !leftContainesNotInSelected
         };
         const inputCom = this.renderFilter(locale);
         const headerCom = this.renderHeader(headerConfig);
@@ -623,6 +653,7 @@ class Transfer extends BaseComponent<TransferProps, TransferState> {
             onAllClick: () => this.foundation.handleClear(),
             type: 'right',
             showButton: Boolean(selectedData.length),
+            num: selectedData.length,
         };
         const headerCom = this.renderHeader(headerConfig);
         const emptyCom = this.renderEmpty('right', emptyContent.right ? emptyContent.right : locale.emptyRight);

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

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

+ 4 - 1
src/templates/scope.js

@@ -26,6 +26,9 @@ import th_TH from '@douyinfe/semi-ui/locale/source/th_TH';
 import tr_TR from '@douyinfe/semi-ui/locale/source/tr_TR';
 import pt_BR from '@douyinfe/semi-ui/locale/source/pt_BR';
 import zh_TW from '@douyinfe/semi-ui/locale/source/zh_TW';
+import sv_SE from '@douyinfe/semi-ui/locale/source/sv_SE';
+import pl_PL from '@douyinfe/semi-ui/locale/source/pl_PL';
+import nl_NL from '@douyinfe/semi-ui/locale/source/nl_NL';
 import es from '@douyinfe/semi-ui/locale/source/es';
 import de from '@douyinfe/semi-ui/locale/source/de';
 import it from '@douyinfe/semi-ui/locale/source/it';
@@ -122,4 +125,4 @@ export {
 
 export { debounce, throttle, range, get, filter, map, some };
 
-export { zh_CN, en_GB, en_US, ko_KR, ja_JP, ar, vi_VN, ru_RU, id_ID, ms_MY, th_TH, tr_TR, pt_BR, zh_TW, es, de, it, fr, ro };
+export { zh_CN, en_GB, en_US, ko_KR, ja_JP, ar, vi_VN, ru_RU, id_ID, ms_MY, th_TH, tr_TR, pt_BR, zh_TW, nl_NL, pl_PL, sv_SE, es, de, it, fr, ro };

Some files were not shown because too many files changed in this diff