@keybindy/react 1.1.3 → 1.1.5

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/README.md CHANGED
@@ -109,27 +109,27 @@ A powerful hook that gives you full control over the shortcut system via the Sho
109
109
 
110
110
  ##### Available methods
111
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 |
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
133
 
134
134
  > _All methods mirror `@keybindy/core` with a React-friendly API._
135
135
 
package/dist/Keybindy.js CHANGED
@@ -2,59 +2,43 @@ import { jsx, Fragment } from 'react/jsx-runtime';
2
2
  import React from 'react';
3
3
  import { useKeybindy } from './useKeybindy.js';
4
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 = 'global', shortcuts = [], children, disabled, onShortcutFired, logs = false, }) => {
5
+ const KeybindyComponent = ({ scope = 'global', shortcuts = [], children, disabled, onShortcutFired, logs = false, }) => {
39
6
  const { register, unregister, manager, pushScope, popScope, getScopes, setScope } = useKeybindy({
40
7
  onShortcutFired,
41
8
  logs,
42
9
  });
43
- const prevScope = React.useRef(null);
10
+ // Memoize a stable representation of shortcuts, excluding the handler.
11
+ // This prevents the effect from re-running unnecessarily.
12
+ const stableShortcuts = React.useMemo(() => {
13
+ return shortcuts.map(({ keys, options }) => ({ keys, options }));
14
+ }, [JSON.stringify(shortcuts.map(s => ({ keys: s.keys, options: s.options })))]);
15
+ // Use a ref to store the latest handlers, preventing re-renders from causing issues.
16
+ const handlersRef = React.useRef({});
17
+ React.useEffect(() => {
18
+ handlersRef.current = shortcuts.reduce((acc, { keys, handler }) => {
19
+ const key = JSON.stringify(keys);
20
+ acc[key] = handler;
21
+ return acc;
22
+ }, {});
23
+ });
44
24
  React.useEffect(() => {
45
25
  if (!manager) {
46
26
  return;
47
27
  }
48
- prevScope.current = manager.getActiveScope() ?? 'global';
49
- // Add scope if doesn't exist
50
28
  if (!getScopes()?.includes(scope)) {
51
29
  pushScope(scope);
52
30
  }
53
- // Set this scope as active
54
31
  setScope(scope);
55
- // Register all shortcuts for this scope
56
- shortcuts.forEach(({ keys, handler, options }) => {
57
- register(keys, handler, { ...options, scope });
32
+ // Register shortcuts using the stable definitions.
33
+ stableShortcuts.forEach(({ keys, options }) => {
34
+ const stableHandler = (event, state) => {
35
+ const key = JSON.stringify(keys);
36
+ const currentHandler = handlersRef.current[key];
37
+ if (currentHandler) {
38
+ currentHandler(event, state);
39
+ }
40
+ };
41
+ register(keys, stableHandler, { ...options, scope });
58
42
  });
59
43
  if (disabled) {
60
44
  manager.disableAll(scope);
@@ -63,7 +47,8 @@ const Keybindy = ({ scope = 'global', shortcuts = [], children, disabled, onShor
63
47
  manager.enableAll(scope);
64
48
  }
65
49
  return () => {
66
- shortcuts.forEach(({ keys }) => {
50
+ // Unregister using the same stable definitions.
51
+ stableShortcuts.forEach(({ keys }) => {
67
52
  if (Array.isArray(keys[0])) {
68
53
  keys.forEach(key => unregister(key, scope));
69
54
  }
@@ -73,8 +58,9 @@ const Keybindy = ({ scope = 'global', shortcuts = [], children, disabled, onShor
73
58
  });
74
59
  popScope();
75
60
  };
76
- }, [scope, shortcuts, manager, disabled]);
61
+ }, [scope, manager, disabled, stableShortcuts]);
77
62
  return jsx(Fragment, { children: children });
78
63
  };
64
+ const Keybindy = React.memo(KeybindyComponent);
79
65
 
80
66
  export { Keybindy };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import React from 'react';
2
- import ShortcutManager, { Keys, ShortcutHandler, HoldShortcutHandler, ShortcutOptions, Shortcut } from '@keybindy/core';
3
- export { KeyBinding, Keys, Shortcut, ShortcutHandler, ShortcutOptions } from '@keybindy/core';
2
+ import ShortcutManager, { Keys, ShortcutHandler, ShortcutOptions, Shortcut } from '@keybindy/core';
3
+ export { Keys, Shortcut, ShortcutOptions } from '@keybindy/core';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
6
  /**
7
- * Represents a keyboard shortcut definition.
7
+ * Represents a shortcut definition for the `<Keybindy />` component.
8
8
  */
9
- type ShortcutDefinition = {
9
+ type KeybindyShortcut = {
10
10
  /**
11
11
  * The key combination(s) to listen for.
12
12
  * Can be a single array of keys or an array of key combinations.
@@ -15,12 +15,13 @@ type ShortcutDefinition = {
15
15
  /**
16
16
  * Callback function to invoke when the shortcut is triggered.
17
17
  */
18
- handler: ShortcutHandler | HoldShortcutHandler;
18
+ handler: ShortcutHandler;
19
19
  /**
20
20
  * Optional configuration, including scope and other metadata.
21
21
  */
22
22
  options?: Omit<ShortcutOptions, 'scope'>;
23
23
  };
24
+
24
25
  /**
25
26
  * Props for the `<Keybindy />` component.
26
27
  */
@@ -33,7 +34,7 @@ type KeybindyProps = {
33
34
  /**
34
35
  * Array of shortcut definitions to register for this scope.
35
36
  */
36
- shortcuts?: ShortcutDefinition[];
37
+ shortcuts?: KeybindyShortcut[];
37
38
  /**
38
39
  * Whether the shortcuts should be disabled for this scope.
39
40
  * Defaults to `false`.
@@ -53,40 +54,7 @@ type KeybindyProps = {
53
54
  */
54
55
  children?: React.ReactNode;
55
56
  };
56
- /**
57
- * `<Keybindy />` is a React component that registers keyboard shortcuts within a given scope. It allows
58
- * users to define custom shortcuts and their associated handlers, while managing scope-based shortcut behavior.
59
- * The component listens for keyboard events and triggers the registered handler when the corresponding keys are pressed.
60
- * It also provides an optional callback (`onShortcutFired`) to notify users when a shortcut is triggered.
61
- *
62
- * @component
63
- *
64
- * @example
65
- * // Basic usage
66
- * <Keybindy scope="global" shortcuts={[{ keys: ['ctrl', 's'], handler: saveDocument }]} >
67
- * <div>Content with shortcuts</div>
68
- * </Keybindy>
69
- *
70
- * @example
71
- * // With custom callback for onShortcutFired
72
- * <Keybindy
73
- * scope="editor"
74
- * shortcuts={[{ keys: ['ctrl', 'e'], handler: editDocument }]}
75
- * onShortcutFired={(info) => console.log('Shortcut fired:', info)}
76
- * >
77
- * <div>Editor with shortcuts</div>
78
- * </Keybindy>
79
- *
80
- * @param {ShortcutProps} props - Props for the Shortcut component.
81
- * @param {string} props.scope - The scope under which the shortcuts should be active.
82
- * @param {ShortcutDefinition[]} [props.shortcuts] - An array of shortcut definitions, each containing keys, handler, and options.
83
- * @param {boolean} [props.disabled=false] - Whether the shortcuts should be disabled for this scope.
84
- * @param {(info: Shortcut) => void} [props.onShortcutFired] - Optional callback triggered when a shortcut is fired, providing the shortcut info.
85
- * @param {React.ReactNode} props.children - The children to be rendered inside the component, which can contain any JSX elements.
86
- *
87
- * @returns {JSX.Element} The rendered component with registered shortcuts within the provided scope.
88
- */
89
- declare const Keybindy: React.FC<KeybindyProps>;
57
+ declare const Keybindy: React.NamedExoticComponent<KeybindyProps>;
90
58
 
91
59
  interface ShortcutLabelProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> {
92
60
  /**
@@ -144,7 +112,7 @@ interface ShortcutLabelProps extends React.DetailedHTMLProps<React.HTMLAttribute
144
112
  declare const ShortcutLabel: ({ keys, renderKey, style, ...props }: ShortcutLabelProps) => react_jsx_runtime.JSX.Element;
145
113
 
146
114
  type UseKeybindyReturn = {
147
- register: (keys: Keys[] | Keys[][], handler: ShortcutHandler | HoldShortcutHandler, options?: ShortcutOptions) => void;
115
+ register: (keys: Keys[] | Keys[][], handler: ShortcutHandler, options?: ShortcutOptions) => void;
148
116
  unregister: (keys: Keys[], scope?: string) => void;
149
117
  enable: (keys: Keys[], scope?: string) => void;
150
118
  disable: (keys: Keys[], scope?: string) => void;
@@ -186,3 +154,4 @@ declare const useKeybindy: ({ logs, onShortcutFired, }?: {
186
154
  }) => UseKeybindyReturn;
187
155
 
188
156
  export { Keybindy, ShortcutLabel, useKeybindy };
157
+ export type { KeybindyShortcut };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keybindy/react",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
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
5
  "author": {
6
6
  "name": "PRASSamin",
@@ -17,13 +17,13 @@
17
17
  "license": "MIT",
18
18
  "sideEffects": false,
19
19
  "type": "module",
20
- "main": "dist/index.cjs",
21
- "module": "dist/index.mjs",
20
+ "main": "dist/index.js",
21
+ "module": "dist/index.js",
22
22
  "types": "dist/index.d.ts",
23
23
  "exports": {
24
24
  ".": {
25
- "require": "./dist/index.cjs",
26
- "import": "./dist/index.mjs"
25
+ "require": "./dist/index.js",
26
+ "import": "./dist/index.js"
27
27
  }
28
28
  },
29
29
  "files": [
@@ -51,7 +51,7 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "react": "^19.1.0",
54
- "@keybindy/core": "1.1.3"
54
+ "@keybindy/core": "1.1.4"
55
55
  },
56
56
  "scripts": {
57
57
  "format": "prettier --write \"src/**/*.{ts,tsx}\"",