DaiQiangReal преди 1 година
родител
ревизия
dbbf5b641a

+ 131 - 84
content/input/hotkeys/index-en-US.md

@@ -3,7 +3,7 @@ localeCode: en-US
 order: 30
 category: Input
 title:  HotKeys
-icon: doc-input
+icon: doc-configprovider
 width: 60%
 brief: used to facilitate the customization of keyboard shortcut
 ---
@@ -12,6 +12,7 @@ brief: used to facilitate the customization of keyboard shortcut
 ## Demos
 
 ### How to import
+PinCode supported from 2.66.0
 
 ```jsx import 
 import { HotKeys } from '@douyinfe/semi-ui';
@@ -20,29 +21,50 @@ import { HotKeys } from '@douyinfe/semi-ui';
 ### Explaination
 The hotkeys only support combinations of modifier keys like Shift, Control, Meta, and Alt with other keys.
 
-When a hotkey is set to a common shortcut like Ctrl/Meta + C, it may prevent the default behavior (e.g., copying) from being triggered properly.
+> [Meta](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey) corresponds to Command on macOS and Win on Windows.
+
+When setting a shortcut that overlaps with common shortcuts like Ctrl/Meta + C, the preventDefault setting can be used to control whether the default event is triggered.
+
+
 
 ### Basic Usage
 
-Pass in key combinations via `hotKeys` and bind a shortcut handler function using `onClick` to respond to the action.
+Pass in key combinations via `hotKeys` and bind a shortcut handler function using `onHotKey` to respond to the action.
 
-When pressing `Ctrl + Shift + A`, it increments the count by 1. By default, it listens on the body, making it effective globally.
+When pressing `Ctrl + Shift + A`, it opens the modal. By default, it listens on the body, making it effective globally.
 
 [values reference](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)
 
+It's also recommended to use the `HotKeys.Keys` wrapper to set hotkeys.
+
 ```jsx live=true
 import React, { useState } from 'react';
-import { HotKeys } from '@douyinfe/semi-ui';
+import { HotKeys, Modal } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
+  const hotKeys = [HotKeys.Keys.Control, 'Shift', HotKeys.Keys.A]
+  
   return (
     <div>
-      <HotKeys hotKeys={['Control', 'Shift', 'a']} onClick={onClick} ></HotKeys>
-      <div>{cnt}</div>
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} ></HotKeys>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {hotKeys.join('+')}.
+      </Modal>
     </div>
   );
 }
@@ -54,19 +76,32 @@ Set the characters through `content`
 
 ```jsx live=true
 import React, { useState } from 'react';
-import { HotKeys } from '@douyinfe/semi-ui';
+import { HotKeys, Modal } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
+  const hotKeys = [HotKeys.Keys.Control, 'Shift', HotKeys.Keys.B]
+  
   return (
     <div>
-      <HotKeys hotKeys={["Control", "b"]} onClick={onClick} content={["Ctrl", "B"]}></HotKeys>
-        <br></br>
-      <HotKeys hotKeys={["Meta","b"]} onClick={onClick} content={["⌘", "B"]}></HotKeys>
-      <div>{cnt}</div>
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} content={['Ctrl', 'Shift', 'B']}></HotKeys>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {hotKeys.join('+')}.
+      </Modal>
     </div>
   );
 }
@@ -74,51 +109,73 @@ function Demo() {
 
 Replace the element through `render`
 
-When encountering issues caused by different operating system shortcuts, you can similarly use two components and customize the rendering.
 ```jsx live=true
 import React, { useState } from 'react';
-import { HotKeys, Tag } from '@douyinfe/semi-ui';
+import { HotKeys, Modal, Tag } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const hotKeys = ["r"]
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
-  const newShortCut = () => {
-    return (
-      <div>
-        <Tag>{"Click R / K to add"}</Tag>
-      </div>
-    )
-  }
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
+  const hotKeys = [HotKeys.Keys.Control, HotKeys.Keys.R]
+  
+  const newHotKeys = <Tag>Press Ctrl+R to Open Modal</Tag>
   return (
     <div>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} render={newShortCut}></HotKeys>
-      <HotKeys hotKeys={["k"]} onClick={onClick} render={() => null}></HotKeys>
-      <div>{cnt}</div>
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} render={newHotKeys}></HotKeys>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {hotKeys.join('+')}.
+      </Modal>
     </div>
   );
 }
 ```
 
-### Clickable
+### prevent Default event
 
-Trigger the function by clicking component
+Control the default event by setting `preventDefault`.
 ```jsx live=true
 import React, { useState } from 'react';
-import { HotKeys } from '@douyinfe/semi-ui';
+import { HotKeys, Modal } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const hotKeys = ["Control", "a"]
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
+  const hotKeys = [HotKeys.Keys.Meta, HotKeys.Keys.S]
+
   return (
     <div>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} clickable></HotKeys>
-      <div>{cnt}</div>
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} preventDefault></HotKeys>
+      <br />
+      <HotKeys hotKeys={[HotKeys.Keys.Control, HotKeys.Keys.S]} onHotKey={showDialog} preventDefault></HotKeys>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {'Meta/Control + S'}.
+      </Modal>
     </div>
   );
 }
@@ -128,46 +185,36 @@ function Demo() {
 The hotkey is listened to on the body by default, through `getListenerTarget`
 ```jsx live=true
 import React, { useState, useRef } from 'react';
-import { HotKeys, Input } from '@douyinfe/semi-ui';
+import { HotKeys, Input, Modal } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const hotKeys = ["Meta", "s"]
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
+  const hotKeys = ["Control", "q"]
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
 
   const inputRef = useRef(null);
   return (
     <div>
       <Input ref={inputRef} placeholder='test for target'></Input>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} 
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} 
         getListenerTarget={() => inputRef.current}>
       </HotKeys>
-      <div>{cnt}</div>
-    </div>
-  );
-}
-```
-
-### Disabled
-By setting `disabled` as `true`, the component will not listen hotkeys.
-You can use it when only styling is needed.
-
-```jsx live=true
-import React, { useState } from 'react';
-import { HotKeys } from '@douyinfe/semi-ui';
-
-function Demo() {
-  const hotKeys = ["Control", "a"]
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
-  return (
-    <div>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} disabled></HotKeys>
-      <div>{cnt}</div>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {hotKeys.join('+')}.
+      </Modal>
     </div>
   );
 }
@@ -180,14 +227,14 @@ function Demo() {
 
 | Property          | Instructions                                                                                                                                                                                  | type                            | Default   |
 |-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------|-----------|
-| hotKeys  | Define keyboard shortcut,[values](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)                                          | KeyboardEvent.key[]                          | -         |
+| className         | class name               | string                          |           |                                     
 | content | Set the characters                                         | string[]                          | -         |
-| onClick        | function that keyboard shortcut triggers                                                             |   () => void                      |    -       |
-| clickable       | whether the function can be triggered by click                                                              | boolean                       |   false       |
-| render        |    Replace the element                                               | () => ReactNode \| ReactNode                       |           |
-| className         | class name                                                                  | string                          |           |
 | getListenerTarget         | change the DOM element the listener is mounted on            | () => HTMLElement                       |  document.body         |
-| disabled          |                                                   | boolean                         | false     |
+| hotKeys  | Define keyboard shortcut,[values](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)                                          | KeyboardEvent.key[]                          | -         |
+| onClick        | callback that clicking triggers                                                             |   () => void                      |    -       |
+| onHotKey      | callback that hotKeys triggers                                                        | (e: KeyboardEvent) => void                       |   -      |
+| preventDefault  | Whether to prevent the default behavior of the shortcut                                         | boolean                          | false        |
+| render        |    Replace the element                                               | () => ReactNode \| ReactNode                       |           |
 | style             | style                                                                  | CSSProperties                   |           |
 
 ## Design Tokens

+ 128 - 87
content/input/hotkeys/index.md

@@ -3,7 +3,7 @@ localeCode: zh-CN
 order: 30
 category: 输入类
 title: HotKeys 快捷键
-icon: doc-input
+icon: doc-configprovider
 width: 60%
 brief: 用于方便用户自定义快捷键及相关操作
 ---
@@ -12,37 +12,55 @@ brief: 用于方便用户自定义快捷键及相关操作
 ## 代码演示
 
 ### 如何引入
+HotKeys 从 2.66.0 开始支持
 
 ```jsx import
 import { HotKeys } from '@douyinfe/semi-ui';
 ```
 
+
 ### 说明
 快捷键仅支持修饰键组合`Shift`,`Control`,`Meta`,`Alt`与其他键的组合。
+> [Meta](https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/metaKey) 在MacOS中为`Command`,在Windows中为`Win`
 
-当设定快捷键与常用快捷键如`Ctrl/Meta + C`相同时,会导致默认行为(复制)不会正常触发。
+当设定快捷键与常用快捷键如`Ctrl/Meta + C`相同时,可以通过设置`preventDefault`控制默认事件是否触发。
 
 ### 基本
 
-基本使用,通过`hotKeys`传入快捷键组合,通过 onClick 绑定快捷键处理函数,作出响应动作。
+基本使用,通过`hotKeys`传入快捷键组合,通过 `onHotKey` 绑定快捷键处理函数,作出响应动作。
 
-按下 Ctrl + Shift + A,使得 count + 1。默认在 body 监听,全局生效。
+按下 Ctrl + Shift + A, 唤起modal。默认在 body.document 监听,全局生效。
 
-[hotKeys取值参考](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)
+[hotKeys取值参考](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values),也可以使用`HotKeys.Keys`进行设置
 
 ```jsx live=true
 import React, { useState } from 'react';
-import { HotKeys } from '@douyinfe/semi-ui';
+import { HotKeys, Modal } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
+  const hotKeys = [HotKeys.Keys.Control, 'Shift', HotKeys.Keys.A]
+  
   return (
     <div>
-      <HotKeys hotKeys={['Control', 'Shift', 'a']} onClick={onClick} ></HotKeys>
-      <div>{cnt}</div>
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} ></HotKeys>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {hotKeys.join('+')}.
+      </Modal>
     </div>
   );
 }
@@ -54,19 +72,32 @@ function Demo() {
 
 ```jsx live=true
 import React, { useState } from 'react';
-import { HotKeys } from '@douyinfe/semi-ui';
+import { HotKeys, Modal } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
+  const hotKeys = [HotKeys.Keys.Control, 'Shift', HotKeys.Keys.B]
+  
   return (
     <div>
-      <HotKeys hotKeys={["Control", "b"]} onClick={onClick} content={["Ctrl", "B"]}></HotKeys>
-        <br></br>
-      <HotKeys hotKeys={["Meta","b"]} onClick={onClick} content={["⌘", "B"]}></HotKeys>
-      <div>{cnt}</div>
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} content={['Ctrl', 'Shift', 'B']}></HotKeys>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {hotKeys.join('+')}.
+      </Modal>
     </div>
   );
 }
@@ -74,51 +105,73 @@ function Demo() {
 
 通过`render`传入代替渲染的元素
 
-当遇到操作系统导致的快捷键不同的问题时,可以类似地使用两个组件且自定义渲染
 ```jsx live=true
 import React, { useState } from 'react';
-import { HotKeys, Tag } from '@douyinfe/semi-ui';
+import { HotKeys, Modal, Tag } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const hotKeys = ["r"]
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
-  const newShortCut = () => {
-    return (
-      <div>
-        <Tag>{"按下R / K即可加一"}</Tag>
-      </div>
-    )
-  }
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
+  const hotKeys = [HotKeys.Keys.Control, HotKeys.Keys.R]
+  
+  const newHotKeys = <Tag>Press Ctrl+R to Open Modal</Tag>
   return (
     <div>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} render={newShortCut}></HotKeys>
-      <HotKeys hotKeys={["k"]} onClick={onClick} render={() => null}></HotKeys>
-      <div>{cnt}</div>
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} render={newHotKeys}></HotKeys>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {hotKeys.join('+')}.
+      </Modal>
     </div>
   );
 }
 ```
 
-### 点击触发
+### 阻止默认事件
 
-设置`clickable`为`true`可以通过点击触发
+通过设置`preventDefault`控制默认事件是否触发。
 ```jsx live=true
 import React, { useState } from 'react';
-import { HotKeys } from '@douyinfe/semi-ui';
+import { HotKeys, Modal } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const hotKeys = ["Control", "a"]
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
+  const hotKeys = [HotKeys.Keys.Meta, HotKeys.Keys.S]
+
   return (
     <div>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} clickable></HotKeys>
-      <div>{cnt}</div>
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} preventDefault></HotKeys>
+      <br />
+      <HotKeys hotKeys={[HotKeys.Keys.Control, HotKeys.Keys.S]} onHotKey={showDialog} preventDefault></HotKeys>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {'Meta/Control + S'}.
+      </Modal>
     </div>
   );
 }
@@ -128,48 +181,36 @@ function Demo() {
 快捷键默认在 body 监听,通过`getListenerTarget`修改快捷键监听挂载的DOM
 ```jsx live=true
 import React, { useState, useRef } from 'react';
-import { HotKeys, Input } from '@douyinfe/semi-ui';
+import { HotKeys, Input, Modal } from '@douyinfe/semi-ui';
 
 function Demo() {
-  const hotKeys = ["Meta", "s"]
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
+  const hotKeys = ["Control", "q"]
+  const [visible, setVisible] = useState(false);
+  const showDialog = () => {
+      setVisible(true);
+  };
+  const handleOk = () => {
+      setVisible(false);
+  };
+  const handleCancel = () => {
+      setVisible(false);
+  };
 
   const inputRef = useRef(null);
   return (
     <div>
       <Input ref={inputRef} placeholder='test for target'></Input>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} 
+      <HotKeys hotKeys={hotKeys} onHotKey={showDialog} 
         getListenerTarget={() => inputRef.current}>
       </HotKeys>
-      <div>{cnt}</div>
-    </div>
-  );
-}
-```
-
-### 不可用
-
-设定 `disabled` 属性为 `true`, 不监听 hotKeys。
-
-当仅需要样式时可以使用
-
-```jsx live=true
-import React, { useState } from 'react';
-import { HotKeys } from '@douyinfe/semi-ui';
-
-function Demo() {
-  const hotKeys = ["Control", "a"]
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
-  return (
-    <div>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} disabled></HotKeys>
-      <div>{cnt}</div>
+      <Modal
+          title="Dialog"
+          visible={visible}
+          onOk={handleOk}
+          onCancel={handleCancel}
+      >
+          This is the Modal opened by hotkey: {hotKeys.join('+')}.
+      </Modal>
     </div>
   );
 }
@@ -181,14 +222,14 @@ function Demo() {
 
 | 属性              | 说明                                                                  | 类型                            | 默认值    |
 | ----------------- | --------------------------------------------------------------------- | ------------------------------- | --------- |
-| hotKeys  | 设置快捷键组合,[取值参考](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)                                          | KeyboardEvent.key[]                          | -         |
-| content | 设置显示内容                                          | string[]                          | -         |
-| onClick        | 快捷键触发函数                                                              |   () => void                      |    -       |
-| clickable       | 设置函数是否可以点击触发                                                              | boolean                       |   false       |
-| render        |    覆盖组件渲染                                               | () => ReactNode \| ReactNode                       |           |
 | className         | 类名                                                                  | string                          |           |
+| content | 设置显示内容                                          | string[]                          | -         |
 | getListenerTarget         | 用于设置监听器挂载的DOM            | () => HTMLElement                       |  document.body         |
-| disabled          | 是否禁用,默认为false                                                 | boolean                         | false     |
+| hotKeys  | 设置快捷键组合,[取值参考](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values)                                          | KeyboardEvent.key[]                          | -         |
+| onClick        | 点击回调函数                                                              |   () => void                      |    -       |
+| onHotKey        | 快捷键回调函数                                                              |   (e: KeyboardEvent) => void                      |    -       |
+| preventDefault        | 是否阻止快捷键默认行为                                                                  | boolean                          | false          |
+| render        |    覆盖组件渲染                                               | () => ReactNode \| ReactNode                       |           |
 | style             | 样式                                                                  | CSSProperties                   |           |
 
 

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

@@ -74,6 +74,45 @@ function Demo(props = {}) {
 }
 ```
 
+### Manually obtain values
+Usually, the value of ConfigProvider is automatically obtained and consumed within the component, so you don't need to worry about it. However, in some special scenarios, you may need to manually obtain the value to perform other operations.
+
+Use ConfigConsumer to obtain the value of ConfigProvider
+
+```jsx live=true dir="column" hideInDSM
+import React, { useMemo, useState } from 'react';
+import { ConfigProvider, ConfigConsumer, Select, DatePicker, TimePicker, Typography } from '@douyinfe/semi-ui';
+
+function Demo(props = {}) {
+  const [timeZone, setTimeZone] = useState('GMT+08:00');
+  const defaultTimestamp = 1581599305265;
+  const gmtList = useMemo(() => {
+    const list = [];
+    for (let hourOffset = -11; hourOffset <= 14; hourOffset++) {
+      const prefix = hourOffset >= 0 ? '+' : '-';
+      const hOffset = Math.abs(parseInt(hourOffset, 10));
+      list.push(`GMT${prefix}${String(hOffset).padStart(2, '0')}:00`);
+    }
+    return list;
+  }, []);
+
+  return (
+          <ConfigProvider timeZone={timeZone}>
+            {/*...*/}
+            <ConfigConsumer>
+              {(value) => {
+                return <Typography.Text ellipsis={{ showTooltip: {opts:{style:{minWidth:"1200px"}} }}}  style={{ width: 600 }}>
+                  {JSON.stringify(value)}
+                </Typography.Text>
+              }}
+            </ConfigConsumer>
+            {/*...*/}
+          </ConfigProvider>
+  );
+}
+
+```
+
 ### RTL/LTR
 Global configuration `direction` can change the text direction of components。`rtl` means right to left (similar to Hebrew or Arabic), `ltr` means left to right (similar to most languages such as English)
 
@@ -454,4 +493,4 @@ semiGlobal.config.overrideDefaultProps = {
 };
 
 
-```
+```

+ 42 - 0
content/other/configprovider/index.md

@@ -77,6 +77,48 @@ function Demo(props = {}) {
 }
 ```
 
+### 手动获取值
+通常情况下,组件内部会自动获取 ConfigProvider 的值自动消费,无需关心。但是一些特殊场景,你可能需要手动获取值来进行其他操作。
+
+使用 ConfigConsumer 获取 ConfigProvider 的值
+
+```jsx live=true dir="column" hideInDSM
+import React, { useMemo, useState } from 'react';
+import { ConfigProvider, ConfigConsumer, Select, DatePicker, TimePicker, Typography } from '@douyinfe/semi-ui';
+
+function Demo(props = {}) {
+  const [timeZone, setTimeZone] = useState('GMT+08:00');
+  const defaultTimestamp = 1581599305265;
+  const gmtList = useMemo(() => {
+    const list = [];
+    for (let hourOffset = -11; hourOffset <= 14; hourOffset++) {
+      const prefix = hourOffset >= 0 ? '+' : '-';
+      const hOffset = Math.abs(parseInt(hourOffset, 10));
+      list.push(`GMT${prefix}${String(hOffset).padStart(2, '0')}:00`);
+    }
+    return list;
+  }, []);
+
+  return (
+          <ConfigProvider timeZone={timeZone}>
+            {/*...*/}
+            <ConfigConsumer>
+              {(value) => {
+                return <Typography.Text ellipsis={{ showTooltip: {opts:{style:{minWidth:"1200px"}} }}}  style={{ width: 600 }}>
+                  {JSON.stringify(value)}
+                </Typography.Text>
+             }}
+            </ConfigConsumer>
+            {/*...*/}
+          </ConfigProvider>
+          );
+}
+
+```
+
+
+
+
 ### RTL/LTR
 全局配置 `direction` 可以改变组件的文本方向(1.8.0)。
 

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

@@ -37,6 +37,7 @@ Button,
 Checkbox,
 DatePicker,
 Form,
+HotKeys,
 Input,
 InputNumber,
 PinCode,

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

@@ -36,6 +36,7 @@ Button 按钮,
 Checkbox 复选框,
 DatePicker 日期选择器,
 Form 表单,
+HotKeys 快捷键,
 Input 输入框,
 InputNumber 数字输入框,
 PinCode 验证码输入,

+ 1 - 1
gatsby-node.js

@@ -106,7 +106,7 @@ exports.onCreateWebpackConfig = ({ stage, rules, loaders, plugins, actions }) =>
                 "#minpath": isSSR ? "vfile/lib/minpath.js" : "vfile/lib/minpath.browser.js",
                 "#minproc": isSSR ? "vfile/lib/minproc.js" : "vfile/lib/minproc.browser.js",
                 "#minurl": isSSR ? "vfile/lib/minurl.js" : "vfile/lib/minurl.browser.js",
-                "estree-util-visit/do-not-use-color": isSSR ? "estree-util-visit/lib/color.node.js":"estree-util-visit/lib/color.js",
+                "estree-util-visit/do-not-use-color": isSSR ? "estree-util-visit/lib/color.node.js":"estree-util-visit/lib/color.default.js",
                 "devlop":"devlop/lib/default.js",
                 "unist-util-visit-parents/do-not-use-color": isSSR?"unist-util-visit-parents/lib/color.node.js":"unist-util-visit-parents/lib/color.js",
                 'semi-site-header': process.env.SEMI_SITE_HEADER || '@douyinfe/semi-site-header',

+ 2 - 1
jest.config.js

@@ -58,7 +58,8 @@ let config = {
         
         '@mdx-js/mdx': '<rootDir>/test/__mocks__/mdx-3.0.1-cjs.js',
         'remark-gfm': '<rootDir>/test/__mocks__/remark-gfm-4.0.0-cjs.js',
-
+        "@testing-library/react": "<rootDir>/node_modules/@testing-library/react",
+        "@testing-library/dom": "<rootDir>/node_modules/@testing-library/dom",
         '@douyinfe/semi-ui(.*)$': '<rootDir>/packages/semi-ui/$1',
         '@douyinfe/semi-foundation(.*)$': '<rootDir>/packages/semi-foundation/$1',
         '@douyinfe/semi-illustrations(.*)$': '<rootDir>/packages/semi-illustrations/src/$1',

+ 1 - 0
package.json

@@ -170,6 +170,7 @@
         "enzyme": "^3.11.0",
         "enzyme-adapter-react-16": "^1.15.6",
         "enzyme-to-json": "^3.6.2",
+        "@testing-library/react": "^12",
         "esbuild": "^0.14.47",
         "esbuild-loader": "^2.14.0",
         "eslint": "^7.32.0",

+ 87 - 55
packages/semi-foundation/hotKeys/constants.ts

@@ -8,62 +8,94 @@ const strings = {
 };
 
 export { cssClasses, strings };
+const keyCodeMap = {
+    // alpha
+    'a': 'KeyA', 'b': 'KeyB', 'c': 'KeyC', 'd': 'KeyD', 'e': 'KeyE',
+    'f': 'KeyF', 'g': 'KeyG', 'h': 'KeyH', 'i': 'KeyI', 'j': 'KeyJ',
+    'k': 'KeyK', 'l': 'KeyL', 'm': 'KeyM', 'n': 'KeyN', 'o': 'KeyO',
+    'p': 'KeyP', 'q': 'KeyQ', 'r': 'KeyR', 's': 'KeyS', 't': 'KeyT',
+    'u': 'KeyU', 'v': 'KeyV', 'w': 'KeyW', 'x': 'KeyX', 'y': 'KeyY',
+    'z': 'KeyZ',
 
-export function keyToCode(key: KeyboardEvent["key"]) {
-    const keyCodeMap = {
-        // alpha
-        'a': 'KeyA', 'b': 'KeyB', 'c': 'KeyC', 'd': 'KeyD', 'e': 'KeyE',
-        'f': 'KeyF', 'g': 'KeyG', 'h': 'KeyH', 'i': 'KeyI', 'j': 'KeyJ',
-        'k': 'KeyK', 'l': 'KeyL', 'm': 'KeyM', 'n': 'KeyN', 'o': 'KeyO',
-        'p': 'KeyP', 'q': 'KeyQ', 'r': 'KeyR', 's': 'KeyS', 't': 'KeyT',
-        'u': 'KeyU', 'v': 'KeyV', 'w': 'KeyW', 'x': 'KeyX', 'y': 'KeyY',
-        'z': 'KeyZ',
-
-        // digit
-        '0': 'Digit0', '1': 'Digit1', '2': 'Digit2', '3': 'Digit3',
-        '4': 'Digit4', '5': 'Digit5', '6': 'Digit6', '7': 'Digit7', 
-        '8': 'Digit8', '9': 'Digit9',
-
-        // punctuation
-        ' ': 'Space', 'enter': 'Enter', 'escape': 'Escape', 'backspace': 'Backspace',
-        'tab': 'Tab', '-': 'Minus', '=': 'Equal', '[': 'BracketLeft',
-        ']': 'BracketRight', '\\': 'Backslash', ';': 'Semicolon', 
-        "'": 'Quote', '`': 'Backquote', ',': 'Comma', '.': 'Period',
-        '/': 'Slash', '?': 'Slash', '!': 'Digit1', '@': 'Digit2',
-        '#': 'Digit3', '$': 'Digit4', '%': 'Digit5', '^': 'Digit6',
-        '&': 'Digit7', '*': 'Digit8', '(': 'Digit9', ')': 'Digit0',
-
-        // arrow
-        'arrowup': 'ArrowUp', 'arrowdown': 'ArrowDown',
-        'arrowleft': 'ArrowLeft', 'arrowright': 'ArrowRight',
-
-        // function
-        'shift': 'ShiftLeft', 'control': 'ControlLeft', 'alt': 'AltLeft',
-        'meta': 'MetaLeft', 'capslock': 'CapsLock', 'f1': 'F1', 
-        'f2': 'F2', 'f3': 'F3', 'f4': 'F4', 'f5': 'F5', 'f6': 'F6', 
-        'f7': 'F7', 'f8': 'F8', 'f9': 'F9', 'f10': 'F10', 'f11': 'F11', 
-        'f12': 'F12', 'insert': 'Insert', 'delete': 'Delete', 'home': 'Home', 
-        'end': 'End', 'pageup': 'PageUp', 'pagedown': 'PageDown',
-        'numlock': 'NumLock', 'scrolllock': 'ScrollLock', 'pause': 'Pause',
-
-        // numpad
-        'numpad0': 'Numpad0', 'numpad1': 'Numpad1', 'numpad2': 'Numpad2',
-        'numpad3': 'Numpad3', 'numpad4': 'Numpad4', 'numpad5': 'Numpad5',
-        'numpad6': 'Numpad6', 'numpad7': 'Numpad7', 'numpad8': 'Numpad8',
-        'numpad9': 'Numpad9', 'numpaddecimal': 'NumpadDecimal', 
-        'numpaddivide': 'NumpadDivide', 'numpadmultiply': 'NumpadMultiply', 
-        'numpadsubtract': 'NumpadSubtract', 'numpadadd': 'NumpadAdd', 
-        'numpadenter': 'NumpadEnter',
-
-        // Others
-        'printscreen': 'PrintScreen', 'contextmenu': 'ContextMenu',
-        'help': 'Help', 'select': 'Select', 'execute': 'Execute', 
-        'clear': 'Clear', 'kana': 'KanaMode', 'kanji': 'KanjiMode',
-        'nonconvert': 'NonConvert', 'convert': 'Convert', 
-        'process': 'Process', 'accept': 'Accept', 'modechange': 'ModeChange',
-        'launchapp1': 'LaunchApp1', 'launchapp2': 'LaunchApp2',
-        'launchmail': 'LaunchMail', 'mediaselect': 'MediaSelect'
-    };
+    // digit
+    '0': 'Digit0', '1': 'Digit1', '2': 'Digit2', '3': 'Digit3',
+    '4': 'Digit4', '5': 'Digit5', '6': 'Digit6', '7': 'Digit7', 
+    '8': 'Digit8', '9': 'Digit9',
+
+    // punctuation
+    ' ': 'Space', 'enter': 'Enter', 'escape': 'Escape', 'backspace': 'Backspace',
+    'tab': 'Tab', '-': 'Minus', '=': 'Equal', '[': 'BracketLeft',
+    ']': 'BracketRight', '\\': 'Backslash', ';': 'Semicolon', 
+    "'": 'Quote', '`': 'Backquote', ',': 'Comma', '.': 'Period',
+    '/': 'Slash', '?': 'Slash', '!': 'Digit1', '@': 'Digit2',
+    '#': 'Digit3', '$': 'Digit4', '%': 'Digit5', '^': 'Digit6',
+    '&': 'Digit7', '*': 'Digit8', '(': 'Digit9', ')': 'Digit0',
+
+    // arrow
+    'arrowup': 'ArrowUp', 'arrowdown': 'ArrowDown',
+    'arrowleft': 'ArrowLeft', 'arrowright': 'ArrowRight',
+
+    // function
+    'shift': 'ShiftLeft', 'control': 'ControlLeft', 'alt': 'AltLeft',
+    'meta': 'MetaLeft', 'capslock': 'CapsLock', 'f1': 'F1', 
+    'f2': 'F2', 'f3': 'F3', 'f4': 'F4', 'f5': 'F5', 'f6': 'F6', 
+    'f7': 'F7', 'f8': 'F8', 'f9': 'F9', 'f10': 'F10', 'f11': 'F11', 
+    'f12': 'F12', 'insert': 'Insert', 'delete': 'Delete', 'home': 'Home', 
+    'end': 'End', 'pageup': 'PageUp', 'pagedown': 'PageDown',
+    'numlock': 'NumLock', 'scrolllock': 'ScrollLock', 'pause': 'Pause',
 
+    // numpad
+    'numpad0': 'Numpad0', 'numpad1': 'Numpad1', 'numpad2': 'Numpad2',
+    'numpad3': 'Numpad3', 'numpad4': 'Numpad4', 'numpad5': 'Numpad5',
+    'numpad6': 'Numpad6', 'numpad7': 'Numpad7', 'numpad8': 'Numpad8',
+    'numpad9': 'Numpad9', 'numpaddecimal': 'NumpadDecimal', 
+    'numpaddivide': 'NumpadDivide', 'numpadmultiply': 'NumpadMultiply', 
+    'numpadsubtract': 'NumpadSubtract', 'numpadadd': 'NumpadAdd', 
+    'numpadenter': 'NumpadEnter',
+};
+export function keyToCode(key: KeyboardEvent["key"]) {
     return keyCodeMap[key.toLowerCase()] || undefined;
 }
+
+enum Keys {
+    A = 'a', B = 'b', C = 'c', D = 'd', E = 'e',
+    F = 'f', G = 'g', H = 'h', I = 'i', J = 'j',
+    K = 'k', L = 'l', M = 'm', N = 'n', O = 'o',
+    P = 'p', Q = 'q', R = 'r', S = 's', T = 't',
+    U = 'u', V = 'v', W = 'w', X = 'x', Y = 'y',
+    Z = 'z',
+
+    Digit0 = '0', Digit1 = '1', Digit2 = '2', Digit3 = '3',
+    Digit4 = '4', Digit5 = '5', Digit6 = '6', Digit7 = '7', 
+    Digit8 = '8', Digit9 = '9',
+
+    Space = ' ', Enter = 'enter', Escape = 'escape', Backspace = 'backspace',
+    Tab = 'tab', Minus = '-', Equal = '=', BracketLeft = '[',
+    BracketRight = ']', Backslash = '\\', Semicolon = ';', 
+    Quote = "'", Backquote = '`', Comma = ',', Period = '.',
+    Slash = '/', Exclamation = '!', At = '@', Hash = '#', 
+    Dollar = '$', Percent = '%', Caret = '^', Ampersand = '&', 
+    Asterisk = '*', LeftParenthesis = '(', RightParenthesis = ')',
+
+    ArrowUp = 'arrowup', ArrowDown = 'arrowdown',
+    ArrowLeft = 'arrowleft', ArrowRight = 'arrowright',
+
+    Shift = 'shift', Control = 'control', Alt = 'alt',
+    Meta = 'meta', CapsLock = 'capslock', F1 = 'f1', 
+    F2 = 'f2', F3 = 'f3', F4 = 'f4', F5 = 'f5', F6 = 'f6', 
+    F7 = 'f7', F8 = 'f8', F9 = 'f9', F10 = 'f10', F11 = 'f11', 
+    F12 = 'f12', Insert = 'insert', Delete = 'delete', Home = 'home', 
+    End = 'end', PageUp = 'pageup', PageDown = 'pagedown',
+    NumLock = 'numlock', ScrollLock = 'scrolllock', Pause = 'pause',
+
+    Numpad0 = 'numpad0', Numpad1 = 'numpad1', Numpad2 = 'numpad2',
+    Numpad3 = 'numpad3', Numpad4 = 'numpad4', Numpad5 = 'numpad5',
+    Numpad6 = 'numpad6', Numpad7 = 'numpad7', Numpad8 = 'numpad8',
+    Numpad9 = 'numpad9', NumpadDecimal = 'numpaddecimal', 
+    NumpadDivide = 'numpaddivide', NumpadMultiply = 'numpadmultiply', 
+    NumpadSubtract = 'numpadsubtract', NumpadAdd = 'numpadadd', 
+    NumpadEnter = 'numpadenter',
+}
+
+
+export { Keys };

+ 34 - 18
packages/semi-foundation/hotKeys/foundation.ts

@@ -1,8 +1,8 @@
 import BaseFoundation, { DefaultAdapter } from '../base/foundation';
-import { keyToCode } from './constants';
+import { keyToCode, Keys } from './constants';
 
 export interface HotKeysAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
-    notifyClick: () => void;
+    notifyHotKey: (e: KeyboardEvent) => void;
     getListenerTarget: () => HTMLElement
 }
 
@@ -15,27 +15,45 @@ export default class HotKeysFoundation<P = Record<string, any>, S = Record<strin
         // init Listener
         const target = this._adapter.getListenerTarget();
         target?.addEventListener('keydown', this.handleKeyDown);
+        const hotKeys = this.getProps().hotKeys;
+        if (!this.isValidHotKeys(hotKeys)) {
+            throw new Error('HotKeys must have one common key and 0/some modifier key');
+        }   
+    }
+
+    isValidHotKeys = (hotKeys: string[]): boolean => {
+        let commonKeyCnt = 0;
+        const modifierKeys: string[] = [Keys.Meta, Keys.Alt, Keys.Shift, Keys.Control];
+
+        hotKeys.forEach(key => {
+            key = key.toLowerCase();
+            if (!Object.values(Keys).some((value) => value === key)) {
+                throw new Error(`${key} is not a valid key`);
+            }
+            if (!modifierKeys.includes(key)) {
+                commonKeyCnt += 1;
+            }
+        });
+
+        return commonKeyCnt === 1;
     }
 
     handleKeyDown = (event: KeyboardEvent): void => {
-        const disabled = this.getProps().disabled;
-        if (disabled) {
-            return;
-        }
-        const hotKeys = this.getProps().hotKeys;
+        const { mergeMetaCtrl: merged, hotKeys, preventDefault } = this.getProps();
         let allModifier = new Array(4).fill(false); // Meta Shift Alt Ctrl
         let clickedModifier = [event.metaKey, event.shiftKey, event.altKey, event.ctrlKey];
-        const keysPressed = hotKeys?.map((key: KeyboardEvent["key"])=> {
-            if (key === "Meta") {
+        const keysPressed = hotKeys?.map((key: KeyboardEvent["key"]) => {
+            key = key.toLowerCase();
+            if (key === Keys.Meta) {
                 allModifier[0] = true;
                 return event.metaKey; 
-            } else if (key === "Shift") {
+            } else if (key === Keys.Shift) {
                 allModifier[1] = true;
                 return event.shiftKey;
-            } else if (key === "Alt") {
+            } else if (key === Keys.Alt) {
                 allModifier[2] = true;
                 return event.altKey;
-            } else if (key === "Control") {
+            } else if (key === Keys.Control) {
                 allModifier[3] = true;
                 return event.ctrlKey;
             }
@@ -46,17 +64,15 @@ export default class HotKeysFoundation<P = Record<string, any>, S = Record<strin
             return;
         }
         if (keysPressed.every(Boolean)) {
-            event.preventDefault();
-            this.handleClick();
+            if (preventDefault) {
+                event.preventDefault();
+            }
+            this._adapter.notifyHotKey(event);
             return;
         }
         
     }
 
-    handleClick(): void {
-        this._adapter.notifyClick();
-    }
-
     destroy(): void {
         // remove Listener
         const target = this._adapter.getListenerTarget();

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

@@ -24,6 +24,7 @@ $module: #{$prefix}-hotKeys;
     &-split {
         @include font-size-small;
         margin: 0 3px;
+        color: $color-hotKeys-split;
     }
 
 }

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

@@ -1,5 +1,6 @@
 $color-hotKeys-bg: var(--semi-color-fill-0); // 快捷键背景颜色
 $color-hotKeys-text: var(--semi-color-text-2); // 快捷键文字颜色
+$color-hotKeys-split: var(--semi-color-text-0); // 快捷键文字颜色
 
 $width-hotKeys-border: 1px; // 快捷键描边宽度
 $radius-hotKeys: 2px; // 边框半径

+ 1 - 1
packages/semi-theme-default/scss/global.scss

@@ -79,7 +79,7 @@ body, body[theme-mode="dark"] .semi-always-light, :host, :host .semi-always-ligh
 
     --semi-color-border: rgba(var(--semi-grey-9), .08); // 默认描边颜色
     --semi-color-nav-bg: rgba(var(--semi-white), 1); // 导航背景色
-    --semi-color-overlay-bg: rgba(172, 172, 181, 0.6); // 蒙层背景色
+    --semi-color-overlay-bg: rgba(22, 22, 26, .6); // 蒙层背景色
 
     --semi-color-fill-0: rgba(var(--semi-grey-8), .05); // 填充色 - 默认态
     --semi-color-fill-1: rgba(var(--semi-grey-8), .09); // 填充色 - 悬浮态

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

@@ -6,6 +6,8 @@ import Context, { ContextValue } from './context';
 
 export interface ConfigProviderProps extends ContextValue {}
 
+export const ConfigConsumer = Context.Consumer;
+
 export default class ConfigProvider extends React.Component<ConfigProviderProps> {
 
     constructor(props: ConfigProviderProps) {

+ 7 - 7
packages/semi-ui/hotKeys/__test__/hotkeys.test.js

@@ -4,10 +4,9 @@ import { noop, drop } from 'lodash';
 import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants';
 import {sleep} from "../../_test_/utils";
 
-function getHK(props) {
-    return mount(<HotKeys {...props}></HotKeys>, {
-        attachTo: document.getElementById('container'),
-    });
+function mount(props) {
+    const container = document.getElementById('container');
+    return testRender(<HotKeys {...props} id={"test"}></HotKeys>, container);
 }
 
 describe('HotKeys', () => {
@@ -34,8 +33,9 @@ describe('HotKeys', () => {
             },
             hotKeys: ['r']
         };
-        const hotkeys = getHK(props);
-        expect(hotkeys.exists(`.${BASE_CLASS_PREFIX}-hotKeys.test`)).toEqual(true);
-        expect(hotkeys.find(`.${BASE_CLASS_PREFIX}-hotKeys`)).toHaveStyle('color', 'red');
+        mount(props);
+
+        expect(Boolean(document.querySelector(`.${BASE_CLASS_PREFIX}-hotKeys.test`))).toEqual(true);
+        expect(document.querySelector(`.${BASE_CLASS_PREFIX}-hotKeys`).style['color']==="red").toEqual(true);
     });
 });

+ 53 - 33
packages/semi-ui/hotKeys/_story/hotKeys.stories.jsx

@@ -1,5 +1,5 @@
 import React, { useState } from 'react';
-import { Button } from '@douyinfe/semi-ui'
+import { Button, Tag } from '@douyinfe/semi-ui'
 import { HotKeys } from '../../index';
 
 export default {
@@ -7,38 +7,24 @@ export default {
 }
 
 export const Demo = () => {
-  const hotKeys = ["Alt","k"]
+  const hotKeys = [HotKeys.Keys.Control, "k"]
   const [cnt, setCnt] = useState(0)
-  const onClick = () => {
+  const onHotKey = (e) => {
+    console.log(e)
     setCnt(cnt+1)
   }
   return (
     <div>
-      <HotKeys hotKeys={hotKeys} onClick={onClick}></HotKeys>
-      <pre id='pre'>{cnt}</pre>
-    </div>
-  );
-}
-
-export const Clickable = () => {
-  const hotKeys = ["Alt","k"]
-  const [cnt, setCnt] = useState(0)
-  const onClick = () => {
-    setCnt(cnt+1)
-  }
-  return (
-    <div>
-      <div>clickable</div>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} clickable={true}></HotKeys>
+      <HotKeys hotKeys={hotKeys} content={["Control", "K"]} onHotKey={onHotKey} onClick={() => setCnt(cnt + 1)} mergeMetaCtrl></HotKeys>
       <pre id='pre'>{cnt}</pre>
     </div>
   );
 }
 
 export const renderButton = () => {
-  const hotKeys = ["r"]
+  const hotKeys = [HotKeys.Keys.R]
   const [cnt, setCnt] = useState(0)
-  const onClick = () => {
+  const onHotKey = () => {
     setCnt(cnt+1)
   }
   const button = () => {
@@ -51,7 +37,7 @@ export const renderButton = () => {
   return (
     <div>
       <pre id='pre'>{" cnt:" + cnt}</pre>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} render={button} clickable></HotKeys>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} render={button}></HotKeys>
     </div>
 
   );
@@ -60,38 +46,38 @@ export const renderButton = () => {
 export const renderNull = () => {
   const hotKeys = ["r"]
   const [cnt, setCnt] = useState(0)
-  const onClick = () => {
+  const onHotKey = () => {
     setCnt(cnt+1)
   }
   return (
     <div>
       <span>{" cnt:" + cnt}</span>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} render={null} clickable></HotKeys>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} render={null}></HotKeys>
     </div>
 
   );
 }
 
 export const combine = () => {
-  const hotKeys = ["Meta", "Alt", "k"]
+  const hotKeys = [HotKeys.Keys.Meta, HotKeys.Keys.Alt, "k"]
   const [cnt, setCnt] = useState(0)
-  const onClick = () => {
+  const onHotKey = () => {
     setCnt(cnt+1)
   }
   return (
     <div>
       <pre id='pre'>{cnt}</pre>
-      <HotKeys hotKeys={hotKeys} onClick={onClick}></HotKeys>
-      <HotKeys hotKeys={["Meta", "Shift", "k"]} onClick={onClick}></HotKeys>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey}></HotKeys>
+      <HotKeys hotKeys={["Meta", "Shift", "k"]} onHotKey={onHotKey}></HotKeys>
     </div>
     
   );
 }
 
 export const target = () => {
-  const hotKeys = ["Meta", "s"]
+  const hotKeys = ["Meta", HotKeys.Keys.S]
   const [cnt, setCnt] = useState(0)
-  const onClick = () => {
+  const onHotKey = () => {
     setCnt(cnt+1)
   }
   
@@ -100,7 +86,7 @@ export const target = () => {
     <div>
       {target}
       <pre id='pre'>{cnt}</pre>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} getListenerTarget={() => document.getElementById("test")}></HotKeys>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} getListenerTarget={() => document.getElementById("test")}></HotKeys>
     </div>
     
   );
@@ -109,13 +95,47 @@ export const target = () => {
 export const disabled = () => {
   const hotKeys = ["Meta", "k"]
   const [cnt, setCnt] = useState(0)
-  const onClick = () => {
+  const onHotKey = () => {
     setCnt(cnt+1)
   }
   return (
     <div>
-      <HotKeys hotKeys={hotKeys} onClick={onClick} disabled></HotKeys>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey}></HotKeys>
       <pre id='pre'>{cnt}</pre>
     </div>
   );
+}
+
+export const hotKeys = () => {
+  const hotKeys = ["Meta", "k"]
+  const [cnt, setCnt] = useState(0)
+  const onHotKey = () => {
+    setCnt(cnt+1)
+  }
+  const button = () => {
+    return (
+      <div>
+        <Tag>{"Press R To do ..."}</Tag>
+      </div>
+    )
+  }
+  return (
+    <div style={{ 
+      width: '195px', height: '129px', 
+      border: '1px solid var(--semi-color-border)',
+      boxSizing: 'border-box',
+      backgroundColor: 'rgb(249,249,249)',
+      display: 'flex',
+      flexDirection: 'column',
+      alignItems: 'center',
+      justifyContent: 'center',
+      fontSize: '24px',
+      fontWeight: 'bold',
+      rowGap: '5px',
+    }}>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey}></HotKeys>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} content={['⌘ ','⏎']}></HotKeys>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} render={button}></HotKeys>
+    </div>
+  );
 }

+ 141 - 0
packages/semi-ui/hotKeys/_story/hotKeys.stories.tsx

@@ -0,0 +1,141 @@
+import React, { useState } from 'react';
+import { Button, Tag } from '@douyinfe/semi-ui'
+import { HotKeys } from '../../index';
+
+export default {
+  title: 'HotKeys'
+}
+
+export const Demo = () => {
+  const hotKeys = [HotKeys.Keys.Control, "k"]
+  const [cnt, setCnt] = useState(0)
+  const onHotKey = (e) => {
+    console.log(e)
+    setCnt(cnt+1)
+  }
+  return (
+    <div>
+      <HotKeys hotKeys={hotKeys} content={["Control", "K"]} onHotKey={onHotKey} onClick={() => setCnt(cnt + 1)} mergeMetaCtrl></HotKeys>
+      <pre id='pre'>{cnt}</pre>
+    </div>
+  );
+}
+
+export const renderButton = () => {
+  const hotKeys = [HotKeys.Keys.R]
+  const [cnt, setCnt] = useState(0)
+  const onHotKey = () => {
+    setCnt(cnt+1)
+  }
+  const button = () => {
+    return (
+      <div>
+        <Button>{"按下R即可加一"}</Button>
+      </div>
+    )
+  }
+  return (
+    <div>
+      <pre id='pre'>{" cnt:" + cnt}</pre>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} render={button}></HotKeys>
+    </div>
+
+  );
+}
+
+export const renderNull = () => {
+  const hotKeys = ["r"]
+  const [cnt, setCnt] = useState(0)
+  const onHotKey = () => {
+    setCnt(cnt+1)
+  }
+  return (
+    <div>
+      <span>{" cnt:" + cnt}</span>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} render={null}></HotKeys>
+    </div>
+
+  );
+}
+
+export const combine = () => {
+  const hotKeys = [HotKeys.Keys.Meta, HotKeys.Keys.Alt, "k"]
+  const [cnt, setCnt] = useState(0)
+  const onHotKey = () => {
+    setCnt(cnt+1)
+  }
+  return (
+    <div>
+      <pre id='pre'>{cnt}</pre>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey}></HotKeys>
+      <HotKeys hotKeys={["Meta", "Shift", "k"]} onHotKey={onHotKey}></HotKeys>
+    </div>
+    
+  );
+}
+
+export const target = () => {
+  const hotKeys = ["Meta", HotKeys.Keys.S]
+  const [cnt, setCnt] = useState(0)
+  const onHotKey = () => {
+    setCnt(cnt+1)
+  }
+  
+  const target = <input id="test" placeholder='test for target'></input>
+  return (
+    <div>
+      {target}
+      <pre id='pre'>{cnt}</pre>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} getListenerTarget={() => document.getElementById("test")}></HotKeys>
+    </div>
+    
+  );
+}
+
+export const disabled = () => {
+  const hotKeys = ["Meta", "k"]
+  const [cnt, setCnt] = useState(0)
+  const onHotKey = () => {
+    setCnt(cnt+1)
+  }
+  return (
+    <div>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey}></HotKeys>
+      <pre id='pre'>{cnt}</pre>
+    </div>
+  );
+}
+
+export const hotKeys = () => {
+  const hotKeys = ["Meta", "k"]
+  const [cnt, setCnt] = useState(0)
+  const onHotKey = () => {
+    setCnt(cnt+1)
+  }
+  const button = () => {
+    return (
+      <div>
+        <Tag>{"Press R To do ..."}</Tag>
+      </div>
+    )
+  }
+  return (
+    <div style={{ 
+      width: '195px', height: '129px', 
+      border: '1px solid var(--semi-color-border)',
+      boxSizing: 'border-box',
+      backgroundColor: 'rgb(249,249,249)',
+      display: 'flex',
+      flexDirection: 'column',
+      alignItems: 'center',
+      justifyContent: 'center',
+      fontSize: '24px',
+      fontWeight: 'bold',
+      rowGap: '5px',
+    }}>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey}></HotKeys>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} content={['⌘ ','⏎']}></HotKeys>
+      <HotKeys hotKeys={hotKeys} onHotKey={onHotKey} render={button}></HotKeys>
+    </div>
+  );
+}

+ 0 - 124
packages/semi-ui/hotKeys/_story/keyboardShortCut.stories.tsx

@@ -1,124 +0,0 @@
-import React, { useState } from 'react';
-import { Button } from '@douyinfe/semi-ui/';
-import {
-    storiesOf
-} from '@storybook/react';
-// import { withKnobs, text, boolean } from '@storybook/addon-knobs';
-
-import { Switch } from '../../index';
-
-const stories = storiesOf('Switch', module);
-
-// stories.addDecorator(withKnobs);;
-
-stories.add('switch', () => (
-    <div>
-        <Switch onChange={(v, e) => console.log(v)} aria-label='power-switch'>
-        </Switch>
-        <Switch defaultChecked={true} onChange={(v, e) => console.log(v)} aria-label='power-switch'>
-        </Switch>
-    </div>
-));
-
-
-stories.add('switch size', () => (
-    <div>
-        <Switch onChange={(v, e) => console.log(v)}></Switch>
-        <Switch onChange={(v, e) => console.log(v)} size='small' aria-label='power-switch'></Switch>
-        <Switch onChange={(v, e) => console.log(v)} size='large' aria-label='power-switch'></Switch>
-    </div>
-));
-
-stories.add('switch checkedText &  uncheckedText', () => (
-    <div>
-        <Switch defaultChecked checkedText='开' uncheckedText='关' aria-label='power-switch'/>
-        <Switch checkedText={'|'} uncheckedText='〇' />
-        <br/><br/>
-        <Switch checkedText='开' uncheckedText='关' />
-        <Switch defaultChecked checkedText='|' uncheckedText='〇' aria-label='power-switch'/>
-        <br/><br/>
-        <Switch checkedText='开' uncheckedText='关' size='large' aria-label='power-switch'/>
-        <Switch checkedText='|' uncheckedText='〇' size='large' aria-label='power-switch'/>
-        <br/><br/>
-        <Switch defaultChecked checkedText='开' uncheckedText='关' size='large' aria-label='power-switch'/>
-        <Switch defaultChecked checkedText='|' uncheckedText='〇' size='large' aria-label='power-switch'/>
-    </div>
-));
-
-stories.add('switch disabled', () => (
-    <>
-        <Switch disabled>
-            disabled
-        </Switch>
-
-        <Switch disabled checked={true} onChange={(v, e) => console.log(v)} aria-label='power-switch'>
-        </Switch>
-    </>
-));
-
-
-const ControlledSwitch = () => {
-    const [checked, onChange] = useState(true);
-    return (
-        <Switch checked={checked} onChange={(v, e) => onChange(v)} />
-    );
-};
-stories.add('switch checked + onChange', () => <ControlledSwitch/>);
-
-const UnControlledSwitch = () => {
-    const onChange = checked => {
-        console.log(checked);
-    };
-    return (
-        <>
-            {/* <Switch onChange={onChange} defaultChecked={false}/> */}
-            <Switch onChange={onChange} defaultChecked={true}/>
-        </>
-    );
-};
-stories.add('switch defaultChecked + onChange', () => <UnControlledSwitch/>);
-
-class LoadingDemo extends React.Component {
-    constructor(props) {
-        super(props);
-        this.state = {
-            checked: true,
-            loading:false
-        }
-        this.onChange = this.onChange.bind(this);
-    }
-    onChange(checked) {
-        this.setState({ checked });
-    }
-    render() {
-        return (
-            <>
-                <Button onClick={() => { this.setState({ checked: true }); }}>
-                    checked
-                </Button>
-                <br /><br />
-                <Button onClick={() => { this.setState({ checked: false }); }}>
-                    unchecked
-                </Button>
-                <br /><br />
-                <Button onClick={() => { this.setState({ loading: !this.state.loading }); }}>
-                    loading
-                </Button>
-                <br /><br />
-                <Switch
-                    checked={this.state.checked}
-                    onChange={this.onChange}
-                    loading={this.state.loading}>
-                </Switch>
-                <br /><br />
-                <hr />
-                <Switch loading disabled/>
-                <br /><br />
-                <Switch loading disabled defaultChecked/>
-                <br /><br />
-            </>
-        )
-    }
-}
-
-stories.add('loading', () => <LoadingDemo/>);

+ 22 - 22
packages/semi-ui/hotKeys/index.tsx

@@ -2,19 +2,19 @@ import React, { ReactNode } from 'react';
 import classNames from 'classnames';
 import PropTypes from 'prop-types';
 import HotKeysFoudation, { HotKeysAdapter } from '@douyinfe/semi-foundation/hotKeys/foundation';
-import { cssClasses, strings } from '@douyinfe/semi-foundation/hotKeys/constants';
+import { cssClasses, Keys } from '@douyinfe/semi-foundation/hotKeys/constants';
 import BaseComponent from '../_base/baseComponent';
 import { noop } from 'lodash';
 import '@douyinfe/semi-foundation/hotKeys/hotKeys.scss';
-
 const prefixCls = cssClasses.PREFIX;
 
 export interface HotKeysProps {
+    preventDefault?: boolean;
     hotKeys?: KeyboardEvent["key"][];
     content?: string[];
     onClick?: () => void;
-    clickable?: boolean;
-    disabled?: boolean;
+    onHotKey?: (e: KeyboardEvent) => void;
+    mergeMetaCtrl?: boolean;
     render?: () => ReactNode | ReactNode;
     getListenerTarget?: () => HTMLElement;
     className?: string;
@@ -22,16 +22,16 @@ export interface HotKeysProps {
 }
 
 export interface HotKeysState {
-    disabled: boolean
 }
 
 class HotKeys extends BaseComponent<HotKeysProps, HotKeysState> {
     static propTypes = {
+        preventDefalut: PropTypes.bool,
         hotKeys: PropTypes.arrayOf(PropTypes.string),
         content: PropTypes.arrayOf(PropTypes.string),
         onClick: PropTypes.func,
-        clickable: PropTypes.bool,
-        disabled: PropTypes.bool,
+        onHotKey: PropTypes.func,
+        mergeMetaCtrl: PropTypes.bool,
         render: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
         getListenerTarget: PropTypes.func,
         className: PropTypes.string,
@@ -39,21 +39,23 @@ class HotKeys extends BaseComponent<HotKeysProps, HotKeysState> {
     };
 
     static defaultProps: Partial<HotKeysProps> = {
+        preventDefault: false,
         hotKeys: null,
         content: null,
         onClick: noop,
-        clickable: false,
-        disabled: false,
+        onHotKey: noop,
+        mergeMetaCtrl: false,
         render: undefined,
         getListenerTarget: () => document.body,
         className: '',
         style: null,
     };
 
+    static Keys = Keys
+
     constructor(props: HotKeysProps) {
         super(props);
         this.state = {
-            disabled: false
         };
         this.foundation = new HotKeysFoudation(this.adapter);
     }
@@ -72,23 +74,18 @@ class HotKeys extends BaseComponent<HotKeysProps, HotKeysState> {
     get adapter(): HotKeysAdapter<HotKeysProps, HotKeysState> {
         return {
             ...super.adapter,
-            notifyClick: () => {
-                if (this.props.onClick) {
-                    this.props.onClick();
-                }
+            notifyHotKey: (e: KeyboardEvent) => {
+                this.props.onHotKey?.(e);
             },
             getListenerTarget: () => {
-                if (this.props.getListenerTarget) {
-                    return this.props.getListenerTarget();
-                }
-                return document.body;
+                return this.props.getListenerTarget?.() ?? document.body;
             },
         };
     }
 
 
     render() {
-        const { hotKeys, content, onClick, clickable, disabled, render, getListenerTarget, className, style, ...rest } = this.props;
+        const { hotKeys, content, onClick, render, getListenerTarget, className, style, ...rest } = this.props;
  
         if (typeof render !== 'undefined') {
             if (render === null || (typeof render === 'function' && render() === null)) {
@@ -96,9 +93,11 @@ class HotKeys extends BaseComponent<HotKeysProps, HotKeysState> {
             }
             return (
                 <div 
-                    onClick={clickable ? onClick : noop}
+                    onClick={onClick}
                     className={classNames(prefixCls, className)}
-                    style={style}>
+                    style={style}
+                    {...this.getDataAttr(rest)}    
+                >
                     { typeof render === 'function' ? render() : render }
                 </div>
             );
@@ -107,9 +106,10 @@ class HotKeys extends BaseComponent<HotKeysProps, HotKeysState> {
 
         return (
             <div
-                onClick={clickable ? onClick : noop}
+                onClick={onClick}
                 className={classNames(prefixCls, className)}
                 style={style}
+                {...this.getDataAttr(rest)}  
             >
                 {renderContent.map((key: KeyboardEvent["key"], index) => {
                     return index === 0 ?

+ 1 - 0
packages/semi-ui/index.ts

@@ -21,6 +21,7 @@ export { default as CheckboxGroup } from './checkbox/checkboxGroup';
 export { default as Collapse } from './collapse';
 export { default as Collapsible } from './collapsible';
 export { default as ConfigProvider } from './configProvider';
+export { ConfigConsumer } from "./configProvider";
 export { default as DatePicker } from './datePicker';
 export { default as Descriptions } from './descriptions';
 export { default as Divider } from './divider';

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

@@ -113,6 +113,7 @@
         "through2": "^4.0.2",
         "ts-loader": "^5.4.5",
         "webpack": "^5.77.0",
-        "webpackbar": "^5.0.0-3"
+        "webpackbar": "^5.0.0-3",
+        "@testing-library/react": "^12"
     }
 }

+ 2 - 1
test/setup.js

@@ -4,7 +4,7 @@ import React from 'react';
 import sinon from 'sinon';
 import enzymeToJson from 'enzyme-to-json';
 import jest from 'jest';
-
+import { render as testRender } from '@testing-library/react';
 import jsdom from 'jsdom';
 import crypto from 'crypto';
 import { advanceBy, advanceTo, clear } from 'jest-date-mock';
@@ -42,6 +42,7 @@ global.shallow = shallow;
 global.render = render;
 global.mount = mount;
 global.sinon = sinon;
+global.testRender = testRender
 
 Object.defineProperty(global.self, 'crypto', {
     value: {

Файловите разлики са ограничени, защото са твърде много
+ 297 - 359
yarn.lock


Някои файлове не бяха показани, защото твърде много файлове са промени