-
-
Notifications
You must be signed in to change notification settings - Fork 367
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add experimental
KeyBinder
component
- Loading branch information
Showing
7 changed files
with
188 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
/Portal | ||
/Popover | ||
/Menu | ||
/KeyBinder | ||
/Hidden | ||
/Group | ||
/Form | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import * as React from "react"; | ||
import { unstable_createComponent } from "../utils/createComponent"; | ||
import { mergeProps } from "../utils/mergeProps"; | ||
import { unstable_useProps } from "../system/useProps"; | ||
import { Keys } from "../__utils/types"; | ||
import { unstable_useOptions } from "../system"; | ||
|
||
type KeyMap = { | ||
[key: string]: | ||
| ((event: React.KeyboardEvent<any>) => any) | ||
| null | ||
| false | ||
| undefined; | ||
}; | ||
|
||
export type unstable_KeyBinderOptions = { | ||
/** | ||
* TODO: Description | ||
*/ | ||
keyMap?: KeyMap | ((event: React.KeyboardEvent) => KeyMap); | ||
/** | ||
* TODO: Description | ||
*/ | ||
onKey?: (event: React.KeyboardEvent) => any; | ||
/** | ||
* TODO: Description | ||
*/ | ||
preventDefault?: boolean | ((event: React.KeyboardEvent) => boolean); | ||
/** | ||
* TODO: Description | ||
*/ | ||
stopPropagation?: boolean | ((event: React.KeyboardEvent) => boolean); | ||
}; | ||
|
||
export type unstable_KeyBinderProps = React.HTMLAttributes<any> & | ||
React.RefAttributes<any>; | ||
|
||
export function unstable_useKeyBinder( | ||
{ preventDefault = true, ...options }: unstable_KeyBinderOptions = {}, | ||
htmlProps: unstable_KeyBinderProps = {} | ||
) { | ||
let _options: unstable_KeyBinderOptions = { preventDefault, ...options }; | ||
_options = unstable_useOptions("KeyBinder", _options, htmlProps); | ||
|
||
htmlProps = mergeProps( | ||
{ | ||
onKeyDown: event => { | ||
if (!_options.keyMap) return; | ||
|
||
const keyMap = | ||
typeof _options.keyMap === "function" | ||
? _options.keyMap(event) | ||
: _options.keyMap; | ||
|
||
const shouldPreventDefault = | ||
typeof _options.preventDefault === "function" | ||
? _options.preventDefault(event) | ||
: _options.preventDefault; | ||
|
||
const shouldStopPropagation = | ||
typeof _options.stopPropagation === "function" | ||
? _options.stopPropagation(event) | ||
: _options.stopPropagation; | ||
|
||
if (event.key in keyMap) { | ||
const action = keyMap[event.key]; | ||
if (typeof action === "function") { | ||
if (shouldPreventDefault) event.preventDefault(); | ||
if (shouldStopPropagation) event.stopPropagation(); | ||
if (_options.onKey) _options.onKey(event); | ||
action(event); | ||
} | ||
} | ||
} | ||
} as typeof htmlProps, | ||
htmlProps | ||
); | ||
htmlProps = unstable_useProps("KeyBinder", options, htmlProps); | ||
return htmlProps; | ||
} | ||
|
||
const keys: Keys<unstable_KeyBinderOptions> = [ | ||
"keyMap", | ||
"onKey", | ||
"preventDefault", | ||
"stopPropagation" | ||
]; | ||
|
||
unstable_useKeyBinder.__keys = keys; | ||
|
||
export const unstable_KeyBinder = unstable_createComponent({ | ||
as: "div", | ||
useHook: unstable_useKeyBinder | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
--- | ||
path: /docs/key-binder | ||
--- | ||
|
||
# KeyBinder | ||
|
||
> **This is experimental** and may have breaking changes in minor or patch version updates. Issues for this module will have lower priority. Even so, if you use it, feel free to [give us feedback](https://github.com/reakit/reakit/issues/new/choose). | ||
`KeyBinder` is an abstract component that adds key bindings to other components. | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install reakit | ||
``` | ||
|
||
Learn more in [Get started](/docs/get-started). | ||
|
||
## Usage | ||
|
||
```jsx | ||
import React from "react"; | ||
import { unstable_KeyBinder as KeyBinder } from "reakit/KeyBinder"; | ||
|
||
function Example() { | ||
const [count, setCount] = React.useState(0); | ||
return ( | ||
<KeyBinder tabIndex={0} keyMap={{ Enter: () => setCount(count + 1) }}> | ||
{count} | ||
</KeyBinder> | ||
); | ||
} | ||
``` | ||
|
||
### Composing with other components | ||
|
||
It's better used in combination with other components: | ||
|
||
```jsx | ||
import { unstable_useKeyBinder as useKeyBinder } from "reakit/KeyBinder"; | ||
import { Hidden, HiddenDisclosure, useHiddenState } from "reakit/Hidden"; | ||
|
||
function Example() { | ||
const hidden = useHiddenState(); | ||
const keybindings = useKeyBinder({ keyMap: { a: hidden.toggle } }); | ||
return ( | ||
<> | ||
<HiddenDisclosure {...hidden} {...keybindings}> | ||
{`Press "a" to toggle`} | ||
</HiddenDisclosure> | ||
<Hidden {...hidden}>Yaay!</Hidden> | ||
</> | ||
); | ||
} | ||
``` | ||
|
||
## Composition | ||
|
||
- `KeyBinder` is used by [Menu](/docs/menu) and [MenuDisclosure](/docs/menu). | ||
|
||
Learn more in [Composition](/docs/composition#props-hooks). | ||
|
||
## Props | ||
|
||
<!-- Automatically generated --> | ||
|
||
### `KeyBinder` | ||
|
||
| Name | Type | Description | | ||
|------|------|-------------| | ||
| <strong><code>keyMap</code> </strong> | <code title="{ [key: string]: false | ((event: KeyboardEvent<any>) => any) | null | undefined; } | undefined">{ [key: string]: false | ((...</code> | TODO: Description | | ||
| <strong><code>onKey</code> </strong> | <code title="((event: KeyboardEvent<any>) => any) | undefined">((event: KeyboardEvent<any>...</code> | TODO: Description | | ||
| <strong><code>preventDefault</code> </strong> | <code>boolean | undefined</code> | TODO: Description | | ||
| <strong><code>stopPropagation</code> </strong> | <code>boolean | undefined</code> | TODO: Description | |
15 changes: 15 additions & 0 deletions
15
packages/reakit/src/KeyBinder/__tests__/KeyBinder-test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// TODO: Add more tests | ||
import * as React from "react"; | ||
import { render } from "react-testing-library"; | ||
import { unstable_KeyBinder as KeyBinder } from "../KeyBinder"; | ||
|
||
test("render", () => { | ||
const { baseElement } = render(<KeyBinder />); | ||
expect(baseElement).toMatchInlineSnapshot(` | ||
<body> | ||
<div> | ||
<div /> | ||
</div> | ||
</body> | ||
`); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./KeyBinder"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters