@tanstack/react-hotkeys 0.19.0 → 0.20.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.
@@ -27,8 +27,10 @@ export interface HotkeysProviderProps<T extends AppHotkeys = AppHotkeys> extends
27
27
  */
28
28
  appHotkeys?: T;
29
29
  /**
30
- * Starting custom hotkeys, such as saved data. Read once; the provider owns
31
- * the state afterwards. Invalid entries are ignored with a warning.
30
+ * The user's saved custom hotkeys. When the value changes, such as after
31
+ * saved data loads or the user signs in, it replaces the current custom
32
+ * hotkeys. `undefined` leaves them unchanged; `null` clears them. Invalid
33
+ * entries are ignored with a warning.
32
34
  */
33
35
  customHotkeys?: CustomHotkeys<T> | null;
34
36
  /** Called with the custom hotkeys after every change, ready to save. */
@@ -60,8 +60,15 @@ function HotkeysProvider({ children, appHotkeys, customHotkeys, onCustomHotkeysC
60
60
  return scopeKey ? activateHotkeysProviderScope(scopeKey) : void 0;
61
61
  }, [scopeKey]);
62
62
  useEffect(() => {
63
- store?.setOptions({ onCustomHotkeysChange });
64
- }, [store, onCustomHotkeysChange]);
63
+ store?.setOptions({
64
+ customHotkeys,
65
+ onCustomHotkeysChange
66
+ });
67
+ }, [
68
+ store,
69
+ customHotkeys,
70
+ onCustomHotkeysChange
71
+ ]);
65
72
  useEffect(() => {
66
73
  if (appHotkeys) store?.setAppHotkeys(appHotkeys);
67
74
  }, [store, appHotkeys]);
@@ -37,7 +37,8 @@ export type UseAppHotkeyOptions<T extends AppHotkey = AppHotkey> = AdapterAppHot
37
37
  * Options merge in this order: provider defaults, app hotkey declaration,
38
38
  * call-site options.
39
39
  *
40
- * @param hotkey - A chord string, RawHotkeyChord object, sequence, or app hotkey
40
+ * @param hotkey - A chord string, RawHotkeyChord object, sequence, or app hotkey.
41
+ * `undefined`, `null`, or `''` registers nothing, for an optional shortcut.
41
42
  * @param callback - The function to call when the hotkey is pressed
42
43
  * @param options - Options for the hotkey behavior
43
44
  *
@@ -60,6 +61,6 @@ export type UseAppHotkeyOptions<T extends AppHotkey = AppHotkey> = AdapterAppHot
60
61
  * }
61
62
  * ```
62
63
  */
63
- export declare function useHotkey<T extends AppHotkey>(hotkey: T, callback: HotkeyCallback, options?: UseAppHotkeyOptions<T>): void;
64
- export declare function useHotkey<const T extends RegisterableHotkey>(hotkey: T, callback: HotkeyCallback<HotkeyCallbackContextFor<T>>, options?: UseHotkeyOptionsFor<T>): void;
64
+ export declare function useHotkey<T extends AppHotkey>(hotkey: T | null | undefined, callback: HotkeyCallback, options?: UseAppHotkeyOptions<T>): void;
65
+ export declare function useHotkey<const T extends RegisterableHotkey>(hotkey: T | null | undefined, callback: HotkeyCallback<HotkeyCallbackContextFor<T>>, options?: UseHotkeyOptionsFor<T>): void;
65
66
  //#endregion
@@ -4,10 +4,11 @@ import { AppHotkey, HeldModifierOptions, RegisterableHotkey } from "@tanstack/ho
4
4
  * Returns a boolean indicating whether held modifiers reveal this shortcut.
5
5
  * The hint shows only while the hotkey can fire: some registration of it is
6
6
  * enabled and in an active scope. An app hotkey matches its registrations by
7
- * id and uses its effective hotkey (false while unbound).
7
+ * id and uses its effective hotkey (false while unbound). No hotkey
8
+ * (`undefined`, `null`, or `''`) returns false.
8
9
  * Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact`
9
10
  * requires every modifier. A sequence reveals its hint from its first step.
10
11
  * Only visibility changes cause a rerender. This helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
11
12
  */
12
- export declare function useHotkeyHint(hotkey: RegisterableHotkey | AppHotkey, options?: HeldModifierOptions): boolean;
13
+ export declare function useHotkeyHint(hotkey: RegisterableHotkey | AppHotkey | null | undefined, options?: HeldModifierOptions): boolean;
13
14
  //#endregion
@@ -9,7 +9,8 @@ import { getHotkeyManager, getKeyStateTracker, isAppHotkey, matchesHeldModifiers
9
9
  * Returns a boolean indicating whether held modifiers reveal this shortcut.
10
10
  * The hint shows only while the hotkey can fire: some registration of it is
11
11
  * enabled and in an active scope. An app hotkey matches its registrations by
12
- * id and uses its effective hotkey (false while unbound).
12
+ * id and uses its effective hotkey (false while unbound). No hotkey
13
+ * (`undefined`, `null`, or `''`) returns false.
13
14
  * Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact`
14
15
  * requires every modifier. A sequence reveals its hint from its first step.
15
16
  * Only visibility changes cause a rerender. This helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
@@ -4,13 +4,11 @@ import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
4
4
  import { useSelector } from "./_useSelector.js";
5
5
  import { HotkeyRecorder } from "@tanstack/hotkeys";
6
6
  import { useEffect, useRef } from "react";
7
+ import { withDefaults } from "@tanstack/hotkeys/adapter";
7
8
 
8
9
  //#region src/useHotkeyRecorder.ts
9
10
  function useHotkeyRecorder(options) {
10
- const mergedOptions = {
11
- ...useDefaultHotkeysOptions().hotkeyRecorder,
12
- ...options
13
- };
11
+ const mergedOptions = withDefaults(useDefaultHotkeysOptions().hotkeyRecorder, options);
14
12
  const recorderRef = useRef(null);
15
13
  if (!recorderRef.current) recorderRef.current = new HotkeyRecorder(mergedOptions);
16
14
  recorderRef.current.setOptions(mergedOptions);
package/dist/utils.js CHANGED
@@ -6,11 +6,11 @@ function isRef(value) {
6
6
  return value !== null && typeof value === "object" && "current" in value;
7
7
  }
8
8
  /**
9
- * Resolves a hook target. A ref that is still null skips registration;
10
- * a null target means document.
9
+ * Resolves a hook target. A null target, or a ref that is still null, skips
10
+ * registration. Omit the target to listen on the document.
11
11
  */
12
12
  function resolveTarget(target) {
13
- return isRef(target) ? target.current : target ?? void 0;
13
+ return isRef(target) ? target.current : target;
14
14
  }
15
15
 
16
16
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-hotkeys",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "React adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -35,7 +35,7 @@
35
35
  "skills"
36
36
  ],
37
37
  "dependencies": {
38
- "@tanstack/hotkeys": "0.20.0"
38
+ "@tanstack/hotkeys": "0.21.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@eslint-react/eslint-plugin": "^4.2.3",
@@ -101,7 +101,7 @@ A hook uses the nearest `HotkeysProvider` above its component. Registering `Esca
101
101
 
102
102
  ## Pitfalls
103
103
 
104
- - **Pass the ref, not `ref.current`.** `target: listRef.current` is `null` on the first render, and a `null` target means `document`, so the hotkey works page-wide. Pass `target: listRef`; a ref that is still empty waits for its element.
104
+ - **Pass the ref, not `ref.current`.** `target: listRef.current` is `null` on the first render, and a `null` target registers nothing, so the hotkey doesn't work until the component renders again. Pass `target: listRef`; a ref that is still empty waits for its element.
105
105
  - **No dependency arrays or `useCallback`.** The hooks read the hotkey, callback, and options on every render, so inline callbacks always see current state.
106
106
  - **Call the hooks directly.** A `useShortcut(keys: string)` wrapper has to cast strings to hotkeys, which turns off type checking and drops sequences, options, app hotkeys, and the typed callback context.
107
107
  - **`matchesKeyboardEvent` takes the DOM event.** In a widget's `onKeyDown`, pass `event.nativeEvent`.