Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add 'Keys' variable to enum values and check for hotKeys #2463

Merged
merged 15 commits into from
Sep 10, 2024
Merged
218 changes: 134 additions & 84 deletions content/input/hotkeys/index-en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
---
Expand All @@ -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';
Expand All @@ -20,29 +21,53 @@ 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 blockDefault 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 `Keys` wrapper to set hotkeys.
```jsx import
import { Keys } from '@douyinfe/semi-ui';
```

```jsx live=true
import React, { useState } from 'react';
import { HotKeys } from '@douyinfe/semi-ui';
import { HotKeys, Keys, 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 = [Keys.Control, 'Shift', 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>
);
}
Expand All @@ -54,71 +79,106 @@ Set the characters through `content`

```jsx live=true
import React, { useState } from 'react';
import { HotKeys } from '@douyinfe/semi-ui';
import { HotKeys, Keys, 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 = [Keys.Control, 'Shift', 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>
);
}
```

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, Keys, Modal, Tag } from '@douyinfe/semi-ui';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

把枚举的 Keys 放到 HotKeys 的static 属性上吧,不要让用户单独 import 了,有点分散


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 = [Keys.Control, 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 `blockDefault`.
```jsx live=true
import React, { useState } from 'react';
import { HotKeys } from '@douyinfe/semi-ui';
import { HotKeys, Keys, 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 = [Keys.Meta, Keys.S]

return (
<div>
<HotKeys hotKeys={hotKeys} onClick={onClick} clickable></HotKeys>
<div>{cnt}</div>
<HotKeys hotKeys={hotKeys} onHotKey={showDialog} blockDefault></HotKeys>
<br />
<HotKeys hotKeys={[Keys.Control, Keys.S]} onHotKey={showDialog} blockDefault></HotKeys>
<Modal
title="Dialog"
visible={visible}
onOk={handleOk}
onCancel={handleCancel}
>
This is the Modal opened by hotkey: {'Meta/Control + S'}.
</Modal>
</div>
);
}
Expand All @@ -128,46 +188,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>
);
}
Expand All @@ -180,14 +230,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[] | - |
| blockDefault | Whether to prevent the default behavior of the shortcut | boolean | false |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改成和原生一样的 preventDefault 吧

| 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 | () => void | - |
| render | Replace the element | () => ReactNode \| ReactNode | |
| style | style | CSSProperties | |

## Design Tokens
Expand Down
Loading