瀏覽代碼

Merge branch 'release'

代强 2 年之前
父節點
當前提交
ec7a462546
共有 67 個文件被更改,包括 996 次插入591 次删除
  1. 2 0
      .github/workflows/publish.yml
  2. 6 5
      content/basic/typography/index-en-US.md
  3. 8 6
      content/basic/typography/index.md
  4. 84 19
      content/feedback/popconfirm/index-en-US.md
  5. 89 26
      content/feedback/popconfirm/index.md
  6. 8 0
      content/input/cascader/index-en-US.md
  7. 9 0
      content/input/cascader/index.md
  8. 2 0
      content/other/locale/index.md
  9. 13 0
      content/start/changelog/index-en-US.md
  10. 12 0
      content/start/changelog/index.md
  11. 58 0
      cypress/integration/popconfirm.spec.js
  12. 1 1
      lerna.json
  13. 45 45
      packages/semi-animation-react/package.json
  14. 41 41
      packages/semi-animation-styled/package.json
  15. 44 44
      packages/semi-animation/package.json
  16. 41 41
      packages/semi-eslint-plugin/package.json
  17. 2 2
      packages/semi-foundation/package.json
  18. 20 1
      packages/semi-foundation/popconfirm/popconfirmFoundation.ts
  19. 1 0
      packages/semi-foundation/select/select.scss
  20. 1 0
      packages/semi-foundation/select/variables.scss
  21. 2 2
      packages/semi-foundation/tooltip/foundation.ts
  22. 6 0
      packages/semi-foundation/treeSelect/treeSelect.scss
  23. 1 1
      packages/semi-foundation/treeSelect/variables.scss
  24. 2 5
      packages/semi-foundation/typography/formatNumeral.ts
  25. 61 61
      packages/semi-icons/package.json
  26. 1 1
      packages/semi-icons/scripts/compileDist.js
  27. 46 46
      packages/semi-illustrations/package.json
  28. 1 1
      packages/semi-next/package.json
  29. 34 34
      packages/semi-scss-compile/package.json
  30. 1 1
      packages/semi-theme-default/package.json
  31. 16 0
      packages/semi-ui/cascader/__test__/cascader.test.js
  32. 53 1
      packages/semi-ui/cascader/_story/cascader.stories.jsx
  33. 8 0
      packages/semi-ui/cascader/index.tsx
  34. 5 5
      packages/semi-ui/locale/source/ar.ts
  35. 5 5
      packages/semi-ui/locale/source/de.ts
  36. 5 5
      packages/semi-ui/locale/source/en_GB.ts
  37. 4 4
      packages/semi-ui/locale/source/en_US.ts
  38. 5 5
      packages/semi-ui/locale/source/es.ts
  39. 5 5
      packages/semi-ui/locale/source/fr.ts
  40. 4 4
      packages/semi-ui/locale/source/id_ID.ts
  41. 5 5
      packages/semi-ui/locale/source/it.ts
  42. 5 5
      packages/semi-ui/locale/source/ja_JP.ts
  43. 4 4
      packages/semi-ui/locale/source/ko_KR.ts
  44. 4 4
      packages/semi-ui/locale/source/ms_MY.ts
  45. 6 6
      packages/semi-ui/locale/source/nl_NL.ts
  46. 4 4
      packages/semi-ui/locale/source/pl_PL.ts
  47. 5 5
      packages/semi-ui/locale/source/pt_BR.ts
  48. 17 4
      packages/semi-ui/locale/source/ro.ts
  49. 4 4
      packages/semi-ui/locale/source/ru_RU.ts
  50. 4 4
      packages/semi-ui/locale/source/sv_SE.ts
  51. 5 5
      packages/semi-ui/locale/source/th_TH.ts
  52. 5 5
      packages/semi-ui/locale/source/tr_TR.ts
  53. 5 5
      packages/semi-ui/locale/source/vi_VN.ts
  54. 4 4
      packages/semi-ui/locale/source/zh_CN.ts
  55. 4 4
      packages/semi-ui/locale/source/zh_TW.ts
  56. 5 1
      packages/semi-ui/modal/confirm.tsx
  57. 8 8
      packages/semi-ui/package.json
  58. 4 4
      packages/semi-ui/pagination/__test__/pagination.test.js
  59. 76 0
      packages/semi-ui/popconfirm/_story/popconfirm.stories.jsx
  60. 41 13
      packages/semi-ui/popconfirm/index.tsx
  61. 13 0
      packages/semi-ui/popover/index.tsx
  62. 12 3
      packages/semi-ui/tooltip/index.tsx
  63. 1 1
      packages/semi-webpack/package.json
  64. 1 1
      src/sitePages/newHome/components/comments/comments.jsx
  65. 1 1
      src/sitePages/newHome/components/operateButton/operateButton.jsx
  66. 1 0
      src/sitePages/newHome/components/theme/theme.js
  67. 0 74
      yarn.lock

+ 2 - 0
.github/workflows/publish.yml

@@ -52,3 +52,5 @@ jobs:
                   lerna version $RELEASE_VERSION --exact --force-publish --yes --no-push
                   lerna publish from-package --dist-tag $DIST_TAG --yes
                   git push -o ci.skip --follow-tags --no-verify --atomic
+              env: 
+                NODE_OPTIONS: '--no-experimental-fetch --openssl-legacy-provider'

+ 6 - 5
content/basic/typography/index-en-US.md

@@ -151,7 +151,8 @@ function Demo() {
 
 Based on Text component, added properties: `rule`, `precision`, `truncate`, `parser`, to provide the ability to handle Numeral in text separately.
 <Notice title='Note'>
-    The Numeral component recursively traverses Children to detect all numeric text within it for conversion and display, taking care to control the rendering structure hierarchy.
+    The Numeral component recursively traverses Children to detect all numeric text within it for conversion and display, taking care to control the rendering structure hierarchy.<br />
+    For Numeral components with a rule of percentage, the data processing rules have changed. In <strong>v2.22.0-v2.29.0</strong>, for num whose absolute value is greater than or equal to 1, the result is num%; for num whose absolute value is less than or equal to 1, the result is (num*100)%. After the <strong>v2.30.0</strong> version, it is unified to (num*100)%.
 </Notice>
 
 `precision` allows you to set the number of decimal places to be retained, used to set precision  
@@ -189,7 +190,7 @@ function Demo() {
             </Numeral>
 
             <Numeral rule="percentages" style={{ marginBottom: 12 }}>
-                My odds of winning this game are 60 and my odds of losing are 40.
+                My odds of winning this game are 0.6 and my odds of losing are 0.4.
             </Numeral>
 
             <Numeral rule="bytes-decimal" precision={2} truncate="floor">
@@ -223,10 +224,10 @@ function Demo() {
 
     function Infos() {
         const data = [
-            { type: 'Stars', min: '6200' },
-            { type: 'Fork', min: '400' },
+            { type: 'Stars', min: '6700' },
+            { type: 'Fork', min: '500' },
             { type: 'Downloads', min: '3000000' },
-            { type: 'Contributors', min: '60' }
+            { type: 'Contributors', min: '90' }
         ];
         return data.map(item =>
             <p key={item.min}>

+ 8 - 6
content/basic/typography/index.md

@@ -141,7 +141,9 @@ function Demo() {
 
 Numeral 组件在Text组件的基础上,添加了属性: `rule`, `precision`, `truncate`, `parser`, 以提供需要单独处理文本中数值的能力。
 <Notice title='注意'>
-    Numeral 组件会递归遍历 Children 检测其中所有的数字文本进行转换展示,请注意控制渲染结构层级。
+    Numeral 组件会递归遍历 Children 检测其中所有的数字文本进行转换展示,请注意控制渲染结构层级;
+    <br />
+    对于 rule 为 percentage 的 Numeral 组件,数据处理规则有变化。在 <strong>v2.22.0-v2.29.0</strong> 中,对于绝对值大于等于 1 的 num,结果为 num%; 对于绝对值小于等于 1 的 num,结果为 (num*100)%。在 <strong>v2.30.0</strong> 版本及之后统一为 (num*100)%。
 </Notice>
 
 `precision` 可以设置小数点后保留位数, 用于设置精度  
@@ -179,7 +181,7 @@ function Demo() {
             </Numeral>
 
             <Numeral rule="percentages" style={{ marginBottom: 12 }}>
-                这场比赛我的胜率是60,输的概率是40
+                这场比赛我的胜率是0.6,输的概率是0.4
             </Numeral>
 
             <Numeral rule="bytes-decimal" precision={2} truncate="floor">
@@ -213,10 +215,10 @@ function Demo() {
 
     function Infos() {
         const data = [
-            { type: 'Stars', min: '6200' },
-            { type: 'Fork', min: '400' },
-            { type: '下载', min: '3000000' },
-            { type: '贡献者', min: '60' }
+            { type: 'Stars', min: '6700' },
+            { type: 'Fork', min: '500' },
+            { type: 'Downloads', min: '3000000' },
+            { type: 'Contributors', min: '90' }
         ];
         return data.map(item =>
             <p key={item.min}>

+ 84 - 19
content/feedback/popconfirm/index-en-US.md

@@ -153,31 +153,79 @@ import { Popconfirm, Button, Toast } from '@douyinfe/semi-ui';
 };
 ```
 
+### Initialize the Focus Position of Popup Layer
+
+`okButtonProps` and `cancelButtonProps` support passing in the `autoFocus` parameter, which will automatically focus at this position when the panel is opened. Version 2.30.0 supported.
+
+`content` supports function, and its parameter is an object, which binds `initialFocusRef` to the focusable DOM or component, and it will automatically focus at this position when the panel is opened. Version 2.30.0 supported.
+
+```jsx live=true
+import React from 'react';
+import { Button, Popconfirm, Space } from '@douyinfe/semi-ui';
+
+() => {
+    return (
+        <Space>
+            <Popconfirm
+                title="Are you sure you want to save this edit?"
+                content="This modification will be irreversible"
+                okButtonProps={{
+                    autoFocus: true,
+                    type: 'danger',
+                }}
+            >
+                <Button>Confirm focus</Button>
+            </Popconfirm>
+            <Popconfirm
+                title="Are you sure you want to save this edit?"
+                content="This modification will be irreversible"
+                cancelButtonProps={{
+                    autoFocus: true,
+                }}
+            >
+                <Button>Cancel focus</Button>
+            </Popconfirm>
+            <Popconfirm
+                title="Are you sure you want to save this edit?"
+                content={({ initialFocusRef }) => {
+                    return <input ref={initialFocusRef} placeholder="focus here" />;
+                }}
+            >
+                <Button>Content focus</Button>
+            </Popconfirm>
+        </Space>
+    );
+};
+```
+
 ### Use with Tooltip or Popover
 
 Please refer to [Use with Tooltip/Popover](/en-US/show/tooltip#Use-with-Popver-or-Popconfirm)
 
 ## API Reference
 
-| Properties         | Instructions                                                                                                                                                          | Type                     | Default             | Version           |
-| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ------------------- | ----------------- |
-| arrowPointAtCenter | Whether the "small triangle" points to the center of the element, you need to pass in "showArrow = true" at the same time                                             | boolean                  | false               | **0.34.0** |
-| cancelText         | Cancel button text                                                                                                                                                    | string                   | "Cancel"            |
-| cancelButtonProps  | Properties for cancel button                                                                                                                                          | object                   |                     | **0.29.0**        |
-| cancelType         | Cancel button type                                                                                                                                                    | string                   | "tertiary"          |
-| content            | Content displayed                                                                                                                                                     | string \| ReactNode        |                     |
-| defaultVisible     | Bubble box is displayed by default                                                                                                                                    | boolean                  |                     | **0.19.0**        |
-| disabled           | Click on the Pop confirmation box to see if the bubbles pop up.                                                                                                       | boolean                  | false               |
-| getPopupContainer  | Specify the parent DOM, and the pop-up layer will be rendered into the DOM. Customization needs to set `position: relative`                                                                                                       | Function():HTMLElement         | () => document.body |
-| icon               | Custom pop bubble Icon icon                                                                                                                                           |  ReactNode      | <IconAlertTriangle size="extra-large" />    |
+| Properties         | Instructions                                                                                                                                                          | Type                       | Default             | Version           |
+| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | ------------------- | ----------------- |
+| arrowPointAtCenter | Whether the "small triangle" points to the center of the element, you need to pass in "showArrow = true" at the same time                                             | boolean                    | false               | **0.34.0** |
+| cancelText         | Cancel button text                                                                                                                                                    | string                     | "Cancel"            |
+| cancelButtonProps  | Properties for cancel button                                                                                                                                          | object                     |                     | **0.29.0**        |
+| cancelType         | Cancel button type                                                                                                                                                    | string                     | "tertiary"          |
+| closeOnEsc         | Whether to close the panel by pressing the Esc key in the trigger or popup layer. It does not take effect when visible is under controlled | boolean | true | **2.8.0** |
+| content            | Content displayed (function type, supported in version 2.30.0)                                                                                                         | ReactNode\|({ initialFocusRef }) => ReactNode        |                     |
+| defaultVisible     | Bubble box is displayed by default                                                                                                                                    | boolean                    |                     | **0.19.0**        |
+| disabled           | Click on the Pop confirmation box to see if the bubbles pop up.                                                                                                       | boolean                    | false               |
+| getPopupContainer  | Specify the parent DOM, and the pop-up layer will be rendered into the DOM. Customization needs to set `position: relative`                                                                                                       | Function():HTMLElement           | () => document.body |
+| guardFocus         | When the focus is in the popup layer, toggle whether the Tab makes the focus loop in the popup layer | boolean | true | **2.8.0** |
+| icon               | Custom pop bubble Icon icon                                                                                                                                           |  ReactNode        | <IconAlertTriangle size="extra-large" />    |
 | motion             | Whether there is animation when the drop-down list appears/hidden. You can customize animation by passing in an object that conforms to the structure | boolean | true |
-| position           | Directions, optional values: `top`, `topLeft`, `topRight`, `leftTop`, `leftBottom`, `rightTop`, `rightTop`, `rightBottom`, `bottomLeft`, `bottomRight`, `bottomRight` | string                   | "bottomLeft"        |
-| okText             | Confirm button text                                                                                                                                | string                         | "Confirm"              |
-| okType             | Confirm button type                                                                                                                                | string                         | "primary"           |
-| okButtonProps      | Confirm button props                                                                                                                            | object                         |                     | **0.29.0**        |
-| showArrow          | Whether to show arrow triangle                                                                                                                         | boolean                        | false               |                   |
-| stopPropagation    | Whether to prevent the click event on the bomb layer from bubbling                                                                                                                | boolean                        | true                | **0.34.0** |
-| position           | Popup layer position,Optional value:`top`,`topLeft`,`topRight`,`left`,`leftTop`,`leftBottom`,<br/>`right`,`rightTop`,`rightBottom`,`bottom`,`bottomLeft`,`bottomRight` | string                         | "bottomLeft"        |
+| position           | Directions, optional values: `top`, `topLeft`, `topRight`, `leftTop`, `leftBottom`, `rightTop`, `rightTop`, `rightBottom`, `bottomLeft`, `bottomRight`, `bottomRight` | string                     | "bottomLeft"        |
+| okText             | Confirm button text                                                                                                                                | string                           | "Confirm"              |
+| okType             | Confirm button type                                                                                                                                | string                           | "primary"           |
+| okButtonProps      | Confirm button props                                                                                                                            | object                           |                     | **0.29.0**        |
+| showArrow          | Whether to show arrow triangle                                                                                                                         | boolean                          | false               |                   |
+| stopPropagation    | Whether to prevent the click event on the bomb layer from bubbling                                                                                                                | boolean                          | true                | **0.34.0** |
+| position           | Popup layer position,Optional value:`top`,`topLeft`,`topRight`,`left`,`leftTop`,`leftBottom`,<br/>`right`,`rightTop`,`rightBottom`,`bottom`,`bottomLeft`,`bottomRight` | string                           | "bottomLeft"        |
+| returnFocusOnClose | After pressing the Esc key, whether the focus returns to the trigger, it only takes effect when the trigger is set to click | boolean | true | **2.8.0** |
 | title              | Displayed title                                                                                                                                  | string\|ReactNode                |                     |
 | trigger            | Timing to trigger the display, optional value:hover / focus / click / custom                                                                                         | string              |   'click'                  |
 | visible            | Whether the bubble box displays controlled attributes                                                                                                                   | boolean                        |                     | **0.19.0**        |
@@ -185,7 +233,24 @@ Please refer to [Use with Tooltip/Popover](/en-US/show/tooltip#Use-with-Popver-o
 | onConfirm          | Click the confirmation button to call back. Promise support after v2.19                                                                                                                           | (e) => void \| Promise                |                     |
 | onCancel           | Click the Cancel button to call back. Promise support after v2.19                                                                                                                          | (e) => void \| Promise                |                     |
 | onVisibleChange    | Bubble box toggle shows hidden callbacks                                                                                                                              | (visible: boolean) => void | () => {}            | **0.19.0**        |
-| onClickOutSide     | Callback when the pop-up layer is in the display state and the non-Children, non-floating layer inner area is clicked                                                 | (e: event) => void       |                     | **2.1.0**        |
+| onEscKeyDown | Called when Esc key is pressed in trigger or popup layer | function(e:event) | | **2.8.0** |
+| onClickOutSide     | Callback when the pop-up layer is in the display state and the non-Children, non-floating layer inner area is clicked                                                 | (e: event) => void         |                     | **2.1.0**        |
+
+## Accessibility
+
+### ARIA
+
+For ARIA, please refer to [Popover](https://semi.design/zh-CN/show/popover#ARIA)
+
+### Keyboard and focus
+
+- Popconfirm must have trigger, trigger can be focused, use `Enter` key to open Popconfirm
+- After Popconfirm is activated, press the arrow key ⬇️ to move the focus to Popconfirm. The initial focus of Popconfirm should follow several principles:
+    - If the Popconfirm contains the last step of an irreversible process, such as: deleting data, etc., then this initial focus is preferably on the least destructive interactable element, such as: the cancel button (by passing the `autoFocus` to the object `cancelButtonProps`)
+    - If you only read text in Popconfirm, it is recommended to set the initial focus on the most likely interactive elements, such as: confirm button (implemented by passing `autoFocus` to the object `okButtonProps` )
+- Keyboard users can dismiss Popconfirm by pressing `Esc` and focus should return to the trigger. After the user closes the Pop through the interactive element within the Popconfirm, the focus should also return to the trigger (only when trigger is `click`)
+- When it is opened, after the user clicks `Esc` in the blank space in Popconfirm, the focus will also return to the trigger (only when trigger is `click`)
+
 ## Design Tokens
 <DesignToken/>
 

+ 89 - 26
content/feedback/popconfirm/index.md

@@ -151,6 +151,50 @@ import { Popconfirm, Button, Toast } from '@douyinfe/semi-ui';
 };
 ```
 
+### 初始化弹出层焦点位置
+
+okButtonProps 和 cancelButtonProps 支持传入 `autoFocus` 参数,传入后打开面板时会自动聚焦在该位置。2.30.0 版本支持。
+
+content 支持传入函数,它的入参是一个对象,将 `initialFocusRef` 绑定在可聚焦 DOM 或组件上,打开面板时会自动聚焦在该位置。2.30.0 版本支持。
+
+```jsx live=true
+import React from 'react';
+import { Button, Popconfirm, Space } from '@douyinfe/semi-ui';
+
+() => {
+    return (
+        <Space>
+            <Popconfirm
+                title="确定是否要保存此修改?"
+                content="此修改将不可逆"
+                okButtonProps={{
+                    autoFocus: true,
+                    type: 'danger',
+                }}
+            >
+                <Button>确认聚焦</Button>
+            </Popconfirm>
+            <Popconfirm
+                title="确定是否要保存此修改?"
+                content="此修改将不可逆"
+                cancelButtonProps={{
+                    autoFocus: true,
+                }}
+            >
+                <Button>取消聚焦</Button>
+            </Popconfirm>
+            <Popconfirm
+                title="确定是否要保存此修改?"
+                content={({ initialFocusRef }) => {
+                    return <input ref={initialFocusRef} placeholder="focus here" />;
+                }}
+            >
+                <Button>内容聚焦</Button>
+            </Popconfirm>
+        </Space>
+    );
+};
+```
 
 ### 搭配 Tooltip 或 Popover 使用
 
@@ -158,32 +202,51 @@ import { Popconfirm, Button, Toast } from '@douyinfe/semi-ui';
 
 ## API 参考
 
-| 属性               | 说明                                                                                                                                        | 类型                           | 默认值              | 版本              |
-| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ------------------- | ----------------- |
-| arrowPointAtCenter | “小三角”是否指向元素中心,需要同时传入"showArrow=true"                                                                                      | boolean                        | false               | **0.34.0** |
-| cancelText         | 取消按钮文字                                                                                                                                | string                         | "取消"              |
-| cancelButtonProps  | 取消按钮的 props                                                                                                                            | object                         |                     | **0.29.0**        |
-| cancelType         | 取消按钮类型                                                                                                                                | string                         | "tertiary"          |
-| content            | 显示的内容                                                                                                                                  | string\|ReactNode                |                     |
-| defaultVisible     | 气泡框默认是否展示                                                                                                                          | boolean                        |                     | **0.19.0**        |
-| disabled           | 点击 Popconfirm 子元素是否弹出气泡确认框                                                                                                    | boolean                        | false               |
-| getPopupContainer  | 指定父级 DOM,弹层将会渲染至该 DOM 中,自定义时容器需要设置 `position: relative`                                                                                                       | Function():HTMLElement         | () => document.body |
-| icon               | 自定义弹出气泡 Icon 图标                                                                                                                    | ReactNode              | <IconAlertTriangle size="extra-large" />    |
-| motion             | 下拉列表出现/隐藏时,是否有动画 | boolean | true |
-| okText             | 确认按钮文字                                                                                                                                | string                         | "确认"              |
-| okType             | 确认按钮类型                                                                                                                                | string                         | "primary"           |
-| okButtonProps      | 确认按钮的 props                                                                                                                            | object                         |                     | **0.29.0**        |
-| position           | 方向,可选值:`top`,`topLeft`,`topRight`,`left`,`leftTop`,`leftBottom`,<br/>`right`,`rightTop`,`rightBottom`,`bottom`,`bottomLeft`,`bottomRight` | string                         | "bottomLeft"        |
-| showArrow          | 是否显示箭头三角形                                                                                                                          | boolean                        | false               |                   |
-| stopPropagation    | 是否阻止弹层上的点击事件冒泡                                                                                                                | boolean                        | true                | **0.34.0** |
-| title              | 显示的标题                                                                                                                                  | string\|ReactNode                |                     |
-| trigger            | 触发展示的时机,可选值:hover / focus / click / custom                                                                                         | string              |   'click'                  |
-| visible            | 气泡框是否展示的受控属性                                                                                                                    | boolean                        |                     | **0.19.0**        |
-| zIndex             | 浮层 z-index 值                                                                                                                             | number                         | 1030                |
-| onConfirm          | 点击确认按钮回调,  Promise类型于 v 2.19后支持                                                                                                                          | Function(e): void \| Promise                      |                     |
-| onCancel           | 点击取消按钮回调,Promise类型于 v 2.19后支持                                                                                                                            | Function(e): void \| Promise                      |                     |
-| onClickOutSide     | 当弹出层处于展示状态,点击非Children、非浮层内部区域时的回调                                                                                      | Function(e)                    |  **2.1.0**      |
-| onVisibleChange    | 气泡框切换显示隐藏的回调                                                                                                               | Function(visible: boolean): void | () => {}            | **0.19.0**        |
+| 属性 | 说明 | 类型 | 默认值 | 版本 |
+| --- | --- | --- | --- | --- |
+| arrowPointAtCenter | “小三角”是否指向元素中心,需要同时传入"showArrow=true" | boolean | false | **0.34.0** |
+| cancelText | 取消按钮文字 | string | "取消" |
+| cancelButtonProps | 取消按钮的 props | object |  | **0.29.0** |
+| cancelType | 取消按钮类型 | string | "tertiary" |
+| closeOnEsc | 在 trigger 聚焦时或在弹出层内聚焦元素上按 Esc 键是否关闭面板,受控时不生效 | boolean | true | **2.8.0** |
+| content | 显示的内容(函数类型,2.10.0 版本支持) | ReactNode\|({ initialFocusRef }) => ReactNode |  |
+| defaultVisible | 气泡框默认是否展示 | boolean |  | **0.19.0** |
+| disabled | 点击 Popconfirm 子元素是否弹出气泡确认框 | boolean | false |
+| getPopupContainer | 指定父级 DOM,弹层将会渲染至该 DOM 中,自定义时容器需要设置 `position: relative` | Function():HTMLElement | () => document.body |
+| guardFocus | 当焦点处于弹出层内时,切换 Tab 是否让焦点在弹出层内循环 | boolean | true | **2.8.0** |
+| icon | 自定义弹出气泡 Icon 图标 | ReactNode | <IconAlertTriangle size="extra-large" /> |
+| motion | 下拉列表出现/隐藏时,是否有动画 | boolean | true |
+| okText | 确认按钮文字 | string | "确认" |
+| okType | 确认按钮类型 | string | "primary" |
+| okButtonProps | 确认按钮的 props | object |  | **0.29.0** |
+| position | 方向,可选值:`top`,`topLeft`,`topRight`,`left`,`leftTop`,`leftBottom`,<br/>`right`,`rightTop`,`rightBottom`,`bottom`,`bottomLeft`,`bottomRight` | string | "bottomLeft" |
+| returnFocusOnClose | 按下 Esc 键后,焦点是否回到 trigger 上,只有设置 trigger 为 click 时生效 | boolean | true | **2.8.0** |
+| showArrow | 是否显示箭头三角形 | boolean | false |  |
+| stopPropagation | 是否阻止弹层上的点击事件冒泡 | boolean | true | **0.34.0** |
+| title | 显示的标题 | string\|ReactNode |  |
+| trigger | 触发展示的时机,可选值:hover / focus / click / custom | string | 'click' |
+| visible | 气泡框是否展示的受控属性 | boolean |  | **0.19.0** |
+| zIndex | 浮层 z-index 值 | number | 1030 |
+| onConfirm | 点击确认按钮回调 | Function(e) |  |
+| onCancel | 点击取消按钮回调 | Function(e) |  |
+| onClickOutSide | 当弹出层处于展示状态,点击非 Children、非浮层内部区域时的回调 | Function(e) | **2.1.0** |
+| onEscKeyDown | 在 trigger 或弹出层按 Esc 键时调用 | function(e:event) |  | **2.8.0** |
+| onVisibleChange | 气泡框切换显示隐藏的回调 | Function(visible: boolean): void | () => {} | **0.19.0** |
+
+## Accessibility
+
+### ARIA
+
+语义化请参考 [Popover](https://semi.design/zh-CN/show/popover#ARIA)
+
+### 键盘和焦点
+
+- Popconfirm 必须带有触发器,触发器可被聚焦,使用 Enter 键打开 Popconfirm
+- Popconfirm 激活后,按下方向键 ⬇️ 将焦点移动到 Popconfirm 上。Popconfirm 的初始焦点应当遵循以下几个原则:
+    - 如果 Popconfirm 内包含一个不可逆转过程的最后一个步骤,比如:删除数据等,那么这个初始焦点最好放在破坏性最小的可交互元素上,如:关闭按钮 ( 通过向对象 cancelButtonProps 中传入 autoFocus 实现)
+    - 如果 Popconfirm 内仅为阅读文本,那么建议将初始焦点设置在最可能常用的交互元素上,如:确定按钮 ( 通过向对象 okButtonProps 中传入 autoFocus 实现)
+- 键盘用户能够通过按 Esc 关闭 Popconfirm,并且焦点应该返回到触发器上。用户通过 Popconfirm 内的交互元素关闭该 Pop 后,焦点也应当返回到触发器上(仅当 trigger 为 click 时)
+- 打开的情况下,用户点击 Popconfirm 内的空白处 Esc 后,焦点也会回到触发器上 (仅当 trigger 为 click 时)
 
 ## 设计变量
 <DesignToken/>

+ 8 - 0
content/input/cascader/index-en-US.md

@@ -1788,6 +1788,14 @@ function Demo() {
 | loading    | loading                        | boolean        | -       |
 | value      | Value property (required)      | string\|number | -       |
 
+## Methods
+Some internal methods provided by Select can be accessed through ref:
+
+| Method      | Instructions                    | Version |
+| ----------- | ------------------------------- | ------- |
+| close       | Manually close dropdown list    | v2.30.0 |
+| open        | Manually open dropdown list     | v2.30.0 |
+
 ## Accessibility
 
 ### ARIA

+ 9 - 0
content/input/cascader/index.md

@@ -1776,6 +1776,15 @@ function Demo() {
 | loading  | 正在加载                | boolean        | -      |
 | value    | 属性值(必填)            | string\|number | -      |
 
+## Methods
+
+绑定在组件实例上的方法,可以通过 ref 调用实现某些特殊交互
+
+| 方法        | 说明                                | 版本    |
+| ----------- | ----------------------------------- | ------- |
+| close       | 调用时可以手动关闭下拉列表          | v2.30.0 |
+| open        | 调用时可以手动展开下拉列表          | v2.30.0 |
+
 ## Accessibility
 
 ### ARIA

+ 2 - 0
content/other/locale/index.md

@@ -218,6 +218,7 @@ class I18nDemo extends React.Component {
         let language = {
             'zh_CN': zh_CN,
             'en_GB': en_GB,
+            'en_US': en_US,
             'ko_KR': ko_KR,
             'ja_JP': ja_JP,
             'ar': ar,
@@ -423,6 +424,7 @@ class I18nDemo extends React.Component {
                 <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_US'>英语(美)</Select.Option>
                         <Select.Option value='en_GB'>英语(英)</Select.Option>
                         <Select.Option value='ja_JP'>日语</Select.Option>
                         <Select.Option value='ko_KR'>韩语</Select.Option>

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

@@ -16,6 +16,19 @@ Version:Major.Minor.Patch (follow the **Semver** specification)
 
 ---
 
+#### 🎉 2.30.0-beta.0 (2023-02-20)
+- 【Breaking Change】
+    - **Modify the calculation rules when the rule in the Numeral component is percentages**
+- 【Feat】
+    - Popconfirm supports A11y keyboard and focus  [#205](https://github.com/DouyinFE/semi-design/issues/205)
+    - Cascader supports calling the open/close method through ref
+    - Optimize the display format of the date at the bottom of the DatePicker panel, and configure it according to different locale language habits [@jacob-lcs](https://github.com/jacob-lcs)
+- 【Style】
+    - Solid Tag close button adds hover state color var(--semi-color-white) and active state color var(--semi-color-white)(opacity 0.9), default color from var(--semi-color-white) Change to var(--semi-color-white)(opacity 0.8).
+- 【Fix】
+    - Fix DatePicker panel moving in multiple mode  [#1422](https://github.com/DouyinFE/semi-design/issues/1422)
+    - Fix the wrong localeCode of nl-NL language pack, the scope of influence (v2.29.0-beta.0) [@jacob-lcs](https://github.com/jacob-lcs)
+
 #### 🎉 2.29.0 (2023-02-10)
 - 【Feat】
     - Locale add Swedish: sv_SE、 Polish: pl_PL、Dutch: nl_NL [#1410](https://github.com/DouyinFE/semi-design/issues/1410)

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

@@ -15,6 +15,18 @@ Semi 版本号遵循 **Semver** 规范(主版本号-次版本号-修订版本
 
 
 ---
+#### 🎉 2.30.0-beta.0 (2023-02-20)
+- 【Breaking Change】
+    - **修改 Numeral 组件中 rule 为 percentages 时候的计算规则**
+- 【Feat】
+    - Popconfirm 支持 A11y 键盘和焦点  [#205](https://github.com/DouyinFE/semi-design/issues/205)
+    - Cascader 支持通过ref调用open/close方法 
+    - 优化 DatePicker 面板底部日期的展示格式,根据不同 locale 语言习惯配置 [@jacob-lcs](https://github.com/jacob-lcs)
+- 【Style】
+    - solid Tag 关闭按钮增加 hover 态颜色 var(--semi-color-white)和 active 态颜色 var(--semi-color-white)(opacity 0.9),default 颜色从 var(--semi-color-white)改为 var(--semi-color-white)(opacity 0.8)。
+- 【Fix】
+    - 修复 DatePicker 多选面板移动问题  [#1422](https://github.com/DouyinFE/semi-design/issues/1422)
+    - 修复 nl-NL 语言包 localeCode 错误的问题,影响范围 (v2.29.0-beta.0) [@jacob-lcs](https://github.com/jacob-lcs)
 
 #### 🎉 2.29.0 (2023-02-10)
 - 【Feat】

+ 58 - 0
cypress/integration/popconfirm.spec.js

@@ -0,0 +1,58 @@
+// popConfirm.spec.js created with Cypress
+//
+// Start writing your Cypress tests below!
+// If you're unfamiliar with how Cypress works,
+// check out the link below and learn how to write your first test:
+// https://on.cypress.io/writing-first-test
+
+// Start writing your Cypress tests below!
+// If you're unfamiliar with how Cypress works,
+// check out the link below and learn how to write your first test:
+// https://on.cypress.io/writing-first-test
+
+describe('popConfirm', () => {
+    it('confirm focus', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=popconfirm--keyboard-and-focus&args=&viewMode=story');
+        cy.get('[data-cy=initial-focus-confirm]').click();
+        cy.get('.semi-popconfirm-footer .semi-button').eq(1).should('be.focused');
+        cy.get('.semi-popconfirm-footer .semi-button').eq(1).click();
+        // return focus to trigger
+        cy.get('[data-cy=initial-focus-confirm] .semi-button').should('be.focused');
+    });
+
+    it('cancel focus', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=popconfirm--keyboard-and-focus&args=&viewMode=story');
+        cy.get('[data-cy=initial-focus-cancel]').click();
+        cy.get('.semi-popconfirm-footer .semi-button').eq(0).should('be.focused');
+        cy.get('.semi-popconfirm-footer .semi-button').eq(0).click();
+        cy.get('[data-cy=initial-focus-cancel] .semi-button').should('be.focused');
+    });
+
+    it('content focus', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=popconfirm--keyboard-and-focus&args=&viewMode=story');
+        cy.get('[data-cy=initial-focus-content]').click();
+        cy.get('.semi-popconfirm-body input').eq(0).should('be.focused');
+        cy.get('.semi-popconfirm-header .semi-popconfirm-btn-close').eq(0).click();
+        cy.get('[data-cy=initial-focus-content] .semi-button').should('be.focused');
+    });
+
+    it('content esc keydown', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=popconfirm--esc-key-down&viewMode=story');
+        cy.get('[data-cy=content]').click();
+        cy.get('.test-ok').type('{esc}');
+        cy.get('.test-ok').should('not.exist');
+    });
+
+    it('content esc keydown', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=popconfirm--esc-key-down&viewMode=story', {
+            onBeforeLoad(win) {
+                cy.stub(win.console, 'log').as('consoleLog');
+            },
+        });
+        cy.get('[data-cy=trigger]').click();
+        cy.get('.test-text').click();
+        cy.get('@consoleLog').should('be.calledWith', 'clicked');
+        cy.get('.test-text').type('{esc}');
+        cy.get('.test-ok').should('not.exist');
+    });
+});

+ 1 - 1
lerna.json

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

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

@@ -1,47 +1,47 @@
 {
-  "name": "@douyinfe/semi-animation-react",
-  "version": "2.29.0",
-  "description": "motion library for semi-ui-react",
-  "keywords": [
-    "motion",
-    "react",
-    "semi-ui"
-  ],
-  "files": [
-    "lib",
-    "README.md"
-  ],
-  "license": "MIT",
-  "main": "lib/cjs/index.js",
-  "module": "lib/es/index.js",
-  "typings": "lib/es/index.d.ts",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/DouyinFE/semi-design"
-  },
-  "scripts": {
-    "test": "echo \"Error: run tests from root\" && exit 1",
-    "build:lib": "node scripts/compileLib",
-    "prepublishOnly": "npm run build:lib"
-  },
-  "dependencies": {
-    "@douyinfe/semi-animation": "2.12.0",
-    "@douyinfe/semi-animation-styled": "2.23.2",
-    "classnames": "^2.2.6"
-  },
-  "devDependencies": {
-    "@babel/preset-env": "^7.15.8",
-    "@babel/preset-react": "^7.14.5",
-    "@storybook/addon-knobs": "^6.3.1",
-    "@vx/gradient": "0.0.199",
-    "del": "^6.0.0",
-    "flubber": "^0.4.2",
-    "gulp": "^4.0.2",
-    "gulp-babel": "^8.0.0",
-    "gulp-typescript": "^6.0.0-alpha.1",
-    "merge2": "^1.4.1",
-    "prop-types": "^15.7.2",
-    "react-storybook-addon-props-combinations": "^1.1.0"
-  },
-  "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
+    "name": "@douyinfe/semi-animation-react",
+    "version": "2.30.0-beta.0",
+    "description": "motion library for semi-ui-react",
+    "keywords": [
+        "motion",
+        "react",
+        "semi-ui"
+    ],
+    "files": [
+        "lib",
+        "README.md"
+    ],
+    "license": "MIT",
+    "main": "lib/cjs/index.js",
+    "module": "lib/es/index.js",
+    "typings": "lib/es/index.d.ts",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DouyinFE/semi-design"
+    },
+    "scripts": {
+        "test": "echo \"Error: run tests from root\" && exit 1",
+        "build:lib": "export NODE_OPTIONS=--no-experimental-fetch && node scripts/compileLib",
+        "prepublishOnly": "npm run build:lib"
+    },
+    "dependencies": {
+        "@douyinfe/semi-animation": "2.12.0",
+        "@douyinfe/semi-animation-styled": "2.23.2",
+        "classnames": "^2.2.6"
+    },
+    "devDependencies": {
+        "@babel/preset-env": "^7.15.8",
+        "@babel/preset-react": "^7.14.5",
+        "@storybook/addon-knobs": "^6.3.1",
+        "@vx/gradient": "0.0.199",
+        "del": "^6.0.0",
+        "flubber": "^0.4.2",
+        "gulp": "^4.0.2",
+        "gulp-babel": "^8.0.0",
+        "gulp-typescript": "^6.0.0-alpha.1",
+        "merge2": "^1.4.1",
+        "prop-types": "^15.7.2",
+        "react-storybook-addon-props-combinations": "^1.1.0"
+    },
+    "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
 }

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

@@ -1,43 +1,43 @@
 {
-  "name": "@douyinfe/semi-animation-styled",
-  "version": "2.29.0",
-  "description": "semi styled animation",
-  "keywords": [
-    "semi",
-    "styled",
-    "animation"
-  ],
-  "homepage": "",
-  "license": "MIT",
-  "main": "lib/cjs/index.js",
-  "module": "lib/es/index.js",
-  "typings": "lib/es/index.d.ts",
-  "directories": {
-    "lib": "lib",
-    "test": "__tests__"
-  },
-  "files": [
-    "lib",
-    "README.md"
-  ],
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/DouyinFE/semi-design"
-  },
-  "scripts": {
-    "test": "echo \"Error: run tests from root\" && exit 1",
-    "build:lib": "node scripts/compileLib",
-    "prepublishOnly": "npm run build:lib"
-  },
-  "devDependencies": {
-    "@babel/plugin-proposal-decorators": "^7.15.8",
-    "@babel/plugin-transform-runtime": "^7.15.8",
-    "@babel/preset-env": "^7.15.8",
-    "del": "^6.0.0",
-    "gulp": "^4.0.2",
-    "gulp-babel": "^8.0.0",
-    "gulp-typescript": "^6.0.0-alpha.1",
-    "merge2": "^1.4.1"
-  },
-  "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
+    "name": "@douyinfe/semi-animation-styled",
+    "version": "2.30.0-beta.0",
+    "description": "semi styled animation",
+    "keywords": [
+        "semi",
+        "styled",
+        "animation"
+    ],
+    "homepage": "",
+    "license": "MIT",
+    "main": "lib/cjs/index.js",
+    "module": "lib/es/index.js",
+    "typings": "lib/es/index.d.ts",
+    "directories": {
+        "lib": "lib",
+        "test": "__tests__"
+    },
+    "files": [
+        "lib",
+        "README.md"
+    ],
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DouyinFE/semi-design"
+    },
+    "scripts": {
+        "test": "echo \"Error: run tests from root\" && exit 1",
+        "build:lib": "export NODE_OPTIONS=--no-experimental-fetch && node scripts/compileLib -tag node",
+        "prepublishOnly": "npm run build:lib"
+    },
+    "devDependencies": {
+        "@babel/plugin-proposal-decorators": "^7.15.8",
+        "@babel/plugin-transform-runtime": "^7.15.8",
+        "@babel/preset-env": "^7.15.8",
+        "del": "^6.0.0",
+        "gulp": "^4.0.2",
+        "gulp-babel": "^8.0.0",
+        "gulp-typescript": "^6.0.0-alpha.1",
+        "merge2": "^1.4.1"
+    },
+    "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
 }

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

@@ -1,46 +1,46 @@
 {
-  "name": "@douyinfe/semi-animation",
-  "version": "2.29.0",
-  "description": "animation base library for semi-ui",
-  "keywords": [
-    "animation",
-    "semi"
-  ],
-  "homepage": "",
-  "license": "MIT",
-  "main": "lib/cjs/index.js",
-  "module": "lib/es/index.js",
-  "typings": "lib/es/index.d.ts",
-  "directories": {
-    "lib": "lib",
-    "test": "__tests__"
-  },
-  "files": [
-    "lib",
-    "docs",
-    "README.md"
-  ],
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/DouyinFE/semi-design"
-  },
-  "scripts": {
-    "test": "echo \"Error: run tests from root\" && exit 1",
-    "build:lib": "node scripts/compileLib",
-    "prepublishOnly": "npm run build:lib"
-  },
-  "dependencies": {
-    "bezier-easing": "^2.1.0"
-  },
-  "devDependencies": {
-    "@babel/plugin-transform-runtime": "^7.15.8",
-    "@babel/preset-env": "^7.15.8",
-    "del": "^6.0.0",
-    "gulp": "^4.0.2",
-    "gulp-babel": "^8.0.0",
-    "gulp-typescript": "^6.0.0-alpha.1",
-    "merge2": "^1.4.1",
-    "react-storybook-addon-props-combinations": "^1.1.0"
-  },
-  "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
+    "name": "@douyinfe/semi-animation",
+    "version": "2.30.0-beta.0",
+    "description": "animation base library for semi-ui",
+    "keywords": [
+        "animation",
+        "semi"
+    ],
+    "homepage": "",
+    "license": "MIT",
+    "main": "lib/cjs/index.js",
+    "module": "lib/es/index.js",
+    "typings": "lib/es/index.d.ts",
+    "directories": {
+        "lib": "lib",
+        "test": "__tests__"
+    },
+    "files": [
+        "lib",
+        "docs",
+        "README.md"
+    ],
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DouyinFE/semi-design"
+    },
+    "scripts": {
+        "test": "echo \"Error: run tests from root\" && exit 1",
+        "build:lib": "export NODE_OPTIONS=--no-experimental-fetch && node scripts/compileLib",
+        "prepublishOnly": "npm run build:lib"
+    },
+    "dependencies": {
+        "bezier-easing": "^2.1.0"
+    },
+    "devDependencies": {
+        "@babel/plugin-transform-runtime": "^7.15.8",
+        "@babel/preset-env": "^7.15.8",
+        "del": "^6.0.0",
+        "gulp": "^4.0.2",
+        "gulp-babel": "^8.0.0",
+        "gulp-typescript": "^6.0.0-alpha.1",
+        "merge2": "^1.4.1",
+        "react-storybook-addon-props-combinations": "^1.1.0"
+    },
+    "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
 }

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

@@ -1,43 +1,43 @@
 {
-  "name": "eslint-plugin-semi-design",
-  "version": "2.29.0",
-  "description": "semi ui eslint plugin",
-  "keywords": [
-    "semi",
-    "eslint"
-  ],
-  "author": "shijia.me <[email protected]>",
-  "homepage": "https://semi.design",
-  "license": "MIT",
-  "main": "lib/index.js",
-  "directories": {
-    "lib": "lib",
-    "test": "__tests__"
-  },
-  "files": [
-    "lib",
-    "README.md",
-    "README-zh_CN.md"
-  ],
-  "publishConfig": {
-    "registry": "https://registry.npmjs.org"
-  },
-  "repository": {
-    "type": "git",
-    "url": "git+https://github.com/DouyinFE/semi-design.git"
-  },
-  "scripts": {
-    "build:lib": "rm -rf lib && tsc",
-    "prepublishOnly": "npm run build:lib",
-    "test": "node __tests__/index.js"
-  },
-  "devDependencies": {
-    "typescript": "^4"
-  },
-  "peerDependencies": {
-    "eslint": ">=0.8.0"
-  },
-  "bugs": {
-    "url": "https://github.com/DouyinFE/semi-design/issues"
-  }
+    "name": "eslint-plugin-semi-design",
+    "version": "2.30.0-beta.0",
+    "description": "semi ui eslint plugin",
+    "keywords": [
+        "semi",
+        "eslint"
+    ],
+    "author": "shijia.me <[email protected]>",
+    "homepage": "https://semi.design",
+    "license": "MIT",
+    "main": "lib/index.js",
+    "directories": {
+        "lib": "lib",
+        "test": "__tests__"
+    },
+    "files": [
+        "lib",
+        "README.md",
+        "README-zh_CN.md"
+    ],
+    "publishConfig": {
+        "registry": "https://registry.npmjs.org"
+    },
+    "repository": {
+        "type": "git",
+        "url": "git+https://github.com/DouyinFE/semi-design.git"
+    },
+    "scripts": {
+        "build:lib": "rm -rf lib && tsc",
+        "prepublishOnly": "npm run build:lib",
+        "test": "node __tests__/index.js"
+    },
+    "devDependencies": {
+        "typescript": "^4"
+    },
+    "peerDependencies": {
+        "eslint": ">=0.8.0"
+    },
+    "bugs": {
+        "url": "https://github.com/DouyinFE/semi-design/issues"
+    }
 }

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

@@ -1,9 +1,9 @@
 {
     "name": "@douyinfe/semi-foundation",
-    "version": "2.29.0",
+    "version": "2.30.0-beta.0",
     "description": "",
     "scripts": {
-        "build:lib": "node ./scripts/compileLib.js",
+        "build:lib": "export NODE_OPTIONS=--no-experimental-fetch && node ./scripts/compileLib.js",
         "prepublishOnly": "npm run build:lib"
     },
     "dependencies": {

+ 20 - 1
packages/semi-foundation/popconfirm/popconfirmFoundation.ts

@@ -1,5 +1,7 @@
 /* eslint-disable @typescript-eslint/no-empty-function */
 
+import { get } from 'lodash';
+
 import BaseFoundation, { DefaultAdapter } from '../base/foundation';
 import isPromise from '../utils/isPromise';
 
@@ -10,7 +12,10 @@ export interface PopconfirmAdapter<P = Record<string, any>, S = Record<string, a
     notifyConfirm: (e: any) => Promise<any> | void;
     notifyCancel: (e: any) => Promise<any> | void;
     notifyVisibleChange: (visible: boolean) => void;
-    notifyClickOutSide: (e: any) => void
+    notifyClickOutSide: (e: any) => void;
+    focusCancelButton: () => void;
+    focusOkButton: () => void;
+    focusPrevFocusElement: () => void
 }
 
 export default class PopConfirmFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<PopconfirmAdapter<P, S>, P, S> {
@@ -63,6 +68,20 @@ export default class PopConfirmFoundation<P = Record<string, any>, S = Record<st
         if (!this._isControlledComponent('visible')) {
             this._adapter.setVisible(visible);
         }
+        if (visible) {
+            this.handleFocusOperateButton();
+        } else {
+            this._adapter.focusPrevFocusElement();
+        }
         this._adapter.notifyVisibleChange(visible);
     }
+
+    handleFocusOperateButton() {
+        const { cancelButtonProps, okButtonProps } = this._adapter.getProps() as any;
+        if (get(cancelButtonProps, 'autoFocus') && !get(cancelButtonProps, 'disabled')) {
+            this._adapter.focusCancelButton();
+        } else if (get(okButtonProps, 'autoFocus') && !get(okButtonProps, 'disabled')) {
+            this._adapter.focusOkButton();
+        }
+    }
 }

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

@@ -36,6 +36,7 @@ $overflowList: #{$prefix}-overflow-list;
 
     &:focus {
         border: $width-select-border-focus solid $color-select-border-focus;
+        background-color: $color-select-bg-focus;
         outline: 0;
     }
 

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

@@ -2,6 +2,7 @@
 $color-select-bg-default: var(--semi-color-fill-0); // 选择器输入框背景色 - 默认态
 $color-select-bg-hover: var(--semi-color-fill-1); // 选择器输入框背景色 - 悬停态
 $color-select-bg-active: var(--semi-color-fill-2); // 选择器输入框背景色 - 按下态
+$color-select-bg-focus: var(--semi-color-fill-0); // 选择器输入框背景色 - 聚焦态
 $color-select-border-default: transparent; // 选择器输入框描边颜色
 $color-select-border-hover: $color-select-border-default;  // 选择器输入框描边颜色 - 悬浮
 $color-select-border-active: var(--semi-color-focus-border); // 选择器输入框描边颜色 - 按下态

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

@@ -1129,7 +1129,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
      * 如果 trigger 是 focus 或者 hover,则它绑定了 onFocus,这里我们如果重新 focus 的话,popup 会再次打开
      * 因此 returnFocusOnClose 只支持 click trigger
      */
-    _focusTrigger() {
+    focusTrigger() {
         const { trigger, returnFocusOnClose, preventScroll } = this.getProps();
         if (returnFocusOnClose && trigger !== 'custom') {
             const triggerNode = this._adapter.getTriggerNode();
@@ -1144,7 +1144,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
         if (trigger !== 'custom') {
             // Move the focus into the trigger first and then close the pop-up layer 
             // to avoid the problem of opening the pop-up layer again when the focus returns to the trigger in the case of hover and focus
-            this._focusTrigger();
+            this.focusTrigger();
             this.hide();
         }
         this._adapter.notifyEscKeydown(event);

+ 6 - 0
packages/semi-foundation/treeSelect/treeSelect.scss

@@ -26,6 +26,12 @@ $module: #{$prefix}-tree-select;
         outline: 0;
     }
 
+    &-focus:hover {
+        border: $width-treeSelect_focus-border solid $color-treeSelect_default-border-focus;
+        outline: 0;
+    }
+
+
     &-warning {
         background-color: $color-treeSelect_warning-bg-default;
         border-color: $color-treeSelect_warning-border-default;

+ 1 - 1
packages/semi-foundation/treeSelect/variables.scss

@@ -6,7 +6,7 @@ $color-treeSelect_default-icon-hover: var(--semi-color-primary-hover); // 树选
 $color-treeSelect_default-icon-active: var(--semi-color-primary-active); // 树选择器选择框清空按钮颜色 - 按下
 $color-treeSelect_default-bg-hover: var(--semi-color-fill-1); // 树选择器选择框背景颜色 - 悬停
 
-$color-treeSelect_default-border-hover: inherit; // 树选择器选择框描边颜色 - 悬浮
+$color-treeSelect_default-border-hover: transparent; // 树选择器选择框描边颜色 - 悬浮
 $color-treeSelect_default-border-focus: var(--semi-color-focus-border);// 树选择器选择框描边颜色 - 选中
 
 $color-treeSelect_search-border-default: var(--semi-color-fill-0); // 树选择器菜单搜索框描边颜色 - 默认

+ 2 - 5
packages/semi-foundation/typography/formatNumeral.ts

@@ -42,11 +42,8 @@ export default class FormatNumeral {
             return `${this.truncatePrecision(value)} ${units[i]}`;
         },
         percentages: (value: number) => {
-            const cArr = value.toString().split('.');
-            if (Number(cArr[0]) === 0) {
-                return `${this.truncatePrecision(value * 100)}%`;
-            }
-            return `${this.truncatePrecision(value)}%`;
+            // The rules here have been modified in version v2.30.0
+            return `${this.truncatePrecision(value * 100)}%`;
         },
         exponential: (value: number) => {
             const vExponential = value.toExponential(this.precision + 2);

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

@@ -1,63 +1,63 @@
 {
-  "name": "@douyinfe/semi-icons",
-  "version": "2.29.0",
-  "description": "semi icons",
-  "keywords": [
-    "semi",
-    "icons"
-  ],
-  "author": "",
-  "homepage": "",
-  "main": "lib/cjs/index.js",
-  "module": "lib/es/index.js",
-  "typings": "lib/es/index.d.ts",
-  "license": "MIT",
-  "files": [
-    "lib",
-    "src",
-    "dist"
-  ],
-  "sideEffects": [
-    "*.scss",
-    "*.css",
-    "lib/es/index.js"
-  ],
-  "scripts": {
-    "clean": "rimraf dist lib",
-    "build:icon": "node scripts/build-icon ",
-    "build:lib": "node ./scripts/compileLib.js",
-    "build:js": "npm run build:lib && node scripts/compileDist.js",
-    "prepublishOnly": "npm run clean && npm run build:js"
-  },
-  "dependencies": {
-    "classnames": "^2.2.6"
-  },
-  "devDependencies": {
-    "@babel/preset-env": "^7.15.8",
-    "@babel/preset-react": "^7.14.5",
-    "@douyinfe/semi-webpack-plugin": "2.23.2",
-    "babel-loader": "^8.2.2",
-    "css-loader": "4.3.0",
-    "del": "^6.0.0",
-    "gulp": "^4.0.2",
-    "gulp-babel": "^8.0.0",
-    "gulp-replace": "^1.1.3",
-    "gulp-sass": "^5.0.0",
-    "gulp-typescript": "^6.0.0-alpha.1",
-    "merge2": "^1.4.1",
-    "mini-css-extract-plugin": "0.11.3",
-    "rimraf": "^3.0.2",
-    "terser-webpack-plugin": "^4.2.3",
-    "through2": "^4.0.2",
-    "ts-loader": "^5.4.5",
-    "webpack": "^4.46.0"
-  },
-  "peerDependencies": {
-    "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
-  },
-  "_unpkg": true,
-  "unpkgFiles": [
-    "*"
-  ],
-  "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
+    "name": "@douyinfe/semi-icons",
+    "version": "2.30.0-beta.0",
+    "description": "semi icons",
+    "keywords": [
+        "semi",
+        "icons"
+    ],
+    "author": "",
+    "homepage": "",
+    "main": "lib/cjs/index.js",
+    "module": "lib/es/index.js",
+    "typings": "lib/es/index.d.ts",
+    "license": "MIT",
+    "files": [
+        "lib",
+        "src",
+        "dist"
+    ],
+    "sideEffects": [
+        "*.scss",
+        "*.css",
+        "lib/es/index.js"
+    ],
+    "scripts": {
+        "clean": "rimraf dist lib",
+        "build:icon": "node scripts/build-icon ",
+        "build:lib": "export NODE_OPTIONS=\"--no-experimental-fetch --openssl-legacy-provider\" && node ./scripts/compileLib.js",
+        "build:js": "export NODE_OPTIONS=\"--no-experimental-fetch --openssl-legacy-provider\" && npm run build:lib && node scripts/compileDist.js",
+        "prepublishOnly": "npm run clean && npm run build:js"
+    },
+    "dependencies": {
+        "classnames": "^2.2.6"
+    },
+    "devDependencies": {
+        "@babel/preset-env": "^7.15.8",
+        "@babel/preset-react": "^7.14.5",
+        "@douyinfe/semi-webpack-plugin": "2.23.2",
+        "babel-loader": "^8.2.2",
+        "css-loader": "4.3.0",
+        "del": "^6.0.0",
+        "gulp": "^4.0.2",
+        "gulp-babel": "^8.0.0",
+        "gulp-replace": "^1.1.3",
+        "gulp-sass": "^5.0.0",
+        "gulp-typescript": "^6.0.0-alpha.1",
+        "merge2": "^1.4.1",
+        "mini-css-extract-plugin": "0.11.3",
+        "rimraf": "^3.0.2",
+        "terser-webpack-plugin": "^4.2.3",
+        "through2": "^4.0.2",
+        "ts-loader": "^5.4.5",
+        "webpack": "^4.46.0"
+    },
+    "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+    },
+    "_unpkg": true,
+    "unpkgFiles": [
+        "*"
+    ],
+    "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
 }

+ 1 - 1
packages/semi-icons/scripts/compileDist.js

@@ -3,7 +3,7 @@ const getWebpackConfig = require('../webpack.config');
 
 function compile() {
     return new Promise((resolve, reject) => {
-        console.log('compile jsx start');
+        console.log('compile jsx start', process.env);
         const config = getWebpackConfig({ minimize: false });
         webpack(config, (err, stats) => {
             if (err) {

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

@@ -1,48 +1,48 @@
 {
-  "name": "@douyinfe/semi-illustrations",
-  "version": "2.29.0",
-  "description": "semi illustrations",
-  "keywords": [
-    "semi",
-    "illustrations"
-  ],
-  "author": "",
-  "homepage": "",
-  "license": "MIT",
-  "files": [
-    "lib",
-    "src",
-    "dist"
-  ],
-  "main": "lib/cjs/index.js",
-  "module": "lib/es/index.js",
-  "typings": "lib/es/index.d.ts",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/DouyinFE/semi-design"
-  },
-  "peerDependencies": {
-    "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
-  },
-  "devDependencies": {
-    "case-sensitive-paths-webpack-plugin": "2.4.0",
-    "del": "^6.0.0",
-    "gulp": "^4.0.2",
-    "gulp-esbuild": "0.10.4",
-    "gulp-replace": "^1.1.3",
-    "gulp-typescript": "^6.0.0-alpha.1",
-    "merge2": "^1.4.1",
-    "rimraf": "^3.0.2",
-    "terser-webpack-plugin": "^4.2.3",
-    "ts-loader": "^5.4.5",
-    "webpack": "^4.46.0"
-  },
-  "scripts": {
-    "clean": "rimraf dist lib",
-    "build:icon": "node scripts/build-illustration",
-    "build:lib": "node ./scripts/compileLib.js",
-    "build:js": "npm run build:lib && node scripts/compileDist.js",
-    "prepublishOnly": "npm run clean && npm run build:js"
-  },
-  "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
+    "name": "@douyinfe/semi-illustrations",
+    "version": "2.30.0-beta.0",
+    "description": "semi illustrations",
+    "keywords": [
+        "semi",
+        "illustrations"
+    ],
+    "author": "",
+    "homepage": "",
+    "license": "MIT",
+    "files": [
+        "lib",
+        "src",
+        "dist"
+    ],
+    "main": "lib/cjs/index.js",
+    "module": "lib/es/index.js",
+    "typings": "lib/es/index.d.ts",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/DouyinFE/semi-design"
+    },
+    "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+    },
+    "devDependencies": {
+        "case-sensitive-paths-webpack-plugin": "2.4.0",
+        "del": "^6.0.0",
+        "gulp": "^4.0.2",
+        "gulp-esbuild": "0.10.4",
+        "gulp-replace": "^1.1.3",
+        "gulp-typescript": "^6.0.0-alpha.1",
+        "merge2": "^1.4.1",
+        "rimraf": "^3.0.2",
+        "terser-webpack-plugin": "^4.2.3",
+        "ts-loader": "^5.4.5",
+        "webpack": "^4.46.0"
+    },
+    "scripts": {
+        "clean": "rimraf dist lib",
+        "build:icon": "node scripts/build-illustration",
+        "build:lib": "export NODE_OPTIONS=--no-experimental-fetch && node ./scripts/compileLib.js",
+        "build:js": "npm run build:lib && node scripts/compileDist.js",
+        "prepublishOnly": "npm run clean && npm run build:js"
+    },
+    "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
 }

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

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

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

@@ -1,36 +1,36 @@
 {
-  "name": "@douyinfe/semi-scss-compile",
-  "version": "2.29.0",
-  "description": "compile semi scss to css",
-  "author": "[email protected]",
-  "license": "MIT",
-  "main": "lib/index.js",
-  "types": "lib/index.d.ts",
-  "bin": {
-    "semi-build-scss": "lib/bin.js"
-  },
-  "files": [
-    "lib",
-    "src"
-  ],
-  "keywords": [
-    "semi-scss-compiler",
-    "scss"
-  ],
-  "scripts": {
-    "build:lib": "rimraf lib && tsc",
-    "prepublishOnly": "npm run build:lib"
-  },
-  "dependencies": {
-    "arg": "^5.0.1",
-    "fs-extra": "^8.1.0",
-    "lodash": "^4.17.21",
-    "sass": "^1.54.9"
-  },
-  "devDependencies": {
-    "@types/lodash": "^4.14.176",
-    "rimraf": "^3.0.2",
-    "typescript": "^4.4.4"
-  },
-  "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
+    "name": "@douyinfe/semi-scss-compile",
+    "version": "2.30.0-beta.0",
+    "description": "compile semi scss to css",
+    "author": "[email protected]",
+    "license": "MIT",
+    "main": "lib/index.js",
+    "types": "lib/index.d.ts",
+    "bin": {
+        "semi-build-scss": "lib/bin.js"
+    },
+    "files": [
+        "lib",
+        "src"
+    ],
+    "keywords": [
+        "semi-scss-compiler",
+        "scss"
+    ],
+    "scripts": {
+        "build:lib": "rimraf lib && tsc",
+        "prepublishOnly": "npm run build:lib"
+    },
+    "dependencies": {
+        "arg": "^5.0.1",
+        "fs-extra": "^8.1.0",
+        "lodash": "^4.17.21",
+        "sass": "^1.54.9"
+    },
+    "devDependencies": {
+        "@types/lodash": "^4.14.176",
+        "rimraf": "^3.0.2",
+        "typescript": "^4.4.4"
+    },
+    "gitHead": "eb34a4f25f002bb4cbcfa51f3df93bed868c831a"
 }

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

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

+ 16 - 0
packages/semi-ui/cascader/__test__/cascader.test.js

@@ -1353,4 +1353,20 @@ describe('Cascader', () => {
         ).toEqual('亚洲');
         cascader4.unmount();
     });
+
+    it('ref method', () => {
+        let r;
+        let props = {
+            ref: (ref) => { r = ref },
+            filter: true,
+            multiple: true,
+        };
+
+        let select = render(props);
+        r.open();
+        expect(select.state().isOpen).toEqual(true);
+
+        r.close();
+        expect(select.state().isOpen).toEqual(false);
+    });
 });

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

@@ -1,4 +1,4 @@
-import React, { useState, useCallback, useEffect } from 'react';
+import React, { useState, useCallback, useEffect, useRef } from 'react';
 import CustomTrigger from './CustomTrigger';
 import { Button, Typography, Toast, Cascader, Checkbox } from '../../index';
 
@@ -1828,3 +1828,55 @@ export const filterRender = () => {
       </div>
   );
 };
+
+export const RefMethods = () => {
+  const cRef = useRef(null);
+
+  const onClickOpen = useCallback(() => {
+    cRef.current.open();
+  }, [cRef]);
+
+  const onClickClose = useCallback(() => {
+    cRef.current.close();
+  }, [cRef]);
+
+  const treeData = [
+    {
+        label: '浙江省',
+        value: 'zhejiang',
+        children: [
+            {
+                label: '杭州市',
+                value: 'hangzhou',
+                children: [
+                    {
+                        label: '西湖区',
+                        value: 'xihu',
+                    },
+                    {
+                        label: '萧山区',
+                        value: 'xiaoshan',
+                    },
+                    {
+                        label: <div onClick={onClickClose}> click to hide</div>,
+                        value: 'linan',
+                    },
+                ],
+            },
+        ],
+    }
+  ];
+  return (
+      <div>
+          <Button onClick={onClickOpen}> cascader visible</Button>
+          <br />
+          <Cascader
+              multiple
+              ref={cRef}
+              style={{ width: 300 }}
+              treeData={treeData}
+              placeholder="单选"
+          />
+      </div>
+  );
+};

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

@@ -613,6 +613,14 @@ class Cascader extends BaseComponent<CascaderProps, CascaderState> {
         this.foundation.handleListScroll(e, ind);
     };
 
+    close() {
+        this.foundation.close();
+    }
+
+    open() {
+        this.foundation.open();
+    }
+
     renderContent = () => {
         const {
             inputValue,

+ 5 - 5
packages/semi-ui/locale/source/ar.ts

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'ar',
     dateFnsLocale: arSA,
     Pagination: {
-        pageSize: 'عدد العناصر في كل صفحة : ${pageSize} ',
-        total: 'مجموع الصفحات: ${total}',
+        pageSize: 'العناصر في كل صفحة: ${pageSize}',
+        total: 'إجمالي الصفحات: ${total}',
         jumpTo: 'اقفز إلى',
         page: ' الصفحات',
     },
@@ -82,7 +82,7 @@ const local: Locale = {
             Sun: 'الأحد‬',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'dd-MM-yyyy',
+            FORMAT_SWITCH_DATE: 'yyyy/MM/dd',
         },
     },
     Popconfirm: {
@@ -146,8 +146,8 @@ const local: Locale = {
         clear: 'واضح',
         selectAll: 'اختر الكل',
         clearSelectAll: 'إلغاء تحديد الكل',
-        total: "الكمية الإجمالية: ${total}",
-        selected: "الكمية المحددة: ${total}"
+        total: "إجمالي العناصر: ${total}",
+        selected: "العناصر المحددة: ${total}"
     },
     Form: {
         optional: '(اختياري)',

+ 5 - 5
packages/semi-ui/locale/source/de.ts

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'de',
     dateFnsLocale: de,
     Pagination: {
-        pageSize: '${pageSize} Artikel / Seite',
-        total: '${total} Seiten',
+        pageSize: 'Elemente pro Seite: ${pageSize}',
+        total: 'Seiten gesamt: ${total}',
         jumpTo: 'Springen zu',
         page: ' Seiten',
     },
@@ -82,7 +82,7 @@ const local: Locale = {
             Sun: 'So.',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd.MM.yyyy',
         },
     },
     Popconfirm: {
@@ -146,8 +146,8 @@ const local: Locale = {
         clear: 'Löschen',
         selectAll: 'Alles auswählen',
         clearSelectAll: 'Alles abwählen',
-        total: 'Gesamt ${total} Artikel',
-        selected: '${total} ausgewählte Artikel',
+        total: 'Elemente gesamt: ${total}',
+        selected: 'Elemente ausgewählt: ${total}',
     },
     Form: {
         optional: '(Optional)',

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

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'en-GB',
     dateFnsLocale: enGB,
     Pagination: {
-        pageSize: '${pageSize} items / page',
-        total: '${total} pages',
+        pageSize: 'Items per page: ${pageSize}',
+        total: 'Total pages: ${total}',
         jumpTo: 'Jump to',
         page: ' page',
     },
@@ -82,7 +82,7 @@ const local: Locale = {
             Sun: 'Sun',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'MM/dd/yyyy',
         },
     },
     Popconfirm: {
@@ -146,8 +146,8 @@ const local: Locale = {
         clear: 'Clear',
         selectAll: 'Select all',
         clearSelectAll: 'Unselect all',
-        total: 'Total ${total} items',
-        selected: '${total} items selected',
+        total: 'Total items: ${total}',
+        selected: 'Items selected: ${total}',
     },
     Form: {
         optional: '(optional)',

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

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'en-US',
     dateFnsLocale: enUS,
     Pagination: {
-        pageSize: '${pageSize} items / page',
-        total: '${total} pages',
+        pageSize: 'Items per page: ${pageSize}',
+        total: 'Total pages: ${total}',
         jumpTo: 'Jump to',
         page: ' page',
     },
@@ -146,8 +146,8 @@ const local: Locale = {
         clear: 'Clear',
         selectAll: 'Select all',
         clearSelectAll: 'Unselect all',
-        total: 'Total ${total} items',
-        selected: '${total} items selected',
+        total: 'Total items: ${total}',
+        selected: 'Items selected: ${total}',
     },
     Form: {
         optional: '(optional)',

+ 5 - 5
packages/semi-ui/locale/source/es.ts

@@ -10,8 +10,8 @@ const locale: Locale = {
     code: 'es',
     dateFnsLocale: es,
     Pagination: {
-        pageSize: '${pageSize} objetos / página',
-        total: '${total} páginas',
+        pageSize: 'Elementos por página: ${pageSize}',
+        total: 'Páginas totales: ${total}',
         jumpTo: 'Ir a',
         page: ' páginas',
     },
@@ -87,7 +87,7 @@ const locale: Locale = {
             Sun: 'Dom',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd/MM/yyyy',
         },
     },
     Popconfirm: {
@@ -151,8 +151,8 @@ const locale: Locale = {
         clear: 'Limpiar',
         selectAll: 'Seleccionar todo',
         clearSelectAll: 'Deseleccionar todo',
-        total: 'Total ${total} objetos',
-        selected: '${total} objetos seleccionados',
+        total: 'Elementos totales: ${total}',
+        selected: 'Elementos seleccionados: ${total}',
     },
     Form: {
         optional: '(opcional)',

+ 5 - 5
packages/semi-ui/locale/source/fr.ts

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'fr',
     dateFnsLocale: fr,
     Pagination: {
-        pageSize: '${pageSize} articles/page',
-        total: '${total} pages',
+        pageSize: 'Éléments par page : ${pageSize}',
+        total: 'Total des pages : ${total}',
         jumpTo: 'Sauter à',
         page: ' pages',
     },
@@ -82,7 +82,7 @@ const local: Locale = {
             Sun: 'dim.',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd/MM/yyyy',
         },
     },
     Popconfirm: {
@@ -146,8 +146,8 @@ const local: Locale = {
         clear: 'Supprimer',
         selectAll: 'Sélectionner tout',
         clearSelectAll: 'Désélectionner tout',
-        total: 'Totale ${total} articles',
-        selected: '${total} articles sélectionnés',
+        total: 'Total des éléments : ${total}',
+        selected: 'Éléments sélectionnés : ${total}',
     },
     Form: {
         optional: '(optionnel)',

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

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'id-ID',
     dateFnsLocale: id,
     Pagination: {
-        pageSize: '${pageSize} item / halaman',
-        total: '${total} halaman',
+        pageSize: 'Item per halaman: ${pageSize}',
+        total: 'Total halaman: ${total}',
         jumpTo: 'Langsung ke',
         page: ' halaman',
     },
@@ -146,8 +146,8 @@ const local: Locale = {
         clear: 'Bersihkan',
         selectAll: 'Pilih Semua',
         clearSelectAll: 'Nyahpilih Semua',
-        total: 'Total ${total} proyek',
-        selected: '${total} item dipilih',
+        total: 'Total item: ${total}',
+        selected: 'Item dipilih: ${total}',
     },
     Form: {
         optional: '(opsional)',

+ 5 - 5
packages/semi-ui/locale/source/it.ts

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'it',
     dateFnsLocale: it,
     Pagination: {
-        pageSize: '${pageSize} elementi / pagine',
-        total: '${total} pagine',
+        pageSize: 'Elementi per pagina: ${pageSize}',
+        total: 'Pagine totali: ${total}',
         jumpTo: 'Vai a',
         page: ' pagine',
     },
@@ -82,7 +82,7 @@ const local: Locale = {
             Sun: 'Dom',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd/MM/yyyy',
         },
     },
     Popconfirm: {
@@ -146,8 +146,8 @@ const local: Locale = {
         clear: 'Cancella',
         selectAll: 'Seleziona tutto',
         clearSelectAll: 'Deseleziona tutto',
-        total: 'Totale ${total} elementi',
-        selected: '${total} elementi selezionati',
+        total: 'Elementi totali: ${total}',
+        selected: 'Elementi selezionati: ${total}',
     },
     Form: {
         optional: '(opzionale)',

+ 5 - 5
packages/semi-ui/locale/source/ja_JP.ts

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'ja-JP',
     dateFnsLocale: ja,
     Pagination: {
-        pageSize: '${pageSize} 個数 / ページ',
-        total: '合計 ${total} ページ',
+        pageSize: '1ページあたりのアイテム数:${pageSize}',
+        total: '合計ページ数:${total}',
         jumpTo: 'ページへ',
         page: 'ページ',
     },
@@ -83,7 +83,7 @@ const local: Locale = {
             Sun: '日',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'yyyy/MM/dd',
         },
     },
     Popconfirm: {
@@ -147,8 +147,8 @@ const local: Locale = {
         clear: '空っぽ',
         selectAll: 'すべて選択',
         clearSelectAll: 'すべてを選択解除',
-        total: '合計 ${total} アイテム',
-        selected: '選択済み ${total} アイテム',
+        total: '合計アイテム数:${total}',
+        selected: '選択されているアイテム数:${total}',
     },
     Form: {
         optional: '(オプション)',

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

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'ko-KR',
     dateFnsLocale: ko,
     Pagination: {
-        pageSize: '${pageSize} 기사 / 페이지',
-        total: '${total} 페이지',
+        pageSize: '페이지당 항목: ${pageSize}',
+        total: '총 페이지: ${total}',
         jumpTo: '이동',
         page: '페이지',
     },
@@ -147,8 +147,8 @@ const local: Locale = {
         clear: '비우기',
         selectAll: '모두 선택',
         clearSelectAll: '모두 선택 취소',
-        total: '총 {total} 개 항목',
-        selected: '선택된 {Total} 개 항목',
+        total: '총 항목: ${total}',
+        selected: '선택한 항목: ${total}',
     },
     Form: {
         optional: '(선택 과목)',

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

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'ms-MY',
     dateFnsLocale: ms,
     Pagination: {
-        pageSize: ' ${pageSize} items / halaman',
-        total: '${total} halaman',
+        pageSize: 'Item setiap halaman: ${pageSize}',
+        total: 'Jumlah halaman: ${total}',
         jumpTo: 'Lompat ke',
         page: ' halaman',
     },
@@ -146,8 +146,8 @@ const local: Locale = {
         clear: 'Kosongkan',
         selectAll: 'Pilih Semua',
         clearSelectAll: 'Nyahpilih semua',
-        total: 'Jumlah ${total} item',
-        selected: '${total} projek dipilih',
+        total: 'Jumlah item: ${total}',
+        selected: 'Item dipilih: ${total}',
     },
     Form: {
         optional: '(pilihan)',

+ 6 - 6
packages/semi-ui/locale/source/nl_NL.ts

@@ -3,18 +3,18 @@ import { nl } from 'date-fns/locale';
 import { Locale } from '../interface';
 
 /**
- * [i18n-Dutch (pl_PL)]
+ * [i18n-Dutch (nl_NL)]
  * 荷兰语
  *
  */
 
 const local: Locale = {
-    code: 'pl_PL',
+    code: 'nl_NL',
     dateFnsLocale: nl, // locale code to dateFns locale
     Pagination: {
         page: 'pagina',
-        pageSize: 'Artikelen per pagina: ${pageSize}',
-        total: "Totaal ${total} pagina's",
+        pageSize: 'Items per pagina: ${pageSize}',
+        total: "Totaal aantal pagina's: ${total}",
         jumpTo: 'Ga naar'
     },
     Modal: {
@@ -153,8 +153,8 @@ const local: Locale = {
         clear: 'Wissen',
         selectAll: 'Alles selecteren',
         clearSelectAll: 'Alle selecties opheffen',
-        total: '${total} stuks in totaal',
-        selected: '${total} artikelen geselecteerd',
+        total: 'Totaal aantal items: ${total}',
+        selected: 'Wybrane pozycje: ${total}',
     },
     Form: {
         optional: 'Optioneel',

+ 4 - 4
packages/semi-ui/locale/source/pl_PL.ts

@@ -13,8 +13,8 @@ const local: Locale = {
     code: 'pl_PL',
     dateFnsLocale: pl, // locale code to dateFns locale
     Pagination: {
-        pageSize: 'Pozycje na stronie: ${pageSize}',
-        total: 'Razem stron: ${total}',
+        pageSize: 'Liczba pozycji na stronie: ${pageSize}',
+        total: 'Strony ogółem: ${total}',
         jumpTo: 'Przejdź do',
         page: 'stron',
     },
@@ -154,8 +154,8 @@ const local: Locale = {
         clear: 'Wyczyść',
         selectAll: 'Zaznacz wszystkie',
         clearSelectAll: 'Usuń zaznaczenie wszystkich',
-        total: 'Całkowita ilość: ${total}',
-        selected: 'Wybrana ilość: ${total}', 
+        total: 'Pozycje ogółem: ${total}',
+        selected: 'Wybrane pozycje: ${total}', 
     },
     Form: {
         optional: '(Opcjonalnie)',

+ 5 - 5
packages/semi-ui/locale/source/pt_BR.ts

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'pt-BR',
     dateFnsLocale: ptBR,
     Pagination: {
-        pageSize: '${pageSize} artigo /página',
-        total: 'Total ${total} página',
+        pageSize: 'Itens por página: ${pageSize}',
+        total: 'Total de páginas: ${total}',
         jumpTo: 'Pule para',
         page: 'página',
     },
@@ -90,7 +90,7 @@ const local: Locale = {
             Sun: 'Sun',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd-MM-yyyy',
         },
     },
     Navigation: {
@@ -154,8 +154,8 @@ const local: Locale = {
         clear: 'Vazio',
         selectAll: 'selecionar tudo',
         clearSelectAll: 'Cancelar selecionar tudo',
-        total: 'Total de ${total} itens',
-        selected: '${total} itens selecionados',
+        total: 'Total de itens: ${total}',
+        selected: 'Itens selecionados: ${total}',
     },
     Form: {
         optional: '(opcional)',

+ 17 - 4
packages/semi-ui/locale/source/ro.ts

@@ -7,7 +7,7 @@ export default {
     code: 'ro',
     dateFnsLocale: ro,
     Pagination: {
-        pageSize: 'Articole pe pagină: ${pageSize}',
+        pageSize: 'Elemente per pagină: ${pageSize}',
         total: 'Total pagini: ${total}',
         jumpTo: 'Treci la',
         page: 'pagini',
@@ -84,7 +84,7 @@ export default {
             Sun: 'dum',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd.MM.yyyy',
         },
     },
     Popconfirm: {
@@ -147,10 +147,23 @@ export default {
         clear: 'Șterge',
         selectAll: 'Selectează toate',
         clearSelectAll: 'Deselectează toate',
-        total: 'Total articole: ${total} ',
-        selected: 'articole selectate: ${total}',
+        total: 'Total elemente: ${total}',
+        selected: 'Elemente selectate: ${total}',
     },
     Form: {
         optional: '(opțional)',
     },
+    Image: {
+        preview: 'Previzualizează',
+        loading: 'Se încarcă',
+        loadError: 'Nu s-a putut încărca',
+        prevTip: 'Anterior',
+        nextTip: 'Înainte',
+        zoomInTip: 'Mărire',
+        zoomOutTip: 'Micșorare',
+        rotateTip: 'Rotește',
+        downloadTip: 'Descarcă',
+        adaptiveTip: 'Afișaj adaptabil',
+        originTip: 'Afișaj implicit',
+    },
 };

+ 4 - 4
packages/semi-ui/locale/source/ru_RU.ts

@@ -5,7 +5,7 @@ const local: Locale = {
     code: 'ru-RU',
     dateFnsLocale: ru,
     Pagination: {
-        pageSize: 'Элементов на странице: ${pageSize}',
+        pageSize: 'Позиции на странице: ${pageSize}',
         total: 'Всего страниц: ${total}',
         jumpTo: 'Прыгать в',
         page: ' страницы'
@@ -85,7 +85,7 @@ const local: Locale = {
             Sun: 'Солнце',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd.MM.yyyy',
         },
     },
     Popconfirm: {
@@ -149,8 +149,8 @@ const local: Locale = {
         clear: 'Очистить',
         selectAll: 'Выбрать все',
         clearSelectAll: 'Снять выделение',
-        total: 'Всего элементов: ${total} ',
-        selected: 'Выбрано элементов: ${total} ',
+        total: 'Всего позиций: ${total}',
+        selected: 'Выбранные позиции: ${total}',
     },
     Form: {
         optional: '(по желанию)',

+ 4 - 4
packages/semi-ui/locale/source/sv_SE.ts

@@ -10,8 +10,8 @@ const local: Locale = {
     code: 'sv_SE',
     dateFnsLocale: sv, 
     Pagination: {
-        pageSize: '${pageSize} artiklar/sida',
-        total: 'Totalt ${total} sidor',
+        pageSize: 'Artiklar per sida: ${pageSize}',
+        total: 'Totalt antal sidor: ${total}',
         jumpTo: 'Gå till',
         page: 'sida',
     },
@@ -151,8 +151,8 @@ const local: Locale = {
         clear: 'Rensa',
         selectAll: 'Markera alla',
         clearSelectAll: 'Avmarkera alla',
-        total: '${total} artiklar totalt',
-        selected: 'Valde ${total} objekt',
+        total: 'Totalt antal artiklar: ${total}',
+        selected: 'Valda artiklar: ${total}',
     },
     Form: {
         optional: '(Frivilligt)',

+ 5 - 5
packages/semi-ui/locale/source/th_TH.ts

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'th-TH',
     dateFnsLocale: th,
     Pagination: {
-        pageSize: '${pageSize} บทความ / หน้า',
-        total: 'หน้าทั้งหมด ${total}',
+        pageSize: 'รายการต่อหน้า: ${pageSize}',
+        total: 'หน้าทั้งหมด: ${total}',
         jumpTo: 'ข้ามไปที่',
         page: 'หน้า',
     },
@@ -86,7 +86,7 @@ const local: Locale = {
             Sun: 'อา',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd/MM/yyyy',
         },
     },
     Navigation: {
@@ -150,8 +150,8 @@ const local: Locale = {
         clear: 'ว่าง',
         selectAll: 'เลือกทั้งหมด',
         clearSelectAll: 'ยกเลิกการเลือกทั้งหมด',
-        total: 'รวม ${total} รายการ',
-        selected: 'เลือก ${total} รายการ',
+        total: 'รายการทั้งหมด: ${total}',
+        selected: 'รายการที่เลือก: ${total}',
     },
     Form: {
         optional: '(ไม่จำเป็น)',

+ 5 - 5
packages/semi-ui/locale/source/tr_TR.ts

@@ -6,8 +6,8 @@ const local: Locale = {
     dateFnsLocale: tr,
     Pagination: {
         page: 'Sayfa',
-        pageSize: '${pageSize} Makale / sayfa',
-        total: 'Toplam ${total} Sayfa',
+        pageSize: 'Sayfa başı öğe: ${pageSize}',
+        total: 'Toplam sayfa: ${total}',
         jumpTo: 'Atlamak'
     },
     Modal: {
@@ -86,7 +86,7 @@ const local: Locale = {
             Sun: 'Paz'
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd.MM.yyyy',
         },
     },
     Navigation:
@@ -146,8 +146,8 @@ const local: Locale = {
         clear: 'Boş',
         selectAll: 'Tümünü seç',
         clearSelectAll: 'Tümünün seçimini kaldır',
-        total: 'Toplam ${total} öğe',
-        selected: '${total} öğe seçildi'
+        total: 'Toplam öğe: ${total}',
+        selected: 'Seçilen öğeler: ${total}'
     },
     Form: {
         optional: '(isteğe bağlı)',

+ 5 - 5
packages/semi-ui/locale/source/vi_VN.ts

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'vi-VN',
     dateFnsLocale: vi,
     Pagination: {
-        pageSize: '${pageSize} Số / trang',
-        total: 'Tổng cộng ${total} Số trang',
+        pageSize: 'Số mục mỗi trang: ${pageSize}',
+        total: 'Tổng số trang: ${total}',
         jumpTo: 'Chuyển đến',
         page: ' Số trang',
     },
@@ -85,7 +85,7 @@ const local: Locale = {
             Sun: 'chủ nhật',
         },
         localeFormatToken: {
-            FORMAT_SWITCH_DATE: 'yyyy-MM-dd',
+            FORMAT_SWITCH_DATE: 'dd/MM/yyyy',
         },
     },
     Popconfirm: {
@@ -149,8 +149,8 @@ const local: Locale = {
         clear: 'Clear',
         selectAll: 'Thông thoáng',
         clearSelectAll: 'Bỏ chọn tất cả',
-        total: 'Tổng số ${total} mặt hàng',
-        selected: '${total} mục được chọn',
+        total: 'Tổng số mục: ${total}',
+        selected: 'Số mục đã chọn: ${total}',
     },
     Form: {
         optional: '(không bắt buộc)',

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

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'zh-CN',
     dateFnsLocale: zhCN, // locale code to dateFns locale
     Pagination: {
-        pageSize: '${pageSize} 条/页',
-        total: '共 ${total} 页',
+        pageSize: '每页条数:${pageSize}',
+        total: '总页数:${total}',
         jumpTo: '跳至',
         page: '页',
     },
@@ -147,8 +147,8 @@ const local: Locale = {
         clear: '清空',
         selectAll: '全选',
         clearSelectAll: '取消全选',
-        total: '共 ${total} 项',
-        selected: '已选 ${total} 项',
+        total: '总个数:${total}',
+        selected: '已选个数:${total}',
     },
     Form: {
         optional: '(可选)',

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

@@ -5,8 +5,8 @@ const local: Locale = {
     code: 'zh-TW',
     dateFnsLocale: zhTW, // locale code to dateFns locale
     Pagination: {
-        pageSize: '${pageSize} 條/頁',
-        total: '共 ${total} 頁',
+        pageSize: '每頁項目數:${pageSize}',
+        total: '總頁數:${total}',
         jumpTo: '跳至',
         page: '頁',
     },
@@ -147,8 +147,8 @@ const local: Locale = {
         clear: '清空',
         selectAll: '全選',
         clearSelectAll: '取消全選',
-        total: '共 ${total} 項',
-        selected: '已選 ${total} 項',
+        total: '總項目數:${total}',
+        selected: '選取的項目數:${total}',
     },
     Form: {
         optional: '(可選)',

+ 5 - 1
packages/semi-ui/modal/confirm.tsx

@@ -36,7 +36,11 @@ export default function confirm<T>(props: ConfirmProps) {
 
 
     function render(renderProps: ConfirmProps) {
-        ReactDOM.render(<ConfirmModal {...renderProps} motion={props.motion}/>, div);
+        const { afterClose } = renderProps;
+        ReactDOM.render(<ConfirmModal {...renderProps} afterClose={(...args:any)=>{
+            afterClose?.(...args);
+            destroy();
+        }} motion={props.motion}/>, div);
     }
 
     function close() {

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

@@ -1,13 +1,13 @@
 {
     "name": "@douyinfe/semi-ui",
-    "version": "2.29.0",
+    "version": "2.30.0-beta.0",
     "description": "",
     "main": "lib/cjs/index.js",
     "module": "lib/es/index.js",
     "typings": "lib/es/index.d.ts",
     "scripts": {
         "clean": "rimraf dist lib",
-        "build:lib": "node ./scripts/compileLib.js",
+        "build:lib": "export NODE_OPTIONS=--no-experimental-fetch && node ./scripts/compileLib.js",
         "build:js": "node scripts/compileDist.js",
         "build:css": "node scripts/compileScss.js",
         "prepublishOnly": "npm run clean && npm run build:lib && npm run build:js && npm run build:css"
@@ -17,12 +17,12 @@
         "lib/*"
     ],
     "dependencies": {
-        "@douyinfe/semi-animation": "2.29.0",
-        "@douyinfe/semi-animation-react": "2.29.0",
-        "@douyinfe/semi-foundation": "2.29.0",
-        "@douyinfe/semi-icons": "2.29.0",
-        "@douyinfe/semi-illustrations": "2.29.0",
-        "@douyinfe/semi-theme-default": "2.29.0",
+        "@douyinfe/semi-animation": "2.30.0-beta.0",
+        "@douyinfe/semi-animation-react": "2.30.0-beta.0",
+        "@douyinfe/semi-foundation": "2.30.0-beta.0",
+        "@douyinfe/semi-icons": "2.30.0-beta.0",
+        "@douyinfe/semi-illustrations": "2.30.0-beta.0",
+        "@douyinfe/semi-theme-default": "2.30.0-beta.0",
         "async-validator": "^3.5.0",
         "classnames": "^2.2.6",
         "copy-text-to-clipboard": "^2.1.1",

+ 4 - 4
packages/semi-ui/pagination/__test__/pagination.test.js

@@ -99,7 +99,7 @@ describe('Pagination', () => {
         };
         const pag = getPagination(props);
         expect(pag.exists('.semi-page-total')).toEqual(true);
-        expect(pag.find('.semi-page-total').text()).toEqual('共 20 页')
+        expect(pag.find('.semi-page-total').text()).toEqual('总页数:20')
         expect(pag.exists('.semi-page-switch')).toEqual(true);
     })
 
@@ -120,19 +120,19 @@ describe('Pagination', () => {
         pag.setProps({ pageSize: 40 });
         pag.update();
         expect(pag.state().pageSize).toEqual(40);
-        expect(pag.find('.semi-select-selection-text').children(0).text()).toEqual('40 条/页');
+        expect(pag.find('.semi-select-selection-text').children(0).text()).toEqual('每页条数:40');
         expect(pag.find('.semi-page-item').children().length).toEqual((200/40) + 2);
         // pageSize 40 -> 100
         pag.setProps({ pageSize: 100 });
         pag.update();
         expect(pag.state().pageSize).toEqual(100);
-        expect(pag.find('.semi-select-selection-text').children(0).text()).toEqual('100 条/页');
+        expect(pag.find('.semi-select-selection-text').children(0).text()).toEqual('每页条数:100');
         expect(pag.find('.semi-page-item').children().length).toEqual((200/100) + 2);
         // pageSize 100 -> 20
         pag.setProps({ pageSize: 20 });
         pag.update();
         expect(pag.state().pageSize).toEqual(20);
-        expect(pag.find('.semi-select-selection-text').children(0).text()).toEqual('20 条/页');
+        expect(pag.find('.semi-select-selection-text').children(0).text()).toEqual('每页条数:20');
         // show ..., always 9
         expect(pag.find('.semi-page-item').children().length).toEqual(9);
     });

+ 76 - 0
packages/semi-ui/popconfirm/_story/popconfirm.stories.jsx

@@ -5,6 +5,7 @@ import Button from '../../button';
 import Input from '../../input';
 import Table from '../../table';
 import Toast from '../../toast';
+import { Space } from '../../index';
 
 import TypesConfrimDemo from './TypesConfirm';
 import DynamicDisableDemo from './DynamicDisable';
@@ -204,3 +205,78 @@ PromiseCallback.story = {
   name: 'PromiseCallbackDemo',
 };
 
+export const KeyboardAndFocus = () => {
+  return (
+    <div style={{ height: '150vh', marginTop: 200 }}>
+      <Space>
+        <div data-cy="initial-focus-confirm">
+          <Popconfirm
+              title="确定是否要保存此修改?"
+              content="此修改将不可逆"
+              okButtonProps={{
+                autoFocus: true,
+                type: 'danger',
+                className: 'test-ok',
+              }}
+          >
+              <Button>确认聚焦</Button>
+          </Popconfirm>
+        </div>
+        <div data-cy="initial-focus-cancel">
+          <Popconfirm
+              title="确定是否要保存此修改?"
+              content="此修改将不可逆"
+              cancelButtonProps={{
+                autoFocus: true,
+                className: 'test-cancel',
+              }}
+          >
+              <Button>取消聚焦</Button>
+          </Popconfirm>
+        </div>
+        <div data-cy="initial-focus-content">
+          <Popconfirm
+              title="确定是否要保存此修改?"
+              content={({ initialFocusRef }) => {
+                return <input ref={initialFocusRef} placeholder="focus here" />;
+              }}
+          >
+              <Button>内容聚焦</Button>
+          </Popconfirm>
+        </div>
+      </Space>
+    </div>
+  );
+};
+KeyboardAndFocus.storyName = "a11y focus";
+
+export const ESCKeyDown = () => {
+  return (
+    <div style={{ height: '150vh', marginTop: 200 }}>
+      <Space>
+        <div data-cy="content">
+          <Popconfirm
+              title="确定是否要保存此修改?"
+              content="此修改将不可逆"
+              okButtonProps={{
+                autoFocus: true,
+                className: 'test-ok',
+              }}
+          >
+              <Button>content</Button>
+          </Popconfirm>
+        </div>
+        <div data-cy="trigger">
+          <Popconfirm
+                title="确定是否要保存此修改?"
+                content={<div onClick={() => console.log('clicked')} className='test-text'>此修改将不可逆</div>}
+                okButtonProps={{ autoFocus: true }}
+            >
+              <Button>trigger</Button>
+          </Popconfirm>
+        </div>
+      </Space>
+    </div>
+  );
+};
+ESCKeyDown.storyName = "a11y esc keydown";

+ 41 - 13
packages/semi-ui/popconfirm/index.tsx

@@ -2,13 +2,13 @@
 import React from 'react';
 import cls from 'classnames';
 import PropTypes from 'prop-types';
-import { noop, get } from 'lodash';
+import { noop, get, isFunction, omit } from 'lodash';
 import { cssClasses, numbers } from '@douyinfe/semi-foundation/popconfirm/constants';
 import PopconfirmFoundation, { PopconfirmAdapter } from '@douyinfe/semi-foundation/popconfirm/popconfirmFoundation';
 import { IconClose, IconAlertTriangle } from '@douyinfe/semi-icons';
 import BaseComponent from '../_base/baseComponent';
 import Popover, { PopoverProps } from '../popover';
-import { Position, Trigger } from '../tooltip';
+import { Position, Trigger, RenderContentProps } from '../tooltip';
 import Button, { ButtonProps } from '../button';
 import { Type as ButtonType } from '../button/Button';
 import ConfigContext, { ContextValue } from '../configProvider/context';
@@ -20,7 +20,6 @@ export interface PopconfirmProps extends PopoverProps {
     cancelText?: string;
     cancelButtonProps?: ButtonProps;
     cancelType?: ButtonType;
-    content?: React.ReactNode;
     defaultVisible?: boolean;
     disabled?: boolean;
     icon?: React.ReactNode;
@@ -55,7 +54,7 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
     static propTypes = {
         motion: PropTypes.oneOfType([PropTypes.bool, PropTypes.func, PropTypes.object]),
         disabled: PropTypes.bool,
-        content: PropTypes.any,
+        content: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
         title: PropTypes.any,
         prefixCls: PropTypes.string,
         className: PropTypes.string,
@@ -96,6 +95,9 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
         onClickOutSide: noop,
     };
 
+    footerRef: React.RefObject<HTMLDivElement | null>;
+    popoverRef: React.RefObject<Popover | null>;
+    foundation: PopconfirmFoundation;
     constructor(props: PopconfirmProps) {
         super(props);
 
@@ -106,6 +108,8 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
         };
 
         this.foundation = new PopconfirmFoundation(this.adapter);
+        this.footerRef = React.createRef();
+        this.popoverRef = React.createRef();
     }
 
     context: ContextValue;
@@ -131,6 +135,17 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
             notifyCancel: (e: React.MouseEvent): Promise<any> | void => this.props.onCancel(e),
             notifyVisibleChange: (visible: boolean): void => this.props.onVisibleChange(visible),
             notifyClickOutSide: (e: React.MouseEvent) => this.props.onClickOutSide(e),
+            focusCancelButton: () => {
+                const buttonNode = this.footerRef?.current?.querySelector('[data-type=cancel]') as HTMLElement;
+                buttonNode?.focus({ preventScroll: true });
+            },
+            focusOkButton: () => {
+                const buttonNode = this.footerRef?.current?.querySelector('[data-type=ok]') as HTMLElement;
+                buttonNode?.focus({ preventScroll: true });
+            },
+            focusPrevFocusElement: () => {
+                this.popoverRef.current?.focusTrigger();
+            }
         };
     }
 
@@ -151,10 +166,23 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
             <LocaleConsumer componentName="Popconfirm">
                 {(locale: LocaleObject['Popconfirm'], localeCode: string) => (
                     <>
-                        <Button type={cancelType} onClick={this.handleCancel} loading={cancelLoading} {...cancelButtonProps}>
+                        <Button
+                            data-type="cancel"
+                            type={cancelType}
+                            onClick={this.handleCancel}
+                            loading={cancelLoading}
+                            {...omit(cancelButtonProps, 'autoFocus')}
+                        >
                             {cancelText || get(locale, 'cancel')}
                         </Button>
-                        <Button type={okType} theme="solid" onClick={this.handleConfirm} loading={confirmLoading} {...okButtonProps}>
+                        <Button
+                            data-type="ok"
+                            type={okType}
+                            theme="solid"
+                            onClick={this.handleConfirm}
+                            loading={confirmLoading}
+                            {...omit(okButtonProps, 'autoFocus')}
+                        >
                             {okText || get(locale, 'confirm')}
                         </Button>
                     </>
@@ -163,7 +191,7 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
         );
     }
 
-    renderConfirmPopCard() {
+    renderConfirmPopCard = ({ initialFocusRef }: { initialFocusRef?: RenderContentProps<any>['initialFocusRef'] }) => {
         const { content, title, className, style, cancelType, icon, prefixCls } = this.props;
         const { direction } = this.context;
         const popCardCls = cls(
@@ -177,7 +205,7 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
         const showContent = !(content === null || typeof content === 'undefined');
 
         return (
-            // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
+            /* eslint-disable-next-line jsx-a11y/no-static-element-interactions */
             <div className={popCardCls} onClick={this.stopImmediatePropagation} style={style}>
                 <div className={`${prefixCls}-inner`}>
                     <div className={`${prefixCls}-header`}>
@@ -202,10 +230,10 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
                     </div>
                     {showContent ? (
                         <div className={`${prefixCls}-body`} x-semi-prop="content">
-                            {content}
+                            {isFunction(content) ? content({ initialFocusRef }) : content}
                         </div>
-                    ) : null} 
-                    <div className={`${prefixCls}-footer`}>{this.renderControls()}</div>
+                    ) : null}
+                    <div className={`${prefixCls}-footer`} ref={this.footerRef}>{this.renderControls()}</div>
                 </div>
             </div>
         );
@@ -230,7 +258,6 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
         }
 
         const { visible } = this.state;
-        const popContent = this.renderConfirmPopCard();
         const popProps: PopProps = {
             onVisibleChange: this.handleVisibleChange,
             className: cssClasses.POPOVER,
@@ -243,8 +270,9 @@ export default class Popconfirm extends BaseComponent<PopconfirmProps, Popconfir
 
         return (
             <Popover
+                ref={this.popoverRef}
                 {...attrs}
-                content={popContent}
+                content={this.renderConfirmPopCard}
                 visible={visible}
                 position={position}
                 {...popProps}

+ 13 - 0
packages/semi-ui/popover/index.tsx

@@ -109,6 +109,18 @@ class Popover extends React.PureComponent<PopoverProps, PopoverState> {
     };
 
     context: ContextValue;
+    tooltipRef: React.RefObject<Tooltip | null>;
+    constructor(props: PopoverProps) {
+        super(props);
+        this.tooltipRef = React.createRef();
+    }
+
+    /**
+     * focus on tooltip trigger
+     */
+    public focusTrigger = () => {
+        this.tooltipRef.current?.focusTrigger();
+    }
 
     renderPopCard = ({ initialFocusRef }: { initialFocusRef: RenderContentProps['initialFocusRef'] }) => {
         const { content, contentClassName, prefixCls } = this.props;
@@ -166,6 +178,7 @@ class Popover extends React.PureComponent<PopoverProps, PopoverState> {
         return (
             <Tooltip
                 guardFocus
+                ref={this.tooltipRef}
                 {...(attr as any)}
                 trigger={trigger}
                 position={position}

+ 12 - 3
packages/semi-ui/tooltip/index.tsx

@@ -34,11 +34,11 @@ export interface ArrowBounding {
     height?: number
 }
 
-export interface RenderContentProps {
-    initialFocusRef?: React.RefObject<HTMLElement>
+export interface RenderContentProps<T = HTMLElement> {
+    initialFocusRef?: React.RefObject<T>
 }
 
-export type RenderContent = (props: RenderContentProps) => React.ReactNode;
+export type RenderContent<T = HTMLElement> = (props: RenderContentProps<T>) => React.ReactNode;
 
 export interface TooltipProps extends BaseProps {
     children?: React.ReactNode;
@@ -449,6 +449,13 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
         this.mounted = false;
         this.foundation.destroy();
     }
+    
+    /**
+     * focus on tooltip trigger
+     */
+    public focusTrigger() {
+        this.foundation.focusTrigger();
+    }
 
     isSpecial = (elem: React.ReactNode | HTMLElement | any) => {
         if (isHTMLElement(elem)) {
@@ -612,6 +619,8 @@ export default class Tooltip extends BaseComponent<TooltipProps, TooltipState> {
             <Portal getPopupContainer={this.props.getPopupContainer} style={{ zIndex }}>
                 {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
                 <div
+                    // listen keyboard event, don't move tabIndex -1
+                    tabIndex={-1}
                     className={`${BASE_CLASS_PREFIX}-portal-inner`}
                     style={portalInnerStyle}
                     ref={this.setContainerEl}

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

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

+ 1 - 1
src/sitePages/newHome/components/comments/comments.jsx

@@ -40,7 +40,7 @@ function numberAnimation(number, s, dom) {
     requestAnimationFrame(fn);
 }
 
-const realNumber = [6200, 400, 3000000, 60];
+const realNumber = [6700, 500, 3000000, 90];
 
 function Comments(props) {
     useEffect(()=> {

+ 1 - 1
src/sitePages/newHome/components/operateButton/operateButton.jsx

@@ -32,7 +32,7 @@ function OperateButton() {
         >
             <span style={{ display: 'flex' }}>
                 GitHub
-                <span className={styles.badge}>6.6k</span>
+                <span className={styles.badge}>6.7k</span>
             </span>
         </Button>
     </div>);

+ 1 - 0
src/sitePages/newHome/components/theme/theme.js

@@ -5793,6 +5793,7 @@ export const capCutTheme = `/* shadow */
   box-sizing: border-box;
   position: relative;
   vertical-align: middle;
+  background-color: transparent;
 }
 
 *[data-theme=theme] .semi-table-tbody > .semi-table-row > .semi-table-row-cell.resizing {

+ 0 - 74
yarn.lock

@@ -1456,15 +1456,6 @@
   resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
   integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
 
-"@douyinfe/[email protected]":
-  version "2.28.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.28.2.tgz#434089fb4b18231fd9299c3224eaebfe9ada07f2"
-  integrity sha512-8SO3JqVjNnvQebPPfP6h9QPT65+pzzt0BLL73svl7Y84RwnkiFiNNLHkfxGYy5718JtN8lrOUxrNRLlMMyg4uw==
-  dependencies:
-    "@douyinfe/semi-animation" "2.12.0"
-    "@douyinfe/semi-animation-styled" "2.23.2"
-    classnames "^2.2.6"
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation-react/-/semi-animation-react-2.9.1.tgz#f2e4c6ef7899729ee6145edf0579598ba195bfdd"
@@ -1495,13 +1486,6 @@
     "@babel/runtime-corejs3" "^7.15.4"
     bezier-easing "^2.1.0"
 
-"@douyinfe/[email protected]":
-  version "2.28.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.28.2.tgz#b420229938b7dfcb7ba93c5d717f4415213916e8"
-  integrity sha512-gQZZK+3jOPsDMnWh/kquTjyMsBlM/Qe4VYGkkGGFqcUA3jyuptO6sxR97UVg81XIX0qjzmS2RXKzSsks4INvUA==
-  dependencies:
-    bezier-easing "^2.1.0"
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-animation/-/semi-animation-2.9.1.tgz#4345fd86823b51e7c6fb5e9079d8f5c3ffe608f8"
@@ -1510,20 +1494,6 @@
     "@babel/runtime-corejs3" "^7.15.4"
     bezier-easing "^2.1.0"
 
-"@douyinfe/[email protected]":
-  version "2.28.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.28.2.tgz#6ebb55f0baf701927d8688ed2ac0e02502df2724"
-  integrity sha512-rXI2GXDULloZTxu1cu1Azb1bPbSRv5wDbfl+LuZ0SVjcm0LlLLfznIWMKN/ngaoKeZizHzu4w7SSm4XF8tDZEA==
-  dependencies:
-    "@douyinfe/semi-animation" "2.12.0"
-    async-validator "^3.5.0"
-    classnames "^2.2.6"
-    date-fns "^2.9.0"
-    date-fns-tz "^1.0.10"
-    lodash "^4.17.21"
-    memoize-one "^5.2.1"
-    scroll-into-view-if-needed "^2.2.24"
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-foundation/-/semi-foundation-2.9.1.tgz#1300bb97d6ceb92274ca4c9e6c66c5c16dc284ea"
@@ -1539,13 +1509,6 @@
     memoize-one "^5.2.1"
     scroll-into-view-if-needed "^2.2.24"
 
-"@douyinfe/[email protected]", "@douyinfe/semi-icons@^2.0.0":
-  version "2.28.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.28.2.tgz#f743abaa868eaef9e7d3c85570e4a232180d0dca"
-  integrity sha512-C0w/VCFdOGZUaU+3oINdytqQR/OWpWOfdMUaGTufHmSas/n3lmrNUyW2gpb7tO/rhgw49j6VBBU6MSW3eDq3eA==
-  dependencies:
-    classnames "^2.2.6"
-
 "@douyinfe/[email protected]", "@douyinfe/semi-icons@latest":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-icons/-/semi-icons-2.9.1.tgz#7a04e1a77070220b04f63e6f65aac30155ed8ddd"
@@ -1554,11 +1517,6 @@
     "@babel/runtime-corejs3" "^7.15.4"
     classnames "^2.2.6"
 
-"@douyinfe/[email protected]":
-  version "2.28.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.28.2.tgz#dd5ae33ed16d3f51c7af07e63445a84649c9d924"
-  integrity sha512-Ytu0CH3r/tyJMCeb2XFfyLruWhvWqDd98f3SavUzhi+E4OIXMoNYxTZ9tPBX25EHqxNtuC6LPc9S8u8IGVjA5A==
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-illustrations/-/semi-illustrations-2.9.1.tgz#1a448d1854ee1beeba57ea612da052b549ea105f"
@@ -1632,13 +1590,6 @@
     monaco-themes "^0.3.3"
     react-live "^2.2.2"
 
-"@douyinfe/[email protected]":
-  version "2.28.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.28.2.tgz#a3628047e6ee0af6cef90331a16fd313f3be8208"
-  integrity sha512-g2fgf61nCYti5gYIWXFYHSRQWuR+ASAVrvmnghCXHnVurIi2mg2tfI0S9oJzUXLC8237RW/EXoPJLuTU+yDz4A==
-  dependencies:
-    glob "^7.1.6"
-
 "@douyinfe/[email protected]":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-theme-default/-/semi-theme-default-2.9.1.tgz#734113e9783ca58b69afe1769005e7e57e5a4da7"
@@ -1646,31 +1597,6 @@
   dependencies:
     glob "^7.1.6"
 
-"@douyinfe/semi-ui@^2.0.0":
-  version "2.28.2"
-  resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.28.2.tgz#24fb27c4af1002fb5e5692dbd9e17b5b1bd35564"
-  integrity sha512-OYllvCtg0OBBg6+Qvboo8A5VLWaIfJEk3DEUz+RLEWhY2AmL/mhplO8CcIkMLR4WUiXbIY1yWkdZ8bX4q2GlAw==
-  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"
-    async-validator "^3.5.0"
-    classnames "^2.2.6"
-    copy-text-to-clipboard "^2.1.1"
-    date-fns "^2.9.0"
-    date-fns-tz "^1.0.10"
-    lodash "^4.17.21"
-    prop-types "^15.7.2"
-    react-resizable "^1.8.0"
-    react-sortable-hoc "^2.0.0"
-    react-window "^1.8.2"
-    resize-observer-polyfill "^1.5.1"
-    scroll-into-view-if-needed "^2.2.24"
-    utility-types "^3.10.0"
-
 "@douyinfe/semi-ui@latest":
   version "2.9.1"
   resolved "https://registry.yarnpkg.com/@douyinfe/semi-ui/-/semi-ui-2.9.1.tgz#505d4783ea1fa73d307b75f62091030f1fee9332"