@tanstack/angular-hotkeys 0.18.0 → 0.19.1

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.
@@ -33,10 +33,12 @@ export interface ProvideHotkeysOptions {
33
33
  */
34
34
  appHotkeys?: AppHotkeys;
35
35
  /**
36
- * Starting custom hotkeys, such as saved data. Read once; the provider owns
37
- * the state afterwards. Invalid entries are ignored with a warning.
36
+ * The user's saved custom hotkeys. When the value changes, such as after
37
+ * saved data loads or the user signs in, it replaces the current custom
38
+ * hotkeys. `undefined` leaves them unchanged; `null` clears them. Invalid
39
+ * entries are ignored with a warning.
38
40
  */
39
- customHotkeys?: CustomHotkeys | null;
41
+ customHotkeys?: MaybeGetter<CustomHotkeys | null | undefined>;
40
42
  /** Called with the custom hotkeys after every change, ready to save. */
41
43
  onCustomHotkeysChange?: (customHotkeys: CustomHotkeys) => void;
42
44
  }
@@ -1,5 +1,5 @@
1
1
  import { publishAppHotkeysStore } from "@tanstack/hotkeys";
2
- import { DestroyRef, InjectionToken, Injector, computed, effect, inject, runInInjectionContext, signal } from "@angular/core";
2
+ import { DestroyRef, InjectionToken, Injector, computed, effect, inject, runInInjectionContext, signal, untracked } from "@angular/core";
3
3
  import { activateHotkeysProviderScope, createAppHotkeysStore, getHotkeysProviderScopeKey, mergeHotkeysProviderDefaults } from "@tanstack/hotkeys/adapter";
4
4
 
5
5
  //#region src/hotkeys-provider.ts
@@ -27,10 +27,15 @@ function createHotkeysContext(options) {
27
27
  let store = null;
28
28
  let appHotkeysState = parent.appHotkeysState;
29
29
  if (appHotkeys) {
30
- store = createAppHotkeysStore(appHotkeys, {
31
- customHotkeys,
30
+ const owned = createAppHotkeysStore(appHotkeys, {
31
+ customHotkeys: read(customHotkeys),
32
32
  onCustomHotkeysChange
33
33
  });
34
+ store = owned;
35
+ if (typeof customHotkeys === "function") effect(() => {
36
+ const next = read(customHotkeys);
37
+ untracked(() => owned.setOptions({ customHotkeys: next }));
38
+ });
34
39
  const state = signal(store.store.state);
35
40
  const subscription = store.store.subscribe(() => state.set(store.store.state));
36
41
  destroyRef.onDestroy(() => subscription.unsubscribe());
@@ -28,7 +28,9 @@ type MaybeGetter<T> = T | (() => T);
28
28
  * Chords accept `requireReset`; sequences accept `timeout`. A one-step
29
29
  * sequence registers as its chord. An app hotkey uses its effective hotkey
30
30
  * from `provideHotkeys` (including the user's custom hotkey) and its declared
31
- * meta and options, and registers nothing while unbound.
31
+ * meta and options, and registers nothing while unbound. A hotkey of
32
+ * `undefined`, `null`, or `''` registers nothing, for an optional shortcut
33
+ * such as `() => this.shortcut()`; when it changes to a hotkey, it registers.
32
34
  *
33
35
  * @example
34
36
  * ```ts
@@ -42,6 +44,6 @@ type MaybeGetter<T> = T | (() => T);
42
44
  * }
43
45
  * ```
44
46
  */
45
- export declare function injectHotkey<T extends AppHotkey>(hotkey: MaybeGetter<T>, callback: HotkeyCallback, options?: MaybeGetter<InjectAppHotkeyOptions<T>>): void;
46
- export declare function injectHotkey<const T extends RegisterableHotkey>(hotkey: MaybeGetter<T>, callback: HotkeyCallback<HotkeyCallbackContextFor<T>>, options?: MaybeGetter<InjectHotkeyOptionsFor<T>>): void;
47
+ export declare function injectHotkey<T extends AppHotkey>(hotkey: MaybeGetter<T | null | undefined>, callback: HotkeyCallback, options?: MaybeGetter<InjectAppHotkeyOptions<T>>): void;
48
+ export declare function injectHotkey<const T extends RegisterableHotkey>(hotkey: MaybeGetter<T | null | undefined>, callback: HotkeyCallback<HotkeyCallbackContextFor<T>>, options?: MaybeGetter<InjectHotkeyOptionsFor<T>>): void;
47
49
  //#endregion
@@ -7,7 +7,8 @@ import { Signal } from "@angular/core";
7
7
  * enabled and in an active scope. Pass getters to track changing hotkeys and
8
8
  * options. A sequence reveals its hint from its first step, and an app hotkey
9
9
  * matches its registrations by id and uses its effective hotkey (false while
10
- * unbound). This helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
10
+ * unbound). No hotkey (`undefined`, `null`, or `''`) returns false. This
11
+ * helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
11
12
  */
12
- export declare function injectHotkeyHint(hotkey: RegisterableHotkey | AppHotkey | (() => RegisterableHotkey | AppHotkey), options?: HeldModifierOptions | (() => HeldModifierOptions)): Signal<boolean>;
13
+ export declare function injectHotkeyHint(hotkey: RegisterableHotkey | AppHotkey | null | undefined | (() => RegisterableHotkey | AppHotkey | null | undefined), options?: HeldModifierOptions | (() => HeldModifierOptions)): Signal<boolean>;
13
14
  //#endregion
@@ -10,7 +10,8 @@ import { computed } from "@angular/core";
10
10
  * enabled and in an active scope. Pass getters to track changing hotkeys and
11
11
  * options. A sequence reveals its hint from its first step, and an app hotkey
12
12
  * matches its registrations by id and uses its effective hotkey (false while
13
- * unbound). This helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
13
+ * unbound). No hotkey (`undefined`, `null`, or `''`) returns false. This
14
+ * helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
14
15
  */
15
16
  function injectHotkeyHint(hotkey, options = {}) {
16
17
  const context = injectHotkeysContext();
@@ -1,6 +1,7 @@
1
1
  import { injectDefaultHotkeysOptions } from "./hotkeys-provider.js";
2
2
  import { HotkeyRecorder } from "@tanstack/hotkeys";
3
3
  import { DestroyRef, computed, effect, inject, linkedSignal, untracked } from "@angular/core";
4
+ import { withDefaults } from "@tanstack/hotkeys/adapter";
4
5
 
5
6
  //#region src/injectHotkeyRecorder.ts
6
7
  function injectHotkeyRecorder(options) {
@@ -8,10 +9,7 @@ function injectHotkeyRecorder(options) {
8
9
  const destroyRef = inject(DestroyRef);
9
10
  const recorderSignal = computed(() => untracked(() => {
10
11
  const resolvedOptions = typeof options === "function" ? options() : options;
11
- return new HotkeyRecorder({
12
- ...defaultOptions.hotkeyRecorder,
13
- ...resolvedOptions
14
- });
12
+ return new HotkeyRecorder(withDefaults(defaultOptions.hotkeyRecorder, resolvedOptions));
15
13
  }));
16
14
  const recorderStore = computed(() => untracked(() => recorderSignal().store));
17
15
  const isRecording = injectLazyStore(recorderStore, (state) => state.isRecording);
@@ -19,10 +17,7 @@ function injectHotkeyRecorder(options) {
19
17
  const recordedHotkey = injectLazyStore(recorderStore, (state) => state.recordedHotkey);
20
18
  effect(() => {
21
19
  const resolved = typeof options === "function" ? options() : options;
22
- recorderSignal().setOptions({
23
- ...defaultOptions.hotkeyRecorder,
24
- ...resolved
25
- });
20
+ recorderSignal().setOptions(withDefaults(defaultOptions.hotkeyRecorder, resolved));
26
21
  });
27
22
  destroyRef.onDestroy(() => {
28
23
  recorderSignal().destroy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/angular-hotkeys",
3
- "version": "0.18.0",
3
+ "version": "0.19.1",
4
4
  "description": "Angular adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  "skills"
38
38
  ],
39
39
  "dependencies": {
40
- "@tanstack/hotkeys": "0.20.0"
40
+ "@tanstack/hotkeys": "0.22.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@angular/common": ">=19.0.0",