@tanstack/angular-hotkeys 0.12.1 → 0.14.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.
@@ -3,49 +3,19 @@ import { HotkeyRecorder } from "@tanstack/hotkeys";
3
3
  import { DestroyRef, computed, effect, inject, linkedSignal, untracked } from "@angular/core";
4
4
 
5
5
  //#region src/injectHotkeyRecorder.ts
6
- /**
7
- * Angular inject-based API for recording keyboard shortcuts.
8
- *
9
- * Thin wrapper around the framework-agnostic HotkeyRecorder class: captures
10
- * keyboard events, converts them to hotkey strings, and handles Escape to
11
- * cancel or Backspace/Delete to clear.
12
- *
13
- * @param options - Configuration options for the recorder (or getter)
14
- * @returns Object with recording state signals and control functions
15
- *
16
- * @example
17
- * ```ts
18
- * @Component({ ... })
19
- * export class ShortcutSettingsComponent {
20
- * shortcut = signal<Hotkey>('Mod+S')
21
- * recorder = injectHotkeyRecorder({
22
- * onRecord: (hotkey) => this.shortcut.set(hotkey),
23
- * onCancel: () => console.log('Recording cancelled'),
24
- * })
25
- *
26
- * constructor() {
27
- * injectHotkey(
28
- * () => this.shortcut(),
29
- * () => this.handleSave(),
30
- * () => ({ enabled: !this.recorder.isRecording() }),
31
- * )
32
- * }
33
- * }
34
- * ```
35
- */
36
6
  function injectHotkeyRecorder(options) {
37
7
  const defaultOptions = injectDefaultHotkeysOptions();
38
8
  const destroyRef = inject(DestroyRef);
39
9
  const recorderSignal = computed(() => untracked(() => {
40
10
  const resolvedOptions = typeof options === "function" ? options() : options;
41
- const mergedOptions = {
11
+ return new HotkeyRecorder({
42
12
  ...defaultOptions.hotkeyRecorder,
43
13
  ...resolvedOptions
44
- };
45
- return new HotkeyRecorder(mergedOptions);
14
+ });
46
15
  }));
47
16
  const recorderStore = computed(() => untracked(() => recorderSignal().store));
48
17
  const isRecording = injectLazyStore(recorderStore, (state) => state.isRecording);
18
+ const steps = injectLazyStore(recorderStore, (state) => state.steps);
49
19
  const recordedHotkey = injectLazyStore(recorderStore, (state) => state.recordedHotkey);
50
20
  effect(() => {
51
21
  const resolved = typeof options === "function" ? options() : options;
@@ -59,10 +29,12 @@ function injectHotkeyRecorder(options) {
59
29
  });
60
30
  return {
61
31
  isRecording,
32
+ steps,
62
33
  recordedHotkey,
63
34
  startRecording: () => recorderSignal().start(),
64
35
  stopRecording: () => recorderSignal().stop(),
65
- cancelRecording: () => recorderSignal().cancel()
36
+ cancelRecording: () => recorderSignal().cancel(),
37
+ commitRecording: () => recorderSignal().commit()
66
38
  };
67
39
  }
68
40
  function injectLazyStore(storeSignal, selector) {
@@ -1,26 +1,28 @@
1
- import { HotkeyRegistrationView, SequenceRegistrationView } from "@tanstack/hotkeys";
1
+ import { HotkeyRegistrationView } from "@tanstack/hotkeys";
2
2
  import { Signal } from "@angular/core";
3
3
  //#region src/injectHotkeyRegistrations.d.ts
4
4
  /**
5
- * Return type for injectHotkeyRegistrations.
6
- */
7
- export interface HotkeyRegistrationsResult {
8
- /** Signal for all registered hotkeys (public view, no callbacks) */
9
- hotkeys: Signal<Array<HotkeyRegistrationView>>;
10
- /** Signal for all registered sequences */
11
- sequences: Signal<Array<SequenceRegistrationView>>;
12
- }
13
- /**
14
- * Angular injectable that reactively reads all hotkey and sequence registrations
15
- * from the singleton managers.
5
+ * Angular inject-based API that reactively reads every hotkey registration,
6
+ * chords and sequences, from the singleton HotkeyManager. Narrow each view
7
+ * with `kind`.
16
8
  *
17
- * This is a standalone injectable that does NOT require the HotkeysProvider.
18
- * It subscribes to both HotkeyManager and SequenceManager stores and
19
- * triggers change detection when registrations change.
9
+ * This does NOT require `provideHotkeys`.
20
10
  *
21
- * Must be called in an injection context (constructor, factory, or `inject()`).
11
+ * @returns A signal of all registrations, without callbacks
22
12
  *
23
- * @returns Object with `hotkeys` and `sequences` signals
13
+ * @example
14
+ * ```ts
15
+ * @Component({
16
+ * template: `
17
+ * @for (reg of registrations(); track reg.id) {
18
+ * <li>{{ reg.options.meta?.name }}</li>
19
+ * }
20
+ * `,
21
+ * })
22
+ * export class ShortcutPalette {
23
+ * registrations = injectHotkeyRegistrations()
24
+ * }
25
+ * ```
24
26
  */
25
- export declare function injectHotkeyRegistrations(): HotkeyRegistrationsResult;
27
+ export declare function injectHotkeyRegistrations(): Signal<Array<HotkeyRegistrationView>>;
26
28
  //#endregion
@@ -1,26 +1,32 @@
1
- import { getHotkeyManager, getSequenceManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
1
+ import { getHotkeyManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
2
2
  import { injectSelector } from "@tanstack/angular-store";
3
3
 
4
4
  //#region src/injectHotkeyRegistrations.ts
5
5
  /**
6
- * Angular injectable that reactively reads all hotkey and sequence registrations
7
- * from the singleton managers.
6
+ * Angular inject-based API that reactively reads every hotkey registration,
7
+ * chords and sequences, from the singleton HotkeyManager. Narrow each view
8
+ * with `kind`.
8
9
  *
9
- * This is a standalone injectable that does NOT require the HotkeysProvider.
10
- * It subscribes to both HotkeyManager and SequenceManager stores and
11
- * triggers change detection when registrations change.
10
+ * This does NOT require `provideHotkeys`.
12
11
  *
13
- * Must be called in an injection context (constructor, factory, or `inject()`).
12
+ * @returns A signal of all registrations, without callbacks
14
13
  *
15
- * @returns Object with `hotkeys` and `sequences` signals
14
+ * @example
15
+ * ```ts
16
+ * @Component({
17
+ * template: `
18
+ * @for (reg of registrations(); track reg.id) {
19
+ * <li>{{ reg.options.meta?.name }}</li>
20
+ * }
21
+ * `,
22
+ * })
23
+ * export class ShortcutPalette {
24
+ * registrations = injectHotkeyRegistrations()
25
+ * }
26
+ * ```
16
27
  */
17
28
  function injectHotkeyRegistrations() {
18
- const hotkeyManager = getHotkeyManager();
19
- const sequenceManager = getSequenceManager();
20
- return {
21
- hotkeys: injectSelector(hotkeyManager.registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView)),
22
- sequences: injectSelector(sequenceManager.registrations, (state) => Array.from(state.values()))
23
- };
29
+ return injectSelector(getHotkeyManager().registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView));
24
30
  }
25
31
 
26
32
  //#endregion
@@ -1,63 +1,27 @@
1
- import { InjectHotkeyOptions } from "./injectHotkey.js";
2
- import { HotkeyCallback, RegisterableHotkey } from "@tanstack/hotkeys";
1
+ import { InjectHotkeyCommonOptions, InjectHotkeyTarget } from "./injectHotkey.js";
2
+ import { AdapterHotkeyDefinition } from "@tanstack/hotkeys/adapter";
3
3
  //#region src/injectHotkeys.d.ts
4
4
  /**
5
- * A single hotkey definition for use with `injectHotkeys`.
5
+ * One hotkey for `injectHotkeys`. Chords accept `requireReset`; sequences
6
+ * accept `timeout`; hotkeys of unknown kind accept only shared options.
6
7
  */
7
- export interface InjectHotkeyDefinition {
8
- /** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */
9
- hotkey: RegisterableHotkey | (() => RegisterableHotkey);
10
- /** The function to call when the hotkey is pressed */
11
- callback: HotkeyCallback;
12
- /** Per-hotkey options (merged on top of commonOptions) */
13
- options?: InjectHotkeyOptions | (() => InjectHotkeyOptions);
14
- }
8
+ export type InjectHotkeyDefinition = AdapterHotkeyDefinition<InjectHotkeyTarget>;
15
9
  /**
16
- * Angular inject-based API for registering multiple keyboard hotkeys at once.
10
+ * Angular inject-based API for registering several hotkeys at once: chords,
11
+ * sequences, and app hotkeys. Pass a getter for a reactive list.
17
12
  *
18
- * Uses the singleton HotkeyManager for efficient event handling.
19
- * Accepts a dynamic array of hotkey definitions.
20
- *
21
- * Call in an injection context (e.g. constructor or field initializer).
22
- * Uses effect() to track reactive dependencies and update registrations
23
- * when options or the callback change.
24
- *
25
- * Options are merged in this order:
26
- * provideHotkeys defaults < commonOptions < per-definition options
27
- *
28
- * @param hotkeys - Array of hotkey definitions, or getter returning them
29
- * @param commonOptions - Shared options for all hotkeys, or getter
30
- *
31
- * @example
32
- * ```ts
33
- * @Component({ ... })
34
- * export class EditorComponent {
35
- * constructor() {
36
- * injectHotkeys([
37
- * { hotkey: 'Mod+S', callback: () => this.save() },
38
- * { hotkey: 'Mod+Z', callback: () => this.undo() },
39
- * { hotkey: 'Escape', callback: () => this.close() },
40
- * ])
41
- * }
42
- * }
43
- * ```
13
+ * Options merge in this order: provider defaults, app hotkey declaration,
14
+ * commonOptions, per-hotkey options. Each option applies only to the kinds
15
+ * that support it. Unbound app hotkeys and empty sequences register nothing.
16
+ * A `null` target skips registration until it is set.
44
17
  *
45
18
  * @example
46
19
  * ```ts
47
- * @Component({ ... })
48
- * export class DynamicShortcuts {
49
- * shortcuts = signal([...])
50
- *
51
- * constructor() {
52
- * injectHotkeys(
53
- * () => this.shortcuts().map((s) => ({
54
- * hotkey: s.key,
55
- * callback: s.action,
56
- * })),
57
- * )
58
- * }
59
- * }
20
+ * injectHotkeys([
21
+ * { hotkey: 'Mod+S', callback: () => this.save() },
22
+ * { hotkey: ['G', 'G'], callback: () => this.scrollToTop(), options: { timeout: 500 } },
23
+ * ])
60
24
  * ```
61
25
  */
62
- export declare function injectHotkeys(hotkeys: Array<InjectHotkeyDefinition> | (() => Array<InjectHotkeyDefinition>), commonOptions?: InjectHotkeyOptions | (() => InjectHotkeyOptions)): void;
26
+ export declare function injectHotkeys(hotkeys: ReadonlyArray<InjectHotkeyDefinition> | (() => ReadonlyArray<InjectHotkeyDefinition>), commonOptions?: InjectHotkeyCommonOptions | (() => InjectHotkeyCommonOptions)): void;
63
27
  //#endregion
@@ -1,118 +1,38 @@
1
- import { injectDefaultHotkeysOptions } from "./hotkeys-provider.js";
2
- import { detectPlatform, getHotkeyManager, normalizeRegisterableHotkey } from "@tanstack/hotkeys";
3
- import { DestroyRef, effect, inject } from "@angular/core";
1
+ import { injectHotkeysContext } from "./hotkeys-provider.js";
2
+ import { DestroyRef, effect, inject, untracked } from "@angular/core";
3
+ import { createHotkeyBindings, prepareHotkeyDefinitions } from "@tanstack/hotkeys/adapter";
4
4
 
5
5
  //#region src/injectHotkeys.ts
6
6
  /**
7
- * Angular inject-based API for registering multiple keyboard hotkeys at once.
7
+ * Angular inject-based API for registering several hotkeys at once: chords,
8
+ * sequences, and app hotkeys. Pass a getter for a reactive list.
8
9
  *
9
- * Uses the singleton HotkeyManager for efficient event handling.
10
- * Accepts a dynamic array of hotkey definitions.
11
- *
12
- * Call in an injection context (e.g. constructor or field initializer).
13
- * Uses effect() to track reactive dependencies and update registrations
14
- * when options or the callback change.
15
- *
16
- * Options are merged in this order:
17
- * provideHotkeys defaults < commonOptions < per-definition options
18
- *
19
- * @param hotkeys - Array of hotkey definitions, or getter returning them
20
- * @param commonOptions - Shared options for all hotkeys, or getter
10
+ * Options merge in this order: provider defaults, app hotkey declaration,
11
+ * commonOptions, per-hotkey options. Each option applies only to the kinds
12
+ * that support it. Unbound app hotkeys and empty sequences register nothing.
13
+ * A `null` target skips registration until it is set.
21
14
  *
22
15
  * @example
23
16
  * ```ts
24
- * @Component({ ... })
25
- * export class EditorComponent {
26
- * constructor() {
27
- * injectHotkeys([
28
- * { hotkey: 'Mod+S', callback: () => this.save() },
29
- * { hotkey: 'Mod+Z', callback: () => this.undo() },
30
- * { hotkey: 'Escape', callback: () => this.close() },
31
- * ])
32
- * }
33
- * }
34
- * ```
35
- *
36
- * @example
37
- * ```ts
38
- * @Component({ ... })
39
- * export class DynamicShortcuts {
40
- * shortcuts = signal([...])
41
- *
42
- * constructor() {
43
- * injectHotkeys(
44
- * () => this.shortcuts().map((s) => ({
45
- * hotkey: s.key,
46
- * callback: s.action,
47
- * })),
48
- * )
49
- * }
50
- * }
17
+ * injectHotkeys([
18
+ * { hotkey: 'Mod+S', callback: () => this.save() },
19
+ * { hotkey: ['G', 'G'], callback: () => this.scrollToTop(), options: { timeout: 500 } },
20
+ * ])
51
21
  * ```
52
22
  */
53
23
  function injectHotkeys(hotkeys, commonOptions = {}) {
54
- const defaultOptions = injectDefaultHotkeysOptions();
55
- const manager = getHotkeyManager();
56
- const destroyRef = inject(DestroyRef);
57
- const registrations = /* @__PURE__ */ new Map();
58
- destroyRef.onDestroy(() => {
59
- for (const { handle } of registrations.values()) if (handle.isActive) handle.unregister();
60
- registrations.clear();
61
- });
24
+ const context = injectHotkeysContext();
25
+ const bindings = createHotkeyBindings();
26
+ inject(DestroyRef).onDestroy(() => bindings.destroy());
62
27
  effect(() => {
63
- const resolvedHotkeys = typeof hotkeys === "function" ? hotkeys() : hotkeys;
64
- const resolvedCommonOptions = typeof commonOptions === "function" ? commonOptions() : commonOptions;
65
- const nextKeys = /* @__PURE__ */ new Set();
66
- const prepared = [];
67
- for (let i = 0; i < resolvedHotkeys.length; i++) {
68
- const def = resolvedHotkeys[i];
69
- const resolvedHotkey = typeof def.hotkey === "function" ? def.hotkey() : def.hotkey;
70
- const resolvedDefOptions = typeof def.options === "function" ? def.options() : def.options ?? {};
71
- const mergedOptions = {
72
- ...defaultOptions.hotkey,
73
- ...resolvedCommonOptions,
74
- ...resolvedDefOptions
75
- };
76
- const platform = mergedOptions.platform ?? detectPlatform();
77
- const hotkeyString = normalizeRegisterableHotkey(resolvedHotkey, platform);
78
- const resolvedTarget = "target" in mergedOptions ? mergedOptions.target ?? null : typeof document !== "undefined" ? document : null;
79
- if (!resolvedTarget) continue;
80
- const registrationKey = `${i}:${hotkeyString}`;
81
- nextKeys.add(registrationKey);
82
- prepared.push({
83
- registrationKey,
84
- def,
85
- mergedOptions,
86
- hotkeyString,
87
- resolvedTarget
88
- });
89
- }
90
- for (const [key, record] of [...registrations.entries()]) if (!nextKeys.has(key)) {
91
- if (record.handle.isActive) record.handle.unregister();
92
- registrations.delete(key);
93
- }
94
- for (const p of prepared) {
95
- const existing = registrations.get(p.registrationKey);
96
- if (existing?.handle.isActive && existing.target === p.resolvedTarget) {
97
- existing.handle.callback = p.def.callback;
98
- const { target: _target, ...optionsWithoutTarget } = p.mergedOptions;
99
- existing.handle.setOptions(optionsWithoutTarget);
100
- continue;
101
- }
102
- if (existing) {
103
- if (existing.handle.isActive) existing.handle.unregister();
104
- registrations.delete(p.registrationKey);
105
- }
106
- const { target: _target, ...optionsWithoutTarget } = p.mergedOptions;
107
- const handle = manager.register(p.hotkeyString, p.def.callback, {
108
- ...optionsWithoutTarget,
109
- target: p.resolvedTarget
110
- });
111
- registrations.set(p.registrationKey, {
112
- handle,
113
- target: p.resolvedTarget
114
- });
115
- }
28
+ context.appHotkeysState();
29
+ const definitions = prepareHotkeyDefinitions(typeof hotkeys === "function" ? hotkeys() : hotkeys, {
30
+ appHotkeysStore: context.appHotkeysStore,
31
+ defaults: context.defaultOptions.hotkey,
32
+ commonOptions: typeof commonOptions === "function" ? commonOptions() : commonOptions,
33
+ resolveTarget: (target) => target
34
+ });
35
+ untracked(() => bindings.update(definitions));
116
36
  });
117
37
  }
118
38
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/angular-hotkeys",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "description": "Angular adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -35,17 +35,17 @@
35
35
  "dist"
36
36
  ],
37
37
  "dependencies": {
38
- "@tanstack/angular-store": "^0.11.1",
39
- "@tanstack/hotkeys": "0.10.1"
38
+ "@tanstack/angular-store": "^0.11.2",
39
+ "@tanstack/hotkeys": "0.12.0"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@angular/common": ">=19.0.0",
43
43
  "@angular/core": ">=19.0.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@angular/common": "^22.1.7",
47
- "@angular/core": "^22.1.7",
48
- "@angular/platform-browser-dynamic": "^22.1.7",
46
+ "@angular/common": "^22.2.1",
47
+ "@angular/core": "^22.2.1",
48
+ "@angular/platform-browser-dynamic": "^22.2.1",
49
49
  "typescript": "6.0.3"
50
50
  },
51
51
  "scripts": {
@@ -1,45 +0,0 @@
1
- import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
2
- //#region src/injectHotkeySequence.d.ts
3
- export interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled' | 'target'> {
4
- /** Whether the sequence is enabled. Defaults to true. */
5
- enabled?: boolean;
6
- /**
7
- * The DOM element to attach the event listener to.
8
- * Can be a direct DOM element, an accessor target, or null.
9
- * Defaults to document when omitted.
10
- */
11
- target?: HTMLElement | Document | Window | null;
12
- }
13
- /**
14
- * Angular inject-based API for registering a keyboard shortcut sequence (Vim-style).
15
- *
16
- * Allows you to register multi-key sequences like 'g g' or 'd d' that trigger
17
- * when the full sequence is pressed within a timeout.
18
- *
19
- * Each step may include modifiers. You can chain the same modifier across
20
- * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
21
- * Control, Alt, or Meta pressed alone) are ignored while matching—they do not
22
- * advance the sequence or reset progress.
23
- *
24
- * @param sequence - Array of hotkey strings that form the sequence (or getter function)
25
- * @param callback - Function to call when the sequence is completed
26
- * @param options - Options for the sequence behavior (or getter function). `enabled: false` still registers
27
- * the sequence (visible in devtools); only execution is suppressed.
28
- *
29
- * @example
30
- * ```ts
31
- * @Component({ ... })
32
- * export class VimEditorComponent {
33
- * lastSequence = signal<string | null>(null)
34
- *
35
- * constructor() {
36
- * injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))
37
- * injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))
38
- * injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })
39
- * injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))
40
- * }
41
- * }
42
- * ```
43
- */
44
- export declare function injectHotkeySequence(sequence: HotkeySequence | (() => HotkeySequence), callback: HotkeyCallback, options?: InjectHotkeySequenceOptions | (() => InjectHotkeySequenceOptions)): void;
45
- //#endregion
@@ -1,92 +0,0 @@
1
- import { injectDefaultHotkeysOptions } from "./hotkeys-provider.js";
2
- import { formatHotkeySequence, getSequenceManager } from "@tanstack/hotkeys";
3
- import { DestroyRef, effect, inject } from "@angular/core";
4
-
5
- //#region src/injectHotkeySequence.ts
6
- /**
7
- * Angular inject-based API for registering a keyboard shortcut sequence (Vim-style).
8
- *
9
- * Allows you to register multi-key sequences like 'g g' or 'd d' that trigger
10
- * when the full sequence is pressed within a timeout.
11
- *
12
- * Each step may include modifiers. You can chain the same modifier across
13
- * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
14
- * Control, Alt, or Meta pressed alone) are ignored while matching—they do not
15
- * advance the sequence or reset progress.
16
- *
17
- * @param sequence - Array of hotkey strings that form the sequence (or getter function)
18
- * @param callback - Function to call when the sequence is completed
19
- * @param options - Options for the sequence behavior (or getter function). `enabled: false` still registers
20
- * the sequence (visible in devtools); only execution is suppressed.
21
- *
22
- * @example
23
- * ```ts
24
- * @Component({ ... })
25
- * export class VimEditorComponent {
26
- * lastSequence = signal<string | null>(null)
27
- *
28
- * constructor() {
29
- * injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))
30
- * injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))
31
- * injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })
32
- * injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))
33
- * }
34
- * }
35
- * ```
36
- */
37
- function injectHotkeySequence(sequence, callback, options = {}) {
38
- const defaultOptions = injectDefaultHotkeysOptions();
39
- const manager = getSequenceManager();
40
- const destroyRef = inject(DestroyRef);
41
- let handle = null;
42
- let lastSequenceKey = null;
43
- let lastTarget = null;
44
- destroyRef.onDestroy(() => {
45
- if (handle?.isActive) {
46
- handle.unregister();
47
- handle = null;
48
- }
49
- lastSequenceKey = null;
50
- lastTarget = null;
51
- });
52
- effect(() => {
53
- const resolvedSequence = typeof sequence === "function" ? sequence() : sequence;
54
- const resolvedOptions = typeof options === "function" ? options() : options;
55
- const { enabled = true, ...sequenceOptions } = {
56
- ...defaultOptions.hotkeySequence,
57
- ...resolvedOptions
58
- };
59
- const resolvedTarget = "target" in sequenceOptions ? sequenceOptions.target ?? null : typeof document !== "undefined" ? document : null;
60
- if (resolvedSequence.length === 0 || !resolvedTarget) {
61
- if (handle?.isActive) {
62
- handle.unregister();
63
- handle = null;
64
- }
65
- lastSequenceKey = null;
66
- lastTarget = null;
67
- return;
68
- }
69
- const sequenceKey = formatHotkeySequence(resolvedSequence);
70
- const registerPayload = {
71
- ...sequenceOptions,
72
- enabled,
73
- target: resolvedTarget
74
- };
75
- const { target: _t, ...optionsWithoutTarget } = registerPayload;
76
- if (handle?.isActive && lastSequenceKey === sequenceKey && lastTarget === resolvedTarget) {
77
- handle.callback = callback;
78
- handle.setOptions(optionsWithoutTarget);
79
- return;
80
- }
81
- if (handle?.isActive) {
82
- handle.unregister();
83
- handle = null;
84
- }
85
- handle = manager.register(resolvedSequence, callback, registerPayload);
86
- lastSequenceKey = sequenceKey;
87
- lastTarget = resolvedTarget;
88
- });
89
- }
90
-
91
- //#endregion
92
- export { injectHotkeySequence };
@@ -1,16 +0,0 @@
1
- import { HotkeySequence, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
2
- //#region src/injectHotkeySequenceRecorder.d.ts
3
- export interface AngularHotkeySequenceRecorder {
4
- readonly isRecording: () => boolean;
5
- readonly steps: () => HotkeySequence;
6
- readonly recordedSequence: () => HotkeySequence | null;
7
- readonly startRecording: () => void;
8
- readonly stopRecording: () => void;
9
- readonly cancelRecording: () => void;
10
- readonly commitRecording: () => void;
11
- }
12
- /**
13
- * Angular inject-based API for recording multi-chord sequences.
14
- */
15
- export declare function injectHotkeySequenceRecorder(options: HotkeySequenceRecorderOptions | (() => HotkeySequenceRecorderOptions)): AngularHotkeySequenceRecorder;
16
- //#endregion
@@ -1,58 +0,0 @@
1
- import { injectDefaultHotkeysOptions } from "./hotkeys-provider.js";
2
- import { HotkeySequenceRecorder } from "@tanstack/hotkeys";
3
- import { DestroyRef, computed, effect, inject, linkedSignal, untracked } from "@angular/core";
4
-
5
- //#region src/injectHotkeySequenceRecorder.ts
6
- /**
7
- * Angular inject-based API for recording multi-chord sequences.
8
- */
9
- function injectHotkeySequenceRecorder(options) {
10
- const defaultOptions = injectDefaultHotkeysOptions();
11
- const destroyRef = inject(DestroyRef);
12
- const recorderSignal = computed(() => untracked(() => {
13
- const resolvedOptions = typeof options === "function" ? options() : options;
14
- const mergedOptions = {
15
- ...defaultOptions.hotkeySequenceRecorder,
16
- ...resolvedOptions
17
- };
18
- return new HotkeySequenceRecorder(mergedOptions);
19
- }));
20
- const recorderStore = computed(() => untracked(() => recorderSignal().store));
21
- const isRecording = injectLazyStore(recorderStore, (state) => state.isRecording);
22
- const steps = injectLazyStore(recorderStore, (state) => state.steps);
23
- const recordedSequence = injectLazyStore(recorderStore, (state) => state.recordedSequence);
24
- effect(() => {
25
- const resolved = typeof options === "function" ? options() : options;
26
- recorderSignal().setOptions({
27
- ...defaultOptions.hotkeySequenceRecorder,
28
- ...resolved
29
- });
30
- });
31
- destroyRef.onDestroy(() => {
32
- recorderSignal().destroy();
33
- });
34
- return {
35
- isRecording,
36
- steps,
37
- recordedSequence,
38
- startRecording: () => recorderSignal().start(),
39
- stopRecording: () => recorderSignal().stop(),
40
- cancelRecording: () => recorderSignal().cancel(),
41
- commitRecording: () => recorderSignal().commit()
42
- };
43
- }
44
- function injectLazyStore(storeSignal, selector) {
45
- const slice = linkedSignal(() => selector(storeSignal().get()));
46
- effect((onCleanup) => {
47
- const currentStore = storeSignal();
48
- slice.set(selector(currentStore.get()));
49
- const { unsubscribe } = currentStore.subscribe((s) => {
50
- slice.set(selector(s));
51
- });
52
- onCleanup(() => unsubscribe());
53
- });
54
- return slice.asReadonly();
55
- }
56
-
57
- //#endregion
58
- export { injectHotkeySequenceRecorder };