@keybindy/react 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +189 -0
- package/dist/Keybindy.d.ts +87 -0
- package/dist/Keybindy.js +74 -0
- package/dist/ShortcutLabel.d.ts +57 -0
- package/dist/ShortcutLabel.js +69 -0
- package/dist/index.d.ts +216 -0
- package/dist/index.js +3 -0
- package/dist/useKeybindy.d.ts +72 -0
- package/dist/useKeybindy.js +253 -0
- package/package.json +71 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 PRASSamin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# @keybindy/react
|
|
2
|
+
|
|
3
|
+
@keybindy/react is the official React integration for the [Keybindy](https://www.npmjs.com/package/@keybindy/core) keyboard shortcut system. Built on top of `@keybindy/core`, this package brings powerful and scoped keyboard bindings to your React applications — with components and hooks tailored to React’s architecture.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@keybindy/react)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 🧠 What is @keybindy/react?
|
|
11
|
+
|
|
12
|
+
While `@keybindy/core` gives you the underlying logic to register and manage shortcuts in any JavaScript environment, **@keybindy/react** wraps it in a React-friendly API with scoped context, declarative components, and hooks for full control.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# npm
|
|
20
|
+
npm install @keybindy/react
|
|
21
|
+
|
|
22
|
+
# yarn
|
|
23
|
+
yarn add @keybindy/react
|
|
24
|
+
|
|
25
|
+
# bun
|
|
26
|
+
bun add @keybindy/react
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
#### `<Keybindy />` component
|
|
34
|
+
|
|
35
|
+
The core declarative component. Register all your scoped or global shortcuts with ease.
|
|
36
|
+
|
|
37
|
+
| Prop | Type | Default | Description |
|
|
38
|
+
| ----------------- | -------------------- | ----------- | -------------------------------------------------------------- |
|
|
39
|
+
| `logs` | `boolean` | `false` | Whether to enable debug logs in the console. |
|
|
40
|
+
| `onShortcutFired` | `fn(info: Shortcut)` | `undefined` | Optional callback to handle shortcut firing events. |
|
|
41
|
+
| `disabled` | `boolean` | `false` | Whether to disable all shortcuts within the component's scope. |
|
|
42
|
+
| `scope` | `string` | `global` | The scope to apply the shortcuts to. |
|
|
43
|
+
| `shortcuts` | `Shortcut[]` | `[]` | Array of shortcut objects to register. |
|
|
44
|
+
| `children` | `React.ReactNode` | `undefined` | The content to render inside the component. |
|
|
45
|
+
|
|
46
|
+
##### Example
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { Keybindy } from '@keybindy/react';
|
|
50
|
+
|
|
51
|
+
<Keybindy
|
|
52
|
+
scope="global"
|
|
53
|
+
shortcuts={[
|
|
54
|
+
{
|
|
55
|
+
keys: ['A'],
|
|
56
|
+
handler: () => console.log('A pressed'),
|
|
57
|
+
options: {
|
|
58
|
+
preventDefault: true,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
keys: ['O', 'P'],
|
|
63
|
+
handler: () => setIsOpen(true),
|
|
64
|
+
options: {
|
|
65
|
+
sequenceDelay: 1000,
|
|
66
|
+
sequential: true,
|
|
67
|
+
preventDefault: true,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
keys: ['R'],
|
|
72
|
+
handler: () => window.open('https://react.dev', '_blank'),
|
|
73
|
+
options: {
|
|
74
|
+
preventDefault: true,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
]}
|
|
78
|
+
/>;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### `<ShortcutLabel />` component
|
|
82
|
+
|
|
83
|
+
A lightweight UI component to render visually styled shortcut hints.
|
|
84
|
+
|
|
85
|
+
| Prop | Type | Default | Description |
|
|
86
|
+
| ----------- | ---------------------------------------------- | ----------- | ---------------------------------------------- |
|
|
87
|
+
| `keys` | `Keys` | `[]` | The key combination(s) to display. |
|
|
88
|
+
| `renderKey` | `fn(key: string, index: number, keys: Keys[])` | `undefined` | Custom renderer for each key badge or segment. |
|
|
89
|
+
|
|
90
|
+
##### Example
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { ShortcutLabel } from '@keybindy/react';
|
|
94
|
+
|
|
95
|
+
<ShortcutLabel
|
|
96
|
+
keys={['ctrl', 'alt', 'delete']}
|
|
97
|
+
renderKey={(key, i, all) => (
|
|
98
|
+
<>
|
|
99
|
+
<span style={{ color: '#00eaff' }}>{key.toUpperCase()}</span>
|
|
100
|
+
{i < all.length - 1 && <span style={{ opacity: 0.5 }}> + </span>}
|
|
101
|
+
</>
|
|
102
|
+
)}
|
|
103
|
+
/>;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `useKeybindy` Hook
|
|
107
|
+
|
|
108
|
+
A powerful hook that gives you full control over the shortcut system via the ShortcutManager under the hood. Best for dynamic or advanced use cases.
|
|
109
|
+
|
|
110
|
+
##### Available methods
|
|
111
|
+
|
|
112
|
+
| Method | Description |
|
|
113
|
+
| --------------------------------------------------------------------- | -------------------------------------------- |
|
|
114
|
+
| [`register()`](https://github.com/keybindy/core#register) | Register a shortcut |
|
|
115
|
+
| [`unregister()`](https://github.com/keybindy/core#unregister) | Unregister a shortcut |
|
|
116
|
+
| [`enable()`](https://github.com/keybindy/core#enable--disable--toggle) | Enable a specific shortcut |
|
|
117
|
+
| [`disable()`](https://github.com/keybindy/core#enable--disable--toggle) | Disable a specific shortcut |
|
|
118
|
+
| [`toggle()`](https://github.com/keybindy/core#enable--disable--toggle) | Toggle a shortcut on/off |
|
|
119
|
+
| [`enableAll()`](https://github.com/keybindy/core#enableall--disableall) | Enable all shortcuts (global or scoped) |
|
|
120
|
+
| [`disableAll()`](https://github.com/keybindy/core#enableall--disableall) | Disable all shortcuts (global or scoped) |
|
|
121
|
+
| [`setScope()`](https://github.com/keybindy/core#setactivescope) | Set the active scope |
|
|
122
|
+
| [`resetScope()`](https://github.com/keybindy/core#resetScope) | Reset to default scope |
|
|
123
|
+
| [`getScopes()`](https://github.com/keybindy/core#getScopes) | Get all defined scopes |
|
|
124
|
+
| [`getActiveScope()`](https://github.com/keybindy/core#getActiveScope) | Get the current active scope |
|
|
125
|
+
| [`popScope()`](https://github.com/keybindy/core#popScope) | Remove the top scope from the scope stack |
|
|
126
|
+
| [`pushScope()`](https://github.com/keybindy/core#pushScope) | Push a new scope onto the scope stack |
|
|
127
|
+
| [`getCheatSheet()`](https://github.com/keybindy/core#getCheatsheet) | Retrieve all shortcuts (optionally by scope) |
|
|
128
|
+
| [`onTyping()`](https://github.com/keybindy/core#onTyping) | Listen to every key press |
|
|
129
|
+
| [`destroy()`](https://github.com/keybindy/core#destroy) | Tear down the current manager instance |
|
|
130
|
+
| [`clear()`](https://github.com/keybindy/core#clear) | Unregister all shortcuts |
|
|
131
|
+
| [`getScopeInfo()`](https://github.com/keybindy/core#getScopeInfo) | Retrieve metadata about a specific scope |
|
|
132
|
+
| [`isScopeActive()`](https://github.com/keybindy/core#isScopeActive) | Check if a scope is currently active |
|
|
133
|
+
|
|
134
|
+
> _All methods mirror `@keybindy/core` with a React-friendly API._
|
|
135
|
+
|
|
136
|
+
##### Example
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { useKeybindy } from '@keybindy/react';
|
|
140
|
+
|
|
141
|
+
const { register, unregister, setScope, getCheatSheet } = useKeybindy();
|
|
142
|
+
|
|
143
|
+
React.useEffect(() => {
|
|
144
|
+
register(
|
|
145
|
+
['ctrl', 'k'],
|
|
146
|
+
() => {
|
|
147
|
+
console.log('Shortcut fired!');
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
scope: 'editor',
|
|
151
|
+
preventDefault: true,
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return () => {
|
|
156
|
+
unregister(['ctrl', 'k'], 'editor');
|
|
157
|
+
};
|
|
158
|
+
}, []);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Reference
|
|
164
|
+
|
|
165
|
+
If you're looking for more detailed implementation logic and architecture, check out the [@keybindy/core](https://www.npmjs.com/package/@keybindy/core) documentation for an in-depth look at how shortcut handling works under the hood.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 🧩 Want More?
|
|
170
|
+
|
|
171
|
+
This package is part of the Keybindy Ecosystem:
|
|
172
|
+
|
|
173
|
+
| Package | Description |
|
|
174
|
+
| -------------------------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
175
|
+
| [`@keybindy/core`](https://npmjs.com/package/@keybindy/core) | The core JavaScript library. Framework-agnostic, fully typed, and tree-shakable. |
|
|
176
|
+
| [`@keybindy/react`](https://npmjs.com/package/@keybindy/react) | React bindings with hooks and components for easy integration. |
|
|
177
|
+
| _Coming Soon_ | Stay tuned! |
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Contributing
|
|
182
|
+
|
|
183
|
+
PRs, issues, and ideas are welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
|
|
184
|
+
|
|
185
|
+
If you're adding a new framework integration (like Vue or Svelte), feel free to open a draft PR — we'd love to collaborate.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
> _Might be new in the shortcut game, but Keybindy’s here to change the frame — fast, flexible, and ready to claim. 🎯_
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Keys, Shortcut as ShortcutType, ShortcutHandler, ShortcutOptions } from '@keybindy/core';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a keyboard shortcut definition.
|
|
5
|
+
*/
|
|
6
|
+
type ShortcutDefinition = {
|
|
7
|
+
/**
|
|
8
|
+
* The key combination(s) to listen for.
|
|
9
|
+
* Can be a single array of keys or an array of key combinations.
|
|
10
|
+
*/
|
|
11
|
+
keys: Keys[] | Keys[][];
|
|
12
|
+
/**
|
|
13
|
+
* Callback function to invoke when the shortcut is triggered.
|
|
14
|
+
*/
|
|
15
|
+
handler: ShortcutHandler;
|
|
16
|
+
/**
|
|
17
|
+
* Optional configuration, including scope and other metadata.
|
|
18
|
+
*/
|
|
19
|
+
options?: Omit<ShortcutOptions, 'scope'>;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Props for the `<Keybindy />` component.
|
|
23
|
+
*/
|
|
24
|
+
type KeybindyProps = {
|
|
25
|
+
/**
|
|
26
|
+
* The scope under which the shortcuts should be active.
|
|
27
|
+
* This allows managing different contexts for shortcuts.
|
|
28
|
+
*/
|
|
29
|
+
scope: string;
|
|
30
|
+
/**
|
|
31
|
+
* Array of shortcut definitions to register for this scope.
|
|
32
|
+
*/
|
|
33
|
+
shortcuts?: ShortcutDefinition[];
|
|
34
|
+
/**
|
|
35
|
+
* Whether the shortcuts should be disabled for this scope.
|
|
36
|
+
* Defaults to `false`.
|
|
37
|
+
*/
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Callback function that will be called when a shortcut is fired.
|
|
41
|
+
* Receives the fired shortcut info as an argument.
|
|
42
|
+
*/
|
|
43
|
+
onShortcutFired?: (info: ShortcutType) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Whether to enable debug logs in the console.
|
|
46
|
+
*/
|
|
47
|
+
logs?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* The content that will be rendered inside the Shortcut component.
|
|
50
|
+
*/
|
|
51
|
+
children: React.ReactNode;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* `<Keybindy />` is a React component that registers keyboard shortcuts within a given scope. It allows
|
|
55
|
+
* users to define custom shortcuts and their associated handlers, while managing scope-based shortcut behavior.
|
|
56
|
+
* The component listens for keyboard events and triggers the registered handler when the corresponding keys are pressed.
|
|
57
|
+
* It also provides an optional callback (`onShortcutFired`) to notify users when a shortcut is triggered.
|
|
58
|
+
*
|
|
59
|
+
* @component
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // Basic usage
|
|
63
|
+
* <Keybindy scope="global" shortcuts={[{ keys: ['ctrl', 's'], handler: saveDocument }]} >
|
|
64
|
+
* <div>Content with shortcuts</div>
|
|
65
|
+
* </Keybindy>
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // With custom callback for onShortcutFired
|
|
69
|
+
* <Keybindy
|
|
70
|
+
* scope="editor"
|
|
71
|
+
* shortcuts={[{ keys: ['ctrl', 'e'], handler: editDocument }]}
|
|
72
|
+
* onShortcutFired={(info) => console.log('Shortcut fired:', info)}
|
|
73
|
+
* >
|
|
74
|
+
* <div>Editor with shortcuts</div>
|
|
75
|
+
* </Keybindy>
|
|
76
|
+
*
|
|
77
|
+
* @param {ShortcutProps} props - Props for the Shortcut component.
|
|
78
|
+
* @param {string} props.scope - The scope under which the shortcuts should be active.
|
|
79
|
+
* @param {ShortcutDefinition[]} [props.shortcuts] - An array of shortcut definitions, each containing keys, handler, and options.
|
|
80
|
+
* @param {boolean} [props.disabled=false] - Whether the shortcuts should be disabled for this scope.
|
|
81
|
+
* @param {(info: Shortcut) => void} [props.onShortcutFired] - Optional callback triggered when a shortcut is fired, providing the shortcut info.
|
|
82
|
+
* @param {React.ReactNode} props.children - The children to be rendered inside the component, which can contain any JSX elements.
|
|
83
|
+
*
|
|
84
|
+
* @returns {JSX.Element} The rendered component with registered shortcuts within the provided scope.
|
|
85
|
+
*/
|
|
86
|
+
export declare const Keybindy: React.FC<KeybindyProps>;
|
|
87
|
+
export {};
|
package/dist/Keybindy.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { useKeybindy } from './useKeybindy.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `<Keybindy />` is a React component that registers keyboard shortcuts within a given scope. It allows
|
|
7
|
+
* users to define custom shortcuts and their associated handlers, while managing scope-based shortcut behavior.
|
|
8
|
+
* The component listens for keyboard events and triggers the registered handler when the corresponding keys are pressed.
|
|
9
|
+
* It also provides an optional callback (`onShortcutFired`) to notify users when a shortcut is triggered.
|
|
10
|
+
*
|
|
11
|
+
* @component
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Basic usage
|
|
15
|
+
* <Keybindy scope="global" shortcuts={[{ keys: ['ctrl', 's'], handler: saveDocument }]} >
|
|
16
|
+
* <div>Content with shortcuts</div>
|
|
17
|
+
* </Keybindy>
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // With custom callback for onShortcutFired
|
|
21
|
+
* <Keybindy
|
|
22
|
+
* scope="editor"
|
|
23
|
+
* shortcuts={[{ keys: ['ctrl', 'e'], handler: editDocument }]}
|
|
24
|
+
* onShortcutFired={(info) => console.log('Shortcut fired:', info)}
|
|
25
|
+
* >
|
|
26
|
+
* <div>Editor with shortcuts</div>
|
|
27
|
+
* </Keybindy>
|
|
28
|
+
*
|
|
29
|
+
* @param {ShortcutProps} props - Props for the Shortcut component.
|
|
30
|
+
* @param {string} props.scope - The scope under which the shortcuts should be active.
|
|
31
|
+
* @param {ShortcutDefinition[]} [props.shortcuts] - An array of shortcut definitions, each containing keys, handler, and options.
|
|
32
|
+
* @param {boolean} [props.disabled=false] - Whether the shortcuts should be disabled for this scope.
|
|
33
|
+
* @param {(info: Shortcut) => void} [props.onShortcutFired] - Optional callback triggered when a shortcut is fired, providing the shortcut info.
|
|
34
|
+
* @param {React.ReactNode} props.children - The children to be rendered inside the component, which can contain any JSX elements.
|
|
35
|
+
*
|
|
36
|
+
* @returns {JSX.Element} The rendered component with registered shortcuts within the provided scope.
|
|
37
|
+
*/
|
|
38
|
+
const Keybindy = ({ scope, shortcuts = [], children, disabled, onShortcutFired, logs = false, }) => {
|
|
39
|
+
const { register, unregister, manager, pushScope, popScope, getScopes, setScope } = useKeybindy({
|
|
40
|
+
onShortcutFired,
|
|
41
|
+
logs,
|
|
42
|
+
});
|
|
43
|
+
const prevScope = React.useRef(null);
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
prevScope.current = manager.getActiveScope();
|
|
46
|
+
// Add scope if doesn't exist
|
|
47
|
+
if (!getScopes()?.includes(scope)) {
|
|
48
|
+
pushScope(scope);
|
|
49
|
+
}
|
|
50
|
+
// Set this scope as active
|
|
51
|
+
setScope(scope);
|
|
52
|
+
// Register all shortcuts for this scope
|
|
53
|
+
shortcuts.forEach(({ keys, handler, options }) => {
|
|
54
|
+
register(keys, handler, { ...options, scope });
|
|
55
|
+
});
|
|
56
|
+
if (disabled) {
|
|
57
|
+
manager.disableAll(scope);
|
|
58
|
+
}
|
|
59
|
+
return () => {
|
|
60
|
+
shortcuts.forEach(({ keys }) => {
|
|
61
|
+
if (Array.isArray(keys[0])) {
|
|
62
|
+
keys.forEach(key => unregister(key, scope));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
unregister(keys, scope);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
popScope();
|
|
69
|
+
};
|
|
70
|
+
}, [scope, JSON.stringify(shortcuts)]);
|
|
71
|
+
return jsx(Fragment, { children: children });
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export { Keybindy };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Keys } from '@keybindy/core';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
interface ShortcutLabelProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> {
|
|
4
|
+
/**
|
|
5
|
+
* Array of keys to display
|
|
6
|
+
*/
|
|
7
|
+
keys: Omit<Keys, 'Ctrl (Left)' | 'Ctrl (Right)' | 'Shift (Left)' | 'Shift (Right)' | 'Alt (Left)' | 'Alt (Right)' | 'Meta (Left)' | 'Meta (Right)'>[];
|
|
8
|
+
/**
|
|
9
|
+
* Custom render function for each key
|
|
10
|
+
*/
|
|
11
|
+
renderKey?: (
|
|
12
|
+
/**
|
|
13
|
+
* The key to render
|
|
14
|
+
*/
|
|
15
|
+
key: string,
|
|
16
|
+
/**
|
|
17
|
+
* The index of the key in the array
|
|
18
|
+
*/
|
|
19
|
+
index: number,
|
|
20
|
+
/**
|
|
21
|
+
* All keys in the array
|
|
22
|
+
*/
|
|
23
|
+
allKeys: string[]) => React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* `<ShortcutLabel />` is a utility React component that visually renders a keyboard shortcut label.
|
|
27
|
+
*
|
|
28
|
+
* It accepts an array of keys (e.g. `["Ctrl", "S"]`) and renders a styled label using platform-aware
|
|
29
|
+
* symbols (⌘ for Mac, Ctrl for others). Users can also provide a custom render function to override
|
|
30
|
+
* the default display logic for advanced layouts or custom themes.
|
|
31
|
+
*
|
|
32
|
+
* @component
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Default usage
|
|
36
|
+
* <ShortcutLabel keys={['ctrl', 's']} />
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // With custom renderKey
|
|
40
|
+
* <ShortcutLabel
|
|
41
|
+
* keys={['ctrl', 'alt', 'delete']}
|
|
42
|
+
* renderKey={(key, i, all) => (
|
|
43
|
+
* <>
|
|
44
|
+
* <span style={{ color: '#00eaff' }}>{key.toUpperCase()}</span>
|
|
45
|
+
* {i < all.length - 1 && <span style={{ opacity: 0.5 }}> + </span>}
|
|
46
|
+
* </>
|
|
47
|
+
* )}
|
|
48
|
+
* />
|
|
49
|
+
*
|
|
50
|
+
* @param {ShortcutLabelProps} props - Props for the ShortcutLabel component
|
|
51
|
+
* @param {string[]} props.keys - The list of keys to display.
|
|
52
|
+
* @param {Function} [props.renderKey] - Optional custom render function for full control over how each key appears.
|
|
53
|
+
*
|
|
54
|
+
* @returns {JSX.Element} Rendered shortcut label
|
|
55
|
+
*/
|
|
56
|
+
export declare const ShortcutLabel: ({ keys, renderKey, style, ...props }: ShortcutLabelProps) => import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `<ShortcutLabel />` is a utility React component that visually renders a keyboard shortcut label.
|
|
6
|
+
*
|
|
7
|
+
* It accepts an array of keys (e.g. `["Ctrl", "S"]`) and renders a styled label using platform-aware
|
|
8
|
+
* symbols (⌘ for Mac, Ctrl for others). Users can also provide a custom render function to override
|
|
9
|
+
* the default display logic for advanced layouts or custom themes.
|
|
10
|
+
*
|
|
11
|
+
* @component
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Default usage
|
|
15
|
+
* <ShortcutLabel keys={['ctrl', 's']} />
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // With custom renderKey
|
|
19
|
+
* <ShortcutLabel
|
|
20
|
+
* keys={['ctrl', 'alt', 'delete']}
|
|
21
|
+
* renderKey={(key, i, all) => (
|
|
22
|
+
* <>
|
|
23
|
+
* <span style={{ color: '#00eaff' }}>{key.toUpperCase()}</span>
|
|
24
|
+
* {i < all.length - 1 && <span style={{ opacity: 0.5 }}> + </span>}
|
|
25
|
+
* </>
|
|
26
|
+
* )}
|
|
27
|
+
* />
|
|
28
|
+
*
|
|
29
|
+
* @param {ShortcutLabelProps} props - Props for the ShortcutLabel component
|
|
30
|
+
* @param {string[]} props.keys - The list of keys to display.
|
|
31
|
+
* @param {Function} [props.renderKey] - Optional custom render function for full control over how each key appears.
|
|
32
|
+
*
|
|
33
|
+
* @returns {JSX.Element} Rendered shortcut label
|
|
34
|
+
*/
|
|
35
|
+
const ShortcutLabel = ({ keys, renderKey, style, ...props }) => {
|
|
36
|
+
const isMac = typeof navigator !== 'undefined' && /Mac/.test(navigator.userAgent);
|
|
37
|
+
const defaultRenderKey = (key) => {
|
|
38
|
+
switch (key.toLowerCase()) {
|
|
39
|
+
case 'meta':
|
|
40
|
+
return isMac ? '⌘' : 'Ctrl';
|
|
41
|
+
case 'ctrl':
|
|
42
|
+
return 'Ctrl';
|
|
43
|
+
case 'shift':
|
|
44
|
+
return '⇧';
|
|
45
|
+
case 'alt':
|
|
46
|
+
return isMac ? '⌥' : 'Alt';
|
|
47
|
+
case 'enter':
|
|
48
|
+
return '↵';
|
|
49
|
+
default:
|
|
50
|
+
return key.toUpperCase();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const rendered = renderKey
|
|
54
|
+
? keys.map((key, i) => (jsx(React.Fragment, { children: renderKey(key, i, keys) }, i)))
|
|
55
|
+
: defaultRenderKey
|
|
56
|
+
? [keys.map(key => defaultRenderKey(key)).join(' + ')]
|
|
57
|
+
: [];
|
|
58
|
+
return (jsx("kbd", { style: {
|
|
59
|
+
fontFamily: 'monospace',
|
|
60
|
+
padding: '2.5px 5px',
|
|
61
|
+
border: '1px solid #cccccc2f',
|
|
62
|
+
backgroundColor: '#2e2e2e',
|
|
63
|
+
borderRadius: '4px',
|
|
64
|
+
userSelect: 'none',
|
|
65
|
+
...style,
|
|
66
|
+
}, ...props, children: rendered }));
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export { ShortcutLabel };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ShortcutManager, { Keys, ShortcutHandler, ShortcutOptions, Shortcut } from '@keybindy/core';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents a keyboard shortcut definition.
|
|
7
|
+
*/
|
|
8
|
+
type ShortcutDefinition = {
|
|
9
|
+
/**
|
|
10
|
+
* The key combination(s) to listen for.
|
|
11
|
+
* Can be a single array of keys or an array of key combinations.
|
|
12
|
+
*/
|
|
13
|
+
keys: Keys[] | Keys[][];
|
|
14
|
+
/**
|
|
15
|
+
* Callback function to invoke when the shortcut is triggered.
|
|
16
|
+
*/
|
|
17
|
+
handler: ShortcutHandler;
|
|
18
|
+
/**
|
|
19
|
+
* Optional configuration, including scope and other metadata.
|
|
20
|
+
*/
|
|
21
|
+
options?: Omit<ShortcutOptions, 'scope'>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Props for the `<Keybindy />` component.
|
|
25
|
+
*/
|
|
26
|
+
type KeybindyProps = {
|
|
27
|
+
/**
|
|
28
|
+
* The scope under which the shortcuts should be active.
|
|
29
|
+
* This allows managing different contexts for shortcuts.
|
|
30
|
+
*/
|
|
31
|
+
scope: string;
|
|
32
|
+
/**
|
|
33
|
+
* Array of shortcut definitions to register for this scope.
|
|
34
|
+
*/
|
|
35
|
+
shortcuts?: ShortcutDefinition[];
|
|
36
|
+
/**
|
|
37
|
+
* Whether the shortcuts should be disabled for this scope.
|
|
38
|
+
* Defaults to `false`.
|
|
39
|
+
*/
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Callback function that will be called when a shortcut is fired.
|
|
43
|
+
* Receives the fired shortcut info as an argument.
|
|
44
|
+
*/
|
|
45
|
+
onShortcutFired?: (info: Shortcut) => void;
|
|
46
|
+
/**
|
|
47
|
+
* Whether to enable debug logs in the console.
|
|
48
|
+
*/
|
|
49
|
+
logs?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The content that will be rendered inside the Shortcut component.
|
|
52
|
+
*/
|
|
53
|
+
children: React.ReactNode;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* `<Keybindy />` is a React component that registers keyboard shortcuts within a given scope. It allows
|
|
57
|
+
* users to define custom shortcuts and their associated handlers, while managing scope-based shortcut behavior.
|
|
58
|
+
* The component listens for keyboard events and triggers the registered handler when the corresponding keys are pressed.
|
|
59
|
+
* It also provides an optional callback (`onShortcutFired`) to notify users when a shortcut is triggered.
|
|
60
|
+
*
|
|
61
|
+
* @component
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Basic usage
|
|
65
|
+
* <Keybindy scope="global" shortcuts={[{ keys: ['ctrl', 's'], handler: saveDocument }]} >
|
|
66
|
+
* <div>Content with shortcuts</div>
|
|
67
|
+
* </Keybindy>
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* // With custom callback for onShortcutFired
|
|
71
|
+
* <Keybindy
|
|
72
|
+
* scope="editor"
|
|
73
|
+
* shortcuts={[{ keys: ['ctrl', 'e'], handler: editDocument }]}
|
|
74
|
+
* onShortcutFired={(info) => console.log('Shortcut fired:', info)}
|
|
75
|
+
* >
|
|
76
|
+
* <div>Editor with shortcuts</div>
|
|
77
|
+
* </Keybindy>
|
|
78
|
+
*
|
|
79
|
+
* @param {ShortcutProps} props - Props for the Shortcut component.
|
|
80
|
+
* @param {string} props.scope - The scope under which the shortcuts should be active.
|
|
81
|
+
* @param {ShortcutDefinition[]} [props.shortcuts] - An array of shortcut definitions, each containing keys, handler, and options.
|
|
82
|
+
* @param {boolean} [props.disabled=false] - Whether the shortcuts should be disabled for this scope.
|
|
83
|
+
* @param {(info: Shortcut) => void} [props.onShortcutFired] - Optional callback triggered when a shortcut is fired, providing the shortcut info.
|
|
84
|
+
* @param {React.ReactNode} props.children - The children to be rendered inside the component, which can contain any JSX elements.
|
|
85
|
+
*
|
|
86
|
+
* @returns {JSX.Element} The rendered component with registered shortcuts within the provided scope.
|
|
87
|
+
*/
|
|
88
|
+
declare const Keybindy: React.FC<KeybindyProps>;
|
|
89
|
+
|
|
90
|
+
interface ShortcutLabelProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> {
|
|
91
|
+
/**
|
|
92
|
+
* Array of keys to display
|
|
93
|
+
*/
|
|
94
|
+
keys: Omit<Keys, 'Ctrl (Left)' | 'Ctrl (Right)' | 'Shift (Left)' | 'Shift (Right)' | 'Alt (Left)' | 'Alt (Right)' | 'Meta (Left)' | 'Meta (Right)'>[];
|
|
95
|
+
/**
|
|
96
|
+
* Custom render function for each key
|
|
97
|
+
*/
|
|
98
|
+
renderKey?: (
|
|
99
|
+
/**
|
|
100
|
+
* The key to render
|
|
101
|
+
*/
|
|
102
|
+
key: string,
|
|
103
|
+
/**
|
|
104
|
+
* The index of the key in the array
|
|
105
|
+
*/
|
|
106
|
+
index: number,
|
|
107
|
+
/**
|
|
108
|
+
* All keys in the array
|
|
109
|
+
*/
|
|
110
|
+
allKeys: string[]) => React.ReactNode;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `<ShortcutLabel />` is a utility React component that visually renders a keyboard shortcut label.
|
|
114
|
+
*
|
|
115
|
+
* It accepts an array of keys (e.g. `["Ctrl", "S"]`) and renders a styled label using platform-aware
|
|
116
|
+
* symbols (⌘ for Mac, Ctrl for others). Users can also provide a custom render function to override
|
|
117
|
+
* the default display logic for advanced layouts or custom themes.
|
|
118
|
+
*
|
|
119
|
+
* @component
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* // Default usage
|
|
123
|
+
* <ShortcutLabel keys={['ctrl', 's']} />
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* // With custom renderKey
|
|
127
|
+
* <ShortcutLabel
|
|
128
|
+
* keys={['ctrl', 'alt', 'delete']}
|
|
129
|
+
* renderKey={(key, i, all) => (
|
|
130
|
+
* <>
|
|
131
|
+
* <span style={{ color: '#00eaff' }}>{key.toUpperCase()}</span>
|
|
132
|
+
* {i < all.length - 1 && <span style={{ opacity: 0.5 }}> + </span>}
|
|
133
|
+
* </>
|
|
134
|
+
* )}
|
|
135
|
+
* />
|
|
136
|
+
*
|
|
137
|
+
* @param {ShortcutLabelProps} props - Props for the ShortcutLabel component
|
|
138
|
+
* @param {string[]} props.keys - The list of keys to display.
|
|
139
|
+
* @param {Function} [props.renderKey] - Optional custom render function for full control over how each key appears.
|
|
140
|
+
*
|
|
141
|
+
* @returns {JSX.Element} Rendered shortcut label
|
|
142
|
+
*/
|
|
143
|
+
declare const ShortcutLabel: ({ keys, renderKey, style, ...props }: ShortcutLabelProps) => react_jsx_runtime.JSX.Element;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* React hook to manage keyboard shortcuts using a shared instance of `ShortcutManager`.
|
|
147
|
+
* Automatically cleans up shortcuts registered by the component on unmount.
|
|
148
|
+
*
|
|
149
|
+
* @param {Object} config - Configuration object.
|
|
150
|
+
* @param {boolean} [config.logs=false] - Whether to enable debug logs in the console.
|
|
151
|
+
*
|
|
152
|
+
* @returns {Object} Object containing shortcut management methods.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* const {
|
|
156
|
+
* register,
|
|
157
|
+
* setScope,
|
|
158
|
+
* getCheatSheet,
|
|
159
|
+
* } = useKeybindy({ logs: true });
|
|
160
|
+
*
|
|
161
|
+
* useEffect(() => {
|
|
162
|
+
* register(['ctrl', 's'], () => save(), {
|
|
163
|
+
* scope: 'editor',
|
|
164
|
+
* data: { description: 'Save document' }
|
|
165
|
+
* });
|
|
166
|
+
* setScope('editor');
|
|
167
|
+
* }, []);
|
|
168
|
+
*/
|
|
169
|
+
declare function useKeybindy({ logs, onShortcutFired, }?: {
|
|
170
|
+
logs?: boolean;
|
|
171
|
+
onShortcutFired?: (info: Shortcut) => void;
|
|
172
|
+
}): {
|
|
173
|
+
register: (keys: Keys[] | Keys[][], handler: ShortcutHandler, options?: ShortcutOptions) => void;
|
|
174
|
+
unregister: (keys: Keys[], scope?: string) => void;
|
|
175
|
+
enable: (keys: Keys[], scope?: string) => void;
|
|
176
|
+
disable: (keys: Keys[], scope?: string) => void;
|
|
177
|
+
toggle: (keys: Keys[], scope?: string) => void;
|
|
178
|
+
setScope: (scope: string) => void;
|
|
179
|
+
getCheatSheet: (scope?: string) => {
|
|
180
|
+
keys: string[];
|
|
181
|
+
}[] | undefined;
|
|
182
|
+
destroy: () => void;
|
|
183
|
+
getScopeInfo: (scope?: string) => {
|
|
184
|
+
shortcuts: {
|
|
185
|
+
keys: string[];
|
|
186
|
+
id: string;
|
|
187
|
+
enabled: boolean;
|
|
188
|
+
data?: Record<string, string>;
|
|
189
|
+
}[];
|
|
190
|
+
isActive?: boolean;
|
|
191
|
+
} | Record<string, {
|
|
192
|
+
shortcuts: {
|
|
193
|
+
keys: string[];
|
|
194
|
+
id: string;
|
|
195
|
+
enabled: boolean;
|
|
196
|
+
data?: Record<string, string>;
|
|
197
|
+
}[];
|
|
198
|
+
isActive?: boolean;
|
|
199
|
+
}> | undefined;
|
|
200
|
+
getActiveScope: () => string | undefined;
|
|
201
|
+
popScope: () => void;
|
|
202
|
+
pushScope: (scope: string) => void;
|
|
203
|
+
resetScope: () => void;
|
|
204
|
+
getScopes: () => string[] | undefined;
|
|
205
|
+
isScopeActive: (scope: string) => boolean | undefined;
|
|
206
|
+
onTyping: (callback: (payload: {
|
|
207
|
+
key: string;
|
|
208
|
+
event: KeyboardEvent;
|
|
209
|
+
}) => void) => void;
|
|
210
|
+
enableAll: (scope?: string) => void;
|
|
211
|
+
clear: () => void;
|
|
212
|
+
disableAll: (scope?: string) => void;
|
|
213
|
+
manager: ShortcutManager;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export { Keybindy, ShortcutLabel, useKeybindy };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import ShortcutManager from '@keybindy/core';
|
|
2
|
+
import type { Keys, Shortcut, ShortcutHandler, ShortcutOptions } from '@keybindy/core';
|
|
3
|
+
/**
|
|
4
|
+
* React hook to manage keyboard shortcuts using a shared instance of `ShortcutManager`.
|
|
5
|
+
* Automatically cleans up shortcuts registered by the component on unmount.
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} config - Configuration object.
|
|
8
|
+
* @param {boolean} [config.logs=false] - Whether to enable debug logs in the console.
|
|
9
|
+
*
|
|
10
|
+
* @returns {Object} Object containing shortcut management methods.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const {
|
|
14
|
+
* register,
|
|
15
|
+
* setScope,
|
|
16
|
+
* getCheatSheet,
|
|
17
|
+
* } = useKeybindy({ logs: true });
|
|
18
|
+
*
|
|
19
|
+
* useEffect(() => {
|
|
20
|
+
* register(['ctrl', 's'], () => save(), {
|
|
21
|
+
* scope: 'editor',
|
|
22
|
+
* data: { description: 'Save document' }
|
|
23
|
+
* });
|
|
24
|
+
* setScope('editor');
|
|
25
|
+
* }, []);
|
|
26
|
+
*/
|
|
27
|
+
export declare function useKeybindy({ logs, onShortcutFired, }?: {
|
|
28
|
+
logs?: boolean;
|
|
29
|
+
onShortcutFired?: (info: Shortcut) => void;
|
|
30
|
+
}): {
|
|
31
|
+
register: (keys: Keys[] | Keys[][], handler: ShortcutHandler, options?: ShortcutOptions) => void;
|
|
32
|
+
unregister: (keys: Keys[], scope?: string) => void;
|
|
33
|
+
enable: (keys: Keys[], scope?: string) => void;
|
|
34
|
+
disable: (keys: Keys[], scope?: string) => void;
|
|
35
|
+
toggle: (keys: Keys[], scope?: string) => void;
|
|
36
|
+
setScope: (scope: string) => void;
|
|
37
|
+
getCheatSheet: (scope?: string) => {
|
|
38
|
+
keys: string[];
|
|
39
|
+
}[] | undefined;
|
|
40
|
+
destroy: () => void;
|
|
41
|
+
getScopeInfo: (scope?: string) => {
|
|
42
|
+
shortcuts: {
|
|
43
|
+
keys: string[];
|
|
44
|
+
id: string;
|
|
45
|
+
enabled: boolean;
|
|
46
|
+
data?: Record<string, string>;
|
|
47
|
+
}[];
|
|
48
|
+
isActive?: boolean;
|
|
49
|
+
} | Record<string, {
|
|
50
|
+
shortcuts: {
|
|
51
|
+
keys: string[];
|
|
52
|
+
id: string;
|
|
53
|
+
enabled: boolean;
|
|
54
|
+
data?: Record<string, string>;
|
|
55
|
+
}[];
|
|
56
|
+
isActive?: boolean;
|
|
57
|
+
}> | undefined;
|
|
58
|
+
getActiveScope: () => string | undefined;
|
|
59
|
+
popScope: () => void;
|
|
60
|
+
pushScope: (scope: string) => void;
|
|
61
|
+
resetScope: () => void;
|
|
62
|
+
getScopes: () => string[] | undefined;
|
|
63
|
+
isScopeActive: (scope: string) => boolean | undefined;
|
|
64
|
+
onTyping: (callback: (payload: {
|
|
65
|
+
key: string;
|
|
66
|
+
event: KeyboardEvent;
|
|
67
|
+
}) => void) => void;
|
|
68
|
+
enableAll: (scope?: string) => void;
|
|
69
|
+
clear: () => void;
|
|
70
|
+
disableAll: (scope?: string) => void;
|
|
71
|
+
manager: ShortcutManager;
|
|
72
|
+
};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ShortcutManager from '@keybindy/core';
|
|
3
|
+
|
|
4
|
+
let sharedInstance = null;
|
|
5
|
+
/**
|
|
6
|
+
* React hook to manage keyboard shortcuts using a shared instance of `ShortcutManager`.
|
|
7
|
+
* Automatically cleans up shortcuts registered by the component on unmount.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} config - Configuration object.
|
|
10
|
+
* @param {boolean} [config.logs=false] - Whether to enable debug logs in the console.
|
|
11
|
+
*
|
|
12
|
+
* @returns {Object} Object containing shortcut management methods.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const {
|
|
16
|
+
* register,
|
|
17
|
+
* setScope,
|
|
18
|
+
* getCheatSheet,
|
|
19
|
+
* } = useKeybindy({ logs: true });
|
|
20
|
+
*
|
|
21
|
+
* useEffect(() => {
|
|
22
|
+
* register(['ctrl', 's'], () => save(), {
|
|
23
|
+
* scope: 'editor',
|
|
24
|
+
* data: { description: 'Save document' }
|
|
25
|
+
* });
|
|
26
|
+
* setScope('editor');
|
|
27
|
+
* }, []);
|
|
28
|
+
*/
|
|
29
|
+
function useKeybindy({ logs = false, onShortcutFired, } = {}) {
|
|
30
|
+
const managerRef = React.useRef(null);
|
|
31
|
+
const registeredIds = React.useRef(new Set());
|
|
32
|
+
if (!sharedInstance) {
|
|
33
|
+
sharedInstance = new ShortcutManager(onShortcutFired);
|
|
34
|
+
}
|
|
35
|
+
if (!managerRef.current) {
|
|
36
|
+
managerRef.current = sharedInstance;
|
|
37
|
+
managerRef.current.start();
|
|
38
|
+
}
|
|
39
|
+
const log = (...args) => {
|
|
40
|
+
if (logs)
|
|
41
|
+
console.log('[Keybindy]', ...args);
|
|
42
|
+
};
|
|
43
|
+
const warn = (...args) => {
|
|
44
|
+
if (logs)
|
|
45
|
+
console.warn('[Keybindy]', ...args);
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Registers a new keyboard shortcut.
|
|
49
|
+
*
|
|
50
|
+
* @param {Keys[] | Keys[][]} keys - Key combination(s) to listen for.
|
|
51
|
+
* @param {ShortcutHandler} handler - Callback function to invoke when shortcut is triggered.
|
|
52
|
+
* @param {ShortcutOptions} [options] - Optional configuration, including scope and metadata.
|
|
53
|
+
*/
|
|
54
|
+
const register = React.useCallback((keys, handler, options) => {
|
|
55
|
+
if (keys.length === 0) {
|
|
56
|
+
warn('No keys provided to register');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const id = options?.data?.id;
|
|
60
|
+
if (id)
|
|
61
|
+
registeredIds.current.add(id);
|
|
62
|
+
log('Registered:', id ?? keys);
|
|
63
|
+
managerRef.current?.register(keys, handler, options);
|
|
64
|
+
}, []);
|
|
65
|
+
/**
|
|
66
|
+
* Unregisters a previously registered keyboard shortcut.
|
|
67
|
+
*
|
|
68
|
+
* @param {Keys[]} keys - Key combination to unregister.
|
|
69
|
+
* @param {string} [scope] - Optional scope for more targeted unregistration.
|
|
70
|
+
*/
|
|
71
|
+
const unregister = React.useCallback((keys, scope) => {
|
|
72
|
+
if (keys.length === 0) {
|
|
73
|
+
warn('No keys provided to unregister');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
managerRef.current?.unregister(keys, scope);
|
|
77
|
+
log('Unregistered:', keys);
|
|
78
|
+
}, []);
|
|
79
|
+
/**
|
|
80
|
+
* Enables a previously disabled shortcut.
|
|
81
|
+
*
|
|
82
|
+
* @param {Keys[]} keys - Key combination to enable.
|
|
83
|
+
* @param {string} [scope] - Optional scope to target a specific set.
|
|
84
|
+
*/
|
|
85
|
+
const enable = React.useCallback((keys, scope) => {
|
|
86
|
+
if (keys.length === 0) {
|
|
87
|
+
warn('No keys provided to enable');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
managerRef.current?.enable(keys, scope);
|
|
91
|
+
log('Enabled:', keys);
|
|
92
|
+
}, []);
|
|
93
|
+
/**
|
|
94
|
+
* Disables a shortcut so it no longer triggers its handler.
|
|
95
|
+
*
|
|
96
|
+
* @param {Keys[]} keys - Key combination to disable.
|
|
97
|
+
* @param {string} [scope] - Optional scope to target a specific set.
|
|
98
|
+
*/
|
|
99
|
+
const disable = React.useCallback((keys, scope) => {
|
|
100
|
+
if (keys.length === 0) {
|
|
101
|
+
warn('No keys provided to disable');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
managerRef.current?.disable(keys, scope);
|
|
105
|
+
log('Disabled:', keys);
|
|
106
|
+
}, []);
|
|
107
|
+
/**
|
|
108
|
+
* Toggles a shortcut between enabled and disabled.
|
|
109
|
+
*
|
|
110
|
+
* @param {Keys[]} keys - Key combination to toggle.
|
|
111
|
+
* @param {string} [scope] - Optional scope to target a specific set.
|
|
112
|
+
*/
|
|
113
|
+
const toggle = React.useCallback((keys, scope) => {
|
|
114
|
+
if (keys.length === 0) {
|
|
115
|
+
warn('No keys provided to toggle');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
managerRef.current?.toggle(keys, scope);
|
|
119
|
+
log('Toggled:', keys);
|
|
120
|
+
}, []);
|
|
121
|
+
/**
|
|
122
|
+
* Returns a list of shortcuts registered in a given scope.
|
|
123
|
+
*
|
|
124
|
+
* @param {string} [scope] - The scope to query. Defaults to the active scope.
|
|
125
|
+
* @returns {Array} List of shortcut definitions.
|
|
126
|
+
*/
|
|
127
|
+
const getCheatSheet = React.useCallback((scope) => {
|
|
128
|
+
return managerRef.current?.getCheatSheet(scope);
|
|
129
|
+
}, []);
|
|
130
|
+
/**
|
|
131
|
+
* Returns the currently active scope.
|
|
132
|
+
* @returns {string} The active scope.
|
|
133
|
+
*/
|
|
134
|
+
const getActiveScope = React.useCallback(() => {
|
|
135
|
+
return managerRef.current?.getActiveScope();
|
|
136
|
+
}, []);
|
|
137
|
+
/**
|
|
138
|
+
* Disables all shortcuts in the specified scope or all scopes if no scope is provided.
|
|
139
|
+
* @param scope - The scope to disable shortcuts in.
|
|
140
|
+
*/
|
|
141
|
+
const disableAll = React.useCallback((scope) => {
|
|
142
|
+
managerRef.current?.disableAll(scope);
|
|
143
|
+
log(`Disabled all shortcuts${scope ? ` in scope "${scope}"` : ''}`);
|
|
144
|
+
}, []);
|
|
145
|
+
/**
|
|
146
|
+
* Enables all shortcuts in the specified scope or all scopes if no scope is provided.
|
|
147
|
+
* @param scope - The scope to enable shortcuts in.
|
|
148
|
+
*/
|
|
149
|
+
const enableAll = React.useCallback((scope) => {
|
|
150
|
+
managerRef.current?.enableAll(scope);
|
|
151
|
+
log(`Enabled all shortcuts${scope ? ` in scope "${scope}"` : ''}`);
|
|
152
|
+
}, []);
|
|
153
|
+
/**
|
|
154
|
+
* Sets the currently active shortcut scope.
|
|
155
|
+
*
|
|
156
|
+
* @param {string} scope - The scope name to set as active.
|
|
157
|
+
*/
|
|
158
|
+
const setScope = React.useCallback((scope) => {
|
|
159
|
+
managerRef.current?.setActiveScope(scope);
|
|
160
|
+
log('Scope set to:', scope);
|
|
161
|
+
}, []);
|
|
162
|
+
/**
|
|
163
|
+
* Resets the scope stack to the default state.
|
|
164
|
+
*/
|
|
165
|
+
const resetScope = React.useCallback(() => {
|
|
166
|
+
managerRef.current?.resetScope();
|
|
167
|
+
log('Reset scope');
|
|
168
|
+
}, []);
|
|
169
|
+
/**
|
|
170
|
+
* Returns all scopes in the stack.
|
|
171
|
+
* @returns An array of scopes.
|
|
172
|
+
*/
|
|
173
|
+
const getScopes = React.useCallback(() => {
|
|
174
|
+
return managerRef.current?.getScopes();
|
|
175
|
+
}, []);
|
|
176
|
+
/**
|
|
177
|
+
* Checks if the given scope is active.
|
|
178
|
+
* @param scope - The scope to check.
|
|
179
|
+
* @returns `true` if the scope is active, `false` otherwise.
|
|
180
|
+
*/
|
|
181
|
+
const isScopeActive = React.useCallback((scope) => {
|
|
182
|
+
return managerRef.current?.isScopeActive(scope);
|
|
183
|
+
}, []);
|
|
184
|
+
/**
|
|
185
|
+
* Registers a callback to be called when a key is typed.
|
|
186
|
+
* @param callback - The callback function to be called.
|
|
187
|
+
*/
|
|
188
|
+
const onTyping = React.useCallback((callback) => {
|
|
189
|
+
managerRef.current?.onTyping(callback);
|
|
190
|
+
}, []);
|
|
191
|
+
/**
|
|
192
|
+
* Pops the last scope from the scope stack.
|
|
193
|
+
*/
|
|
194
|
+
const popScope = React.useCallback(() => {
|
|
195
|
+
managerRef.current?.popScope();
|
|
196
|
+
log('Popped scope, active scope is:', managerRef.current?.getActiveScope());
|
|
197
|
+
}, []);
|
|
198
|
+
/**
|
|
199
|
+
* Pushes a new scope onto the scope stack.
|
|
200
|
+
* @param scope - The scope to push.
|
|
201
|
+
*/
|
|
202
|
+
const pushScope = React.useCallback((scope) => {
|
|
203
|
+
managerRef.current?.pushScope(scope);
|
|
204
|
+
log('Pushed scope:', scope);
|
|
205
|
+
}, []);
|
|
206
|
+
/**
|
|
207
|
+
* Returns internal information about the registered scopes and shortcuts.
|
|
208
|
+
*
|
|
209
|
+
* @param {string} [scope] - Optional scope to filter results.
|
|
210
|
+
* @returns {Object} Scope information.
|
|
211
|
+
*/
|
|
212
|
+
const getScopeInfo = React.useCallback((scope) => {
|
|
213
|
+
return managerRef.current?.getScopesInfo(scope);
|
|
214
|
+
}, []);
|
|
215
|
+
/**
|
|
216
|
+
* Destroys the instance of `ShortcutManager`.
|
|
217
|
+
* This should be called explicitly when you no longer need the manager.
|
|
218
|
+
*/
|
|
219
|
+
const destroy = () => {
|
|
220
|
+
managerRef.current?.destroy();
|
|
221
|
+
};
|
|
222
|
+
/**
|
|
223
|
+
* Clears the internal state, removing all pressed keys and event listeners.
|
|
224
|
+
* This does not unregister shortcuts.
|
|
225
|
+
*/
|
|
226
|
+
const clear = () => {
|
|
227
|
+
managerRef.current?.clear();
|
|
228
|
+
};
|
|
229
|
+
return {
|
|
230
|
+
register,
|
|
231
|
+
unregister,
|
|
232
|
+
enable,
|
|
233
|
+
disable,
|
|
234
|
+
toggle,
|
|
235
|
+
setScope,
|
|
236
|
+
getCheatSheet,
|
|
237
|
+
destroy,
|
|
238
|
+
getScopeInfo,
|
|
239
|
+
getActiveScope,
|
|
240
|
+
popScope,
|
|
241
|
+
pushScope,
|
|
242
|
+
resetScope,
|
|
243
|
+
getScopes,
|
|
244
|
+
isScopeActive,
|
|
245
|
+
onTyping,
|
|
246
|
+
enableAll,
|
|
247
|
+
clear,
|
|
248
|
+
disableAll,
|
|
249
|
+
manager: managerRef.current,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export { useKeybindy };
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@keybindy/react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Keybindy for React: Simple, scoped keyboard shortcuts that require little setup. designed to smoothly blend in with your React applications, allowing for robust keybinding functionality without the overhead.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "PRASSamin",
|
|
7
|
+
"url": "https://github.com/prasssamin"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/keybindy/react.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/keybindy/react",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/keybindy/react/issues"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"sideEffects": false,
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"import": "./dist/index.js",
|
|
26
|
+
"require": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist/**/*.js",
|
|
32
|
+
"dist/**/*.d.ts",
|
|
33
|
+
"!dist/**/*.map",
|
|
34
|
+
"package.json",
|
|
35
|
+
"LICENSE.md",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"keywords": [
|
|
39
|
+
"keyboard",
|
|
40
|
+
"shortcut",
|
|
41
|
+
"hotkeys",
|
|
42
|
+
"keybind",
|
|
43
|
+
"keybinding",
|
|
44
|
+
"shortcut-manager",
|
|
45
|
+
"react",
|
|
46
|
+
"shortcut-engine"
|
|
47
|
+
],
|
|
48
|
+
"scripts": {
|
|
49
|
+
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
50
|
+
"publish": "npm publish --access public",
|
|
51
|
+
"prebuild": "rm -rf dist",
|
|
52
|
+
"build": "rollup -c"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@rollup/plugin-commonjs": "^28.0.3",
|
|
56
|
+
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
57
|
+
"@rollup/plugin-typescript": "^12.1.2",
|
|
58
|
+
"@types/bun": "latest",
|
|
59
|
+
"@types/react": "^19.1.2",
|
|
60
|
+
"prettier": "^3.5.3",
|
|
61
|
+
"rollup-plugin-dts": "^6.2.1",
|
|
62
|
+
"tslib": "^2.8.1"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"typescript": "^5.0.0"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@keybindy/core": "^1.0.0",
|
|
69
|
+
"react": "^19.1.0"
|
|
70
|
+
}
|
|
71
|
+
}
|