@tanstack/angular-hotkeys 0.6.0 → 0.7.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/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@ const require_hotkeys_provider = require('./hotkeys-provider.cjs');
3
3
  const require_injectHotkey = require('./injectHotkey.cjs');
4
4
  const require_injectHotkeys = require('./injectHotkeys.cjs');
5
5
  const require_injectHotkeySequence = require('./injectHotkeySequence.cjs');
6
+ const require_injectHotkeySequences = require('./injectHotkeySequences.cjs');
6
7
  const require_injectHeldKeys = require('./injectHeldKeys.cjs');
7
8
  const require_injectHeldKeyCodes = require('./injectHeldKeyCodes.cjs');
8
9
  const require_injectKeyHold = require('./injectKeyHold.cjs');
@@ -17,6 +18,7 @@ exports.injectHotkey = require_injectHotkey.injectHotkey;
17
18
  exports.injectHotkeyRecorder = require_injectHotkeyRecorder.injectHotkeyRecorder;
18
19
  exports.injectHotkeySequence = require_injectHotkeySequence.injectHotkeySequence;
19
20
  exports.injectHotkeySequenceRecorder = require_injectHotkeySequenceRecorder.injectHotkeySequenceRecorder;
21
+ exports.injectHotkeySequences = require_injectHotkeySequences.injectHotkeySequences;
20
22
  exports.injectHotkeys = require_injectHotkeys.injectHotkeys;
21
23
  exports.injectHotkeysContext = require_hotkeys_provider.injectHotkeysContext;
22
24
  exports.injectKeyHold = require_injectKeyHold.injectKeyHold;
package/dist/index.d.cts CHANGED
@@ -2,10 +2,11 @@ import { InjectHotkeyOptions, injectHotkey } from "./injectHotkey.cjs";
2
2
  import { InjectHotkeySequenceOptions, injectHotkeySequence } from "./injectHotkeySequence.cjs";
3
3
  import { HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, injectDefaultHotkeysOptions, injectHotkeysContext, provideHotkeys } from "./hotkeys-provider.cjs";
4
4
  import { InjectHotkeyDefinition, injectHotkeys } from "./injectHotkeys.cjs";
5
+ import { InjectHotkeySequenceDefinition, injectHotkeySequences } from "./injectHotkeySequences.cjs";
5
6
  import { injectHeldKeys } from "./injectHeldKeys.cjs";
6
7
  import { injectHeldKeyCodes } from "./injectHeldKeyCodes.cjs";
7
8
  import { injectKeyHold } from "./injectKeyHold.cjs";
8
9
  import { AngularHotkeyRecorder, injectHotkeyRecorder } from "./injectHotkeyRecorder.cjs";
9
10
  import { AngularHotkeySequenceRecorder, injectHotkeySequenceRecorder } from "./injectHotkeySequenceRecorder.cjs";
10
11
  export * from "@tanstack/hotkeys";
11
- export { AngularHotkeyRecorder, AngularHotkeySequenceRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeySequenceRecorder, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
12
+ export { AngularHotkeyRecorder, AngularHotkeySequenceRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceDefinition, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeySequenceRecorder, injectHotkeySequences, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
package/dist/index.d.ts CHANGED
@@ -2,10 +2,11 @@ import { InjectHotkeyOptions, injectHotkey } from "./injectHotkey.js";
2
2
  import { InjectHotkeySequenceOptions, injectHotkeySequence } from "./injectHotkeySequence.js";
3
3
  import { HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, injectDefaultHotkeysOptions, injectHotkeysContext, provideHotkeys } from "./hotkeys-provider.js";
4
4
  import { InjectHotkeyDefinition, injectHotkeys } from "./injectHotkeys.js";
5
+ import { InjectHotkeySequenceDefinition, injectHotkeySequences } from "./injectHotkeySequences.js";
5
6
  import { injectHeldKeys } from "./injectHeldKeys.js";
6
7
  import { injectHeldKeyCodes } from "./injectHeldKeyCodes.js";
7
8
  import { injectKeyHold } from "./injectKeyHold.js";
8
9
  import { AngularHotkeyRecorder, injectHotkeyRecorder } from "./injectHotkeyRecorder.js";
9
10
  import { AngularHotkeySequenceRecorder, injectHotkeySequenceRecorder } from "./injectHotkeySequenceRecorder.js";
10
11
  export * from "@tanstack/hotkeys";
11
- export { AngularHotkeyRecorder, AngularHotkeySequenceRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeySequenceRecorder, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
12
+ export { AngularHotkeyRecorder, AngularHotkeySequenceRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceDefinition, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeySequenceRecorder, injectHotkeySequences, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ import { HOTKEYS_INJECTION_TOKEN, injectDefaultHotkeysOptions, injectHotkeysCont
2
2
  import { injectHotkey } from "./injectHotkey.js";
3
3
  import { injectHotkeys } from "./injectHotkeys.js";
4
4
  import { injectHotkeySequence } from "./injectHotkeySequence.js";
5
+ import { injectHotkeySequences } from "./injectHotkeySequences.js";
5
6
  import { injectHeldKeys } from "./injectHeldKeys.js";
6
7
  import { injectHeldKeyCodes } from "./injectHeldKeyCodes.js";
7
8
  import { injectKeyHold } from "./injectKeyHold.js";
@@ -10,4 +11,4 @@ import { injectHotkeySequenceRecorder } from "./injectHotkeySequenceRecorder.js"
10
11
 
11
12
  export * from "@tanstack/hotkeys"
12
13
 
13
- export { HOTKEYS_INJECTION_TOKEN, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeySequenceRecorder, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
14
+ export { HOTKEYS_INJECTION_TOKEN, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeySequenceRecorder, injectHotkeySequences, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
@@ -13,6 +13,8 @@ let _angular_core = require("@angular/core");
13
13
  * Call in an injection context (e.g. constructor or field initializer).
14
14
  * Uses effect() to track reactive dependencies and update registration
15
15
  * when options or the callback change.
16
+ * `enabled: false` keeps the registration (visible in devtools) and only suppresses firing; the same
17
+ * handle is updated instead of unregistering and re-registering when identity is unchanged.
16
18
  *
17
19
  * @param hotkey - The hotkey string (e.g. 'Mod+S', 'Escape') or getter function
18
20
  * @param callback - The function to call when the hotkey is pressed
@@ -62,7 +64,19 @@ let _angular_core = require("@angular/core");
62
64
  function injectHotkey(hotkey, callback, options = {}) {
63
65
  const defaultOptions = require_hotkeys_provider.injectDefaultHotkeysOptions();
64
66
  const manager = (0, _tanstack_hotkeys.getHotkeyManager)();
65
- (0, _angular_core.effect)((onCleanup) => {
67
+ const destroyRef = (0, _angular_core.inject)(_angular_core.DestroyRef);
68
+ let registration = null;
69
+ let lastHotkeyString = null;
70
+ let lastTarget = null;
71
+ destroyRef.onDestroy(() => {
72
+ if (registration?.isActive) {
73
+ registration.unregister();
74
+ registration = null;
75
+ }
76
+ lastHotkeyString = null;
77
+ lastTarget = null;
78
+ });
79
+ (0, _angular_core.effect)(() => {
66
80
  const resolvedHotkey = typeof hotkey === "function" ? hotkey() : hotkey;
67
81
  const resolvedOptions = typeof options === "function" ? options() : options;
68
82
  const mergedOptions = {
@@ -72,9 +86,26 @@ function injectHotkey(hotkey, callback, options = {}) {
72
86
  const platform = mergedOptions.platform ?? (0, _tanstack_hotkeys.detectPlatform)();
73
87
  const hotkeyString = typeof resolvedHotkey === "string" ? resolvedHotkey : (0, _tanstack_hotkeys.formatHotkey)((0, _tanstack_hotkeys.rawHotkeyToParsedHotkey)(resolvedHotkey, platform));
74
88
  const resolvedTarget = "target" in mergedOptions ? mergedOptions.target ?? null : typeof document !== "undefined" ? document : null;
75
- if (!resolvedTarget) return;
89
+ if (!resolvedTarget) {
90
+ if (registration?.isActive) {
91
+ registration.unregister();
92
+ registration = null;
93
+ }
94
+ lastHotkeyString = null;
95
+ lastTarget = null;
96
+ return;
97
+ }
76
98
  const { target: _target, ...optionsWithoutTarget } = mergedOptions;
77
- const registration = manager.register(hotkeyString, callback, {
99
+ if (registration?.isActive && lastHotkeyString === hotkeyString && lastTarget === resolvedTarget) {
100
+ registration.callback = callback;
101
+ registration.setOptions(optionsWithoutTarget);
102
+ return;
103
+ }
104
+ if (registration?.isActive) {
105
+ registration.unregister();
106
+ registration = null;
107
+ }
108
+ registration = manager.register(hotkeyString, callback, {
78
109
  ...optionsWithoutTarget,
79
110
  target: resolvedTarget
80
111
  });
@@ -82,9 +113,8 @@ function injectHotkey(hotkey, callback, options = {}) {
82
113
  registration.callback = callback;
83
114
  registration.setOptions(optionsWithoutTarget);
84
115
  }
85
- onCleanup(() => {
86
- if (registration.isActive) registration.unregister();
87
- });
116
+ lastHotkeyString = hotkeyString;
117
+ lastTarget = resolvedTarget;
88
118
  });
89
119
  }
90
120
 
@@ -1 +1 @@
1
- {"version":3,"file":"injectHotkey.cjs","names":["injectDefaultHotkeysOptions"],"sources":["../src/injectHotkey.ts"],"sourcesContent":["import { effect } from '@angular/core'\nimport {\n detectPlatform,\n formatHotkey,\n getHotkeyManager,\n rawHotkeyToParsedHotkey,\n} from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyOptions,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\n\nexport interface InjectHotkeyOptions extends Omit<HotkeyOptions, 'target'> {\n /**\n * The DOM element to attach the event listener to.\n * Can be a direct DOM element, an accessor (for reactive targets that become\n * available after mount), or null. Defaults to document.\n * When using scoped targets, pass an accessor: () => ({ target: elementSignal() })\n * so the hotkey waits for the element to be attached before registering.\n */\n target?: HTMLElement | Document | Window | null\n}\n\n/**\n * Angular inject-based API for registering a keyboard hotkey.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * The callback receives both the keyboard event and a context object\n * containing the hotkey string and parsed hotkey.\n *\n * Call in an injection context (e.g. constructor or field initializer).\n * Uses effect() to track reactive dependencies and update registration\n * when options or the callback change.\n *\n * @param hotkey - The hotkey string (e.g. 'Mod+S', 'Escape') or getter function\n * @param callback - The function to call when the hotkey is pressed\n * @param options - Options for the hotkey behavior, or getter for reactive options\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class SaveButtonComponent {\n * private readonly saveCount = signal(0)\n *\n * constructor() {\n * injectHotkey('Mod+S', (event, { hotkey }) => {\n * event.preventDefault()\n * this.saveCount.update(c => c + 1)\n * })\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class ModalComponent {\n * isOpen = signal(true)\n *\n * constructor() {\n * injectHotkey('Escape', () => this.close(), () => ({ enabled: this.isOpen() }))\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class EditorComponent {\n * private readonly editorRef = viewChild<ElementRef<HTMLDivElement>>('editorRef')\n *\n * constructor() {\n * injectHotkey('Mod+B', () => this.toggleBold(), () => ({\n * target: this.editorRef()?.nativeElement ?? null,\n * }))\n * }\n * }\n * ```\n */\nexport function injectHotkey(\n hotkey: RegisterableHotkey | (() => RegisterableHotkey),\n callback: HotkeyCallback,\n options: InjectHotkeyOptions | (() => InjectHotkeyOptions) = {},\n): void {\n const defaultOptions = injectDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n\n effect((onCleanup) => {\n // Resolve reactive values\n const resolvedHotkey = typeof hotkey === 'function' ? hotkey() : hotkey\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedOptions,\n } as InjectHotkeyOptions\n\n // Normalize to hotkey string\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString: Hotkey =\n typeof resolvedHotkey === 'string'\n ? resolvedHotkey\n : (formatHotkey(\n rawHotkeyToParsedHotkey(resolvedHotkey, platform),\n ) as Hotkey)\n\n // Resolve target: when explicitly provided (even as null), use it and skip if null.\n // When not provided, default to document. Matches React's ref handling.\n const resolvedTarget =\n 'target' in mergedOptions\n ? (mergedOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (!resolvedTarget) {\n return\n }\n\n // Extract options without target (target is handled separately)\n const { target: _target, ...optionsWithoutTarget } = mergedOptions\n\n // Register the hotkey\n const registration = manager.register(hotkeyString, callback, {\n ...optionsWithoutTarget,\n target: resolvedTarget,\n })\n\n // Update callback and options on every effect run\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n // Cleanup on disposal\n onCleanup(() => {\n if (registration.isActive) {\n registration.unregister()\n }\n })\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFA,SAAgB,aACd,QACA,UACA,UAA6D,EAAE,EACzD;CACN,MAAM,iBAAiBA,sDAA6B;CACpD,MAAM,mDAA4B;AAElC,4BAAQ,cAAc;EAEpB,MAAM,iBAAiB,OAAO,WAAW,aAAa,QAAQ,GAAG;EACjE,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAEpE,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;EAGD,MAAM,WAAW,cAAc,mDAA4B;EAC3D,MAAM,eACJ,OAAO,mBAAmB,WACtB,oGAE0B,gBAAgB,SAAS,CAClD;EAIP,MAAM,iBACJ,YAAY,gBACP,cAAc,UAAU,OACzB,OAAO,aAAa,cAClB,WACA;AAER,MAAI,CAAC,eACH;EAIF,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB;EAGrD,MAAM,eAAe,QAAQ,SAAS,cAAc,UAAU;GAC5D,GAAG;GACH,QAAQ;GACT,CAAC;AAGF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAI/C,kBAAgB;AACd,OAAI,aAAa,SACf,cAAa,YAAY;IAE3B;GACF"}
1
+ {"version":3,"file":"injectHotkey.cjs","names":["injectDefaultHotkeysOptions","DestroyRef"],"sources":["../src/injectHotkey.ts"],"sourcesContent":["import { DestroyRef, effect, inject } from '@angular/core'\nimport {\n detectPlatform,\n formatHotkey,\n getHotkeyManager,\n rawHotkeyToParsedHotkey,\n} from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyOptions,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\n\nexport interface InjectHotkeyOptions extends Omit<HotkeyOptions, 'target'> {\n /**\n * The DOM element to attach the event listener to.\n * Can be a direct DOM element, an accessor (for reactive targets that become\n * available after mount), or null. Defaults to document.\n * When using scoped targets, pass an accessor: () => ({ target: elementSignal() })\n * so the hotkey waits for the element to be attached before registering.\n */\n target?: HTMLElement | Document | Window | null\n}\n\n/**\n * Angular inject-based API for registering a keyboard hotkey.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * The callback receives both the keyboard event and a context object\n * containing the hotkey string and parsed hotkey.\n *\n * Call in an injection context (e.g. constructor or field initializer).\n * Uses effect() to track reactive dependencies and update registration\n * when options or the callback change.\n * `enabled: false` keeps the registration (visible in devtools) and only suppresses firing; the same\n * handle is updated instead of unregistering and re-registering when identity is unchanged.\n *\n * @param hotkey - The hotkey string (e.g. 'Mod+S', 'Escape') or getter function\n * @param callback - The function to call when the hotkey is pressed\n * @param options - Options for the hotkey behavior, or getter for reactive options\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class SaveButtonComponent {\n * private readonly saveCount = signal(0)\n *\n * constructor() {\n * injectHotkey('Mod+S', (event, { hotkey }) => {\n * event.preventDefault()\n * this.saveCount.update(c => c + 1)\n * })\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class ModalComponent {\n * isOpen = signal(true)\n *\n * constructor() {\n * injectHotkey('Escape', () => this.close(), () => ({ enabled: this.isOpen() }))\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class EditorComponent {\n * private readonly editorRef = viewChild<ElementRef<HTMLDivElement>>('editorRef')\n *\n * constructor() {\n * injectHotkey('Mod+B', () => this.toggleBold(), () => ({\n * target: this.editorRef()?.nativeElement ?? null,\n * }))\n * }\n * }\n * ```\n */\nexport function injectHotkey(\n hotkey: RegisterableHotkey | (() => RegisterableHotkey),\n callback: HotkeyCallback,\n options: InjectHotkeyOptions | (() => InjectHotkeyOptions) = {},\n): void {\n const defaultOptions = injectDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n const destroyRef = inject(DestroyRef)\n\n let registration: HotkeyRegistrationHandle | null = null\n let lastHotkeyString: Hotkey | null = null\n let lastTarget: HTMLElement | Document | Window | null = null\n\n destroyRef.onDestroy(() => {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n lastHotkeyString = null\n lastTarget = null\n })\n\n effect(() => {\n // Resolve reactive values\n const resolvedHotkey = typeof hotkey === 'function' ? hotkey() : hotkey\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedOptions,\n } as InjectHotkeyOptions\n\n // Normalize to hotkey string\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString: Hotkey =\n typeof resolvedHotkey === 'string'\n ? resolvedHotkey\n : (formatHotkey(\n rawHotkeyToParsedHotkey(resolvedHotkey, platform),\n ) as Hotkey)\n\n // Resolve target: when explicitly provided (even as null), use it and skip if null.\n // When not provided, default to document. Matches React's ref handling.\n const resolvedTarget =\n 'target' in mergedOptions\n ? (mergedOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (!resolvedTarget) {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n lastHotkeyString = null\n lastTarget = null\n return\n }\n\n // Extract options without target (target is handled separately)\n const { target: _target, ...optionsWithoutTarget } = mergedOptions\n\n if (\n registration?.isActive &&\n lastHotkeyString === hotkeyString &&\n lastTarget === resolvedTarget\n ) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n return\n }\n\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n registration = manager.register(hotkeyString, callback, {\n ...optionsWithoutTarget,\n target: resolvedTarget,\n })\n\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n lastHotkeyString = hotkeyString\n lastTarget = resolvedTarget\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqFA,SAAgB,aACd,QACA,UACA,UAA6D,EAAE,EACzD;CACN,MAAM,iBAAiBA,sDAA6B;CACpD,MAAM,mDAA4B;CAClC,MAAM,uCAAoBC,yBAAW;CAErC,IAAI,eAAgD;CACpD,IAAI,mBAAkC;CACtC,IAAI,aAAqD;AAEzD,YAAW,gBAAgB;AACzB,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAEjB,qBAAmB;AACnB,eAAa;GACb;AAEF,iCAAa;EAEX,MAAM,iBAAiB,OAAO,WAAW,aAAa,QAAQ,GAAG;EACjE,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAEpE,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;EAGD,MAAM,WAAW,cAAc,mDAA4B;EAC3D,MAAM,eACJ,OAAO,mBAAmB,WACtB,oGAE0B,gBAAgB,SAAS,CAClD;EAIP,MAAM,iBACJ,YAAY,gBACP,cAAc,UAAU,OACzB,OAAO,aAAa,cAClB,WACA;AAER,MAAI,CAAC,gBAAgB;AACnB,OAAI,cAAc,UAAU;AAC1B,iBAAa,YAAY;AACzB,mBAAe;;AAEjB,sBAAmB;AACnB,gBAAa;AACb;;EAIF,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB;AAErD,MACE,cAAc,YACd,qBAAqB,gBACrB,eAAe,gBACf;AACA,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;AAC7C;;AAGF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAGjB,iBAAe,QAAQ,SAAS,cAAc,UAAU;GACtD,GAAG;GACH,QAAQ;GACT,CAAC;AAEF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAG/C,qBAAmB;AACnB,eAAa;GACb"}
@@ -21,6 +21,8 @@ interface InjectHotkeyOptions extends Omit<HotkeyOptions, 'target'> {
21
21
  * Call in an injection context (e.g. constructor or field initializer).
22
22
  * Uses effect() to track reactive dependencies and update registration
23
23
  * when options or the callback change.
24
+ * `enabled: false` keeps the registration (visible in devtools) and only suppresses firing; the same
25
+ * handle is updated instead of unregistering and re-registering when identity is unchanged.
24
26
  *
25
27
  * @param hotkey - The hotkey string (e.g. 'Mod+S', 'Escape') or getter function
26
28
  * @param callback - The function to call when the hotkey is pressed
@@ -21,6 +21,8 @@ interface InjectHotkeyOptions extends Omit<HotkeyOptions, 'target'> {
21
21
  * Call in an injection context (e.g. constructor or field initializer).
22
22
  * Uses effect() to track reactive dependencies and update registration
23
23
  * when options or the callback change.
24
+ * `enabled: false` keeps the registration (visible in devtools) and only suppresses firing; the same
25
+ * handle is updated instead of unregistering and re-registering when identity is unchanged.
24
26
  *
25
27
  * @param hotkey - The hotkey string (e.g. 'Mod+S', 'Escape') or getter function
26
28
  * @param callback - The function to call when the hotkey is pressed
@@ -1,6 +1,6 @@
1
1
  import { injectDefaultHotkeysOptions } from "./hotkeys-provider.js";
2
2
  import { detectPlatform, formatHotkey, getHotkeyManager, rawHotkeyToParsedHotkey } from "@tanstack/hotkeys";
3
- import { effect } from "@angular/core";
3
+ import { DestroyRef, effect, inject } from "@angular/core";
4
4
 
5
5
  //#region src/injectHotkey.ts
6
6
  /**
@@ -13,6 +13,8 @@ import { effect } from "@angular/core";
13
13
  * Call in an injection context (e.g. constructor or field initializer).
14
14
  * Uses effect() to track reactive dependencies and update registration
15
15
  * when options or the callback change.
16
+ * `enabled: false` keeps the registration (visible in devtools) and only suppresses firing; the same
17
+ * handle is updated instead of unregistering and re-registering when identity is unchanged.
16
18
  *
17
19
  * @param hotkey - The hotkey string (e.g. 'Mod+S', 'Escape') or getter function
18
20
  * @param callback - The function to call when the hotkey is pressed
@@ -62,7 +64,19 @@ import { effect } from "@angular/core";
62
64
  function injectHotkey(hotkey, callback, options = {}) {
63
65
  const defaultOptions = injectDefaultHotkeysOptions();
64
66
  const manager = getHotkeyManager();
65
- effect((onCleanup) => {
67
+ const destroyRef = inject(DestroyRef);
68
+ let registration = null;
69
+ let lastHotkeyString = null;
70
+ let lastTarget = null;
71
+ destroyRef.onDestroy(() => {
72
+ if (registration?.isActive) {
73
+ registration.unregister();
74
+ registration = null;
75
+ }
76
+ lastHotkeyString = null;
77
+ lastTarget = null;
78
+ });
79
+ effect(() => {
66
80
  const resolvedHotkey = typeof hotkey === "function" ? hotkey() : hotkey;
67
81
  const resolvedOptions = typeof options === "function" ? options() : options;
68
82
  const mergedOptions = {
@@ -72,9 +86,26 @@ function injectHotkey(hotkey, callback, options = {}) {
72
86
  const platform = mergedOptions.platform ?? detectPlatform();
73
87
  const hotkeyString = typeof resolvedHotkey === "string" ? resolvedHotkey : formatHotkey(rawHotkeyToParsedHotkey(resolvedHotkey, platform));
74
88
  const resolvedTarget = "target" in mergedOptions ? mergedOptions.target ?? null : typeof document !== "undefined" ? document : null;
75
- if (!resolvedTarget) return;
89
+ if (!resolvedTarget) {
90
+ if (registration?.isActive) {
91
+ registration.unregister();
92
+ registration = null;
93
+ }
94
+ lastHotkeyString = null;
95
+ lastTarget = null;
96
+ return;
97
+ }
76
98
  const { target: _target, ...optionsWithoutTarget } = mergedOptions;
77
- const registration = manager.register(hotkeyString, callback, {
99
+ if (registration?.isActive && lastHotkeyString === hotkeyString && lastTarget === resolvedTarget) {
100
+ registration.callback = callback;
101
+ registration.setOptions(optionsWithoutTarget);
102
+ return;
103
+ }
104
+ if (registration?.isActive) {
105
+ registration.unregister();
106
+ registration = null;
107
+ }
108
+ registration = manager.register(hotkeyString, callback, {
78
109
  ...optionsWithoutTarget,
79
110
  target: resolvedTarget
80
111
  });
@@ -82,9 +113,8 @@ function injectHotkey(hotkey, callback, options = {}) {
82
113
  registration.callback = callback;
83
114
  registration.setOptions(optionsWithoutTarget);
84
115
  }
85
- onCleanup(() => {
86
- if (registration.isActive) registration.unregister();
87
- });
116
+ lastHotkeyString = hotkeyString;
117
+ lastTarget = resolvedTarget;
88
118
  });
89
119
  }
90
120
 
@@ -1 +1 @@
1
- {"version":3,"file":"injectHotkey.js","names":[],"sources":["../src/injectHotkey.ts"],"sourcesContent":["import { effect } from '@angular/core'\nimport {\n detectPlatform,\n formatHotkey,\n getHotkeyManager,\n rawHotkeyToParsedHotkey,\n} from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyOptions,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\n\nexport interface InjectHotkeyOptions extends Omit<HotkeyOptions, 'target'> {\n /**\n * The DOM element to attach the event listener to.\n * Can be a direct DOM element, an accessor (for reactive targets that become\n * available after mount), or null. Defaults to document.\n * When using scoped targets, pass an accessor: () => ({ target: elementSignal() })\n * so the hotkey waits for the element to be attached before registering.\n */\n target?: HTMLElement | Document | Window | null\n}\n\n/**\n * Angular inject-based API for registering a keyboard hotkey.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * The callback receives both the keyboard event and a context object\n * containing the hotkey string and parsed hotkey.\n *\n * Call in an injection context (e.g. constructor or field initializer).\n * Uses effect() to track reactive dependencies and update registration\n * when options or the callback change.\n *\n * @param hotkey - The hotkey string (e.g. 'Mod+S', 'Escape') or getter function\n * @param callback - The function to call when the hotkey is pressed\n * @param options - Options for the hotkey behavior, or getter for reactive options\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class SaveButtonComponent {\n * private readonly saveCount = signal(0)\n *\n * constructor() {\n * injectHotkey('Mod+S', (event, { hotkey }) => {\n * event.preventDefault()\n * this.saveCount.update(c => c + 1)\n * })\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class ModalComponent {\n * isOpen = signal(true)\n *\n * constructor() {\n * injectHotkey('Escape', () => this.close(), () => ({ enabled: this.isOpen() }))\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class EditorComponent {\n * private readonly editorRef = viewChild<ElementRef<HTMLDivElement>>('editorRef')\n *\n * constructor() {\n * injectHotkey('Mod+B', () => this.toggleBold(), () => ({\n * target: this.editorRef()?.nativeElement ?? null,\n * }))\n * }\n * }\n * ```\n */\nexport function injectHotkey(\n hotkey: RegisterableHotkey | (() => RegisterableHotkey),\n callback: HotkeyCallback,\n options: InjectHotkeyOptions | (() => InjectHotkeyOptions) = {},\n): void {\n const defaultOptions = injectDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n\n effect((onCleanup) => {\n // Resolve reactive values\n const resolvedHotkey = typeof hotkey === 'function' ? hotkey() : hotkey\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedOptions,\n } as InjectHotkeyOptions\n\n // Normalize to hotkey string\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString: Hotkey =\n typeof resolvedHotkey === 'string'\n ? resolvedHotkey\n : (formatHotkey(\n rawHotkeyToParsedHotkey(resolvedHotkey, platform),\n ) as Hotkey)\n\n // Resolve target: when explicitly provided (even as null), use it and skip if null.\n // When not provided, default to document. Matches React's ref handling.\n const resolvedTarget =\n 'target' in mergedOptions\n ? (mergedOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (!resolvedTarget) {\n return\n }\n\n // Extract options without target (target is handled separately)\n const { target: _target, ...optionsWithoutTarget } = mergedOptions\n\n // Register the hotkey\n const registration = manager.register(hotkeyString, callback, {\n ...optionsWithoutTarget,\n target: resolvedTarget,\n })\n\n // Update callback and options on every effect run\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n // Cleanup on disposal\n onCleanup(() => {\n if (registration.isActive) {\n registration.unregister()\n }\n })\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFA,SAAgB,aACd,QACA,UACA,UAA6D,EAAE,EACzD;CACN,MAAM,iBAAiB,6BAA6B;CACpD,MAAM,UAAU,kBAAkB;AAElC,SAAQ,cAAc;EAEpB,MAAM,iBAAiB,OAAO,WAAW,aAAa,QAAQ,GAAG;EACjE,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAEpE,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;EAGD,MAAM,WAAW,cAAc,YAAY,gBAAgB;EAC3D,MAAM,eACJ,OAAO,mBAAmB,WACtB,iBACC,aACC,wBAAwB,gBAAgB,SAAS,CAClD;EAIP,MAAM,iBACJ,YAAY,gBACP,cAAc,UAAU,OACzB,OAAO,aAAa,cAClB,WACA;AAER,MAAI,CAAC,eACH;EAIF,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB;EAGrD,MAAM,eAAe,QAAQ,SAAS,cAAc,UAAU;GAC5D,GAAG;GACH,QAAQ;GACT,CAAC;AAGF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAI/C,kBAAgB;AACd,OAAI,aAAa,SACf,cAAa,YAAY;IAE3B;GACF"}
1
+ {"version":3,"file":"injectHotkey.js","names":[],"sources":["../src/injectHotkey.ts"],"sourcesContent":["import { DestroyRef, effect, inject } from '@angular/core'\nimport {\n detectPlatform,\n formatHotkey,\n getHotkeyManager,\n rawHotkeyToParsedHotkey,\n} from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyOptions,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\n\nexport interface InjectHotkeyOptions extends Omit<HotkeyOptions, 'target'> {\n /**\n * The DOM element to attach the event listener to.\n * Can be a direct DOM element, an accessor (for reactive targets that become\n * available after mount), or null. Defaults to document.\n * When using scoped targets, pass an accessor: () => ({ target: elementSignal() })\n * so the hotkey waits for the element to be attached before registering.\n */\n target?: HTMLElement | Document | Window | null\n}\n\n/**\n * Angular inject-based API for registering a keyboard hotkey.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * The callback receives both the keyboard event and a context object\n * containing the hotkey string and parsed hotkey.\n *\n * Call in an injection context (e.g. constructor or field initializer).\n * Uses effect() to track reactive dependencies and update registration\n * when options or the callback change.\n * `enabled: false` keeps the registration (visible in devtools) and only suppresses firing; the same\n * handle is updated instead of unregistering and re-registering when identity is unchanged.\n *\n * @param hotkey - The hotkey string (e.g. 'Mod+S', 'Escape') or getter function\n * @param callback - The function to call when the hotkey is pressed\n * @param options - Options for the hotkey behavior, or getter for reactive options\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class SaveButtonComponent {\n * private readonly saveCount = signal(0)\n *\n * constructor() {\n * injectHotkey('Mod+S', (event, { hotkey }) => {\n * event.preventDefault()\n * this.saveCount.update(c => c + 1)\n * })\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class ModalComponent {\n * isOpen = signal(true)\n *\n * constructor() {\n * injectHotkey('Escape', () => this.close(), () => ({ enabled: this.isOpen() }))\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class EditorComponent {\n * private readonly editorRef = viewChild<ElementRef<HTMLDivElement>>('editorRef')\n *\n * constructor() {\n * injectHotkey('Mod+B', () => this.toggleBold(), () => ({\n * target: this.editorRef()?.nativeElement ?? null,\n * }))\n * }\n * }\n * ```\n */\nexport function injectHotkey(\n hotkey: RegisterableHotkey | (() => RegisterableHotkey),\n callback: HotkeyCallback,\n options: InjectHotkeyOptions | (() => InjectHotkeyOptions) = {},\n): void {\n const defaultOptions = injectDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n const destroyRef = inject(DestroyRef)\n\n let registration: HotkeyRegistrationHandle | null = null\n let lastHotkeyString: Hotkey | null = null\n let lastTarget: HTMLElement | Document | Window | null = null\n\n destroyRef.onDestroy(() => {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n lastHotkeyString = null\n lastTarget = null\n })\n\n effect(() => {\n // Resolve reactive values\n const resolvedHotkey = typeof hotkey === 'function' ? hotkey() : hotkey\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedOptions,\n } as InjectHotkeyOptions\n\n // Normalize to hotkey string\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString: Hotkey =\n typeof resolvedHotkey === 'string'\n ? resolvedHotkey\n : (formatHotkey(\n rawHotkeyToParsedHotkey(resolvedHotkey, platform),\n ) as Hotkey)\n\n // Resolve target: when explicitly provided (even as null), use it and skip if null.\n // When not provided, default to document. Matches React's ref handling.\n const resolvedTarget =\n 'target' in mergedOptions\n ? (mergedOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (!resolvedTarget) {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n lastHotkeyString = null\n lastTarget = null\n return\n }\n\n // Extract options without target (target is handled separately)\n const { target: _target, ...optionsWithoutTarget } = mergedOptions\n\n if (\n registration?.isActive &&\n lastHotkeyString === hotkeyString &&\n lastTarget === resolvedTarget\n ) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n return\n }\n\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n registration = manager.register(hotkeyString, callback, {\n ...optionsWithoutTarget,\n target: resolvedTarget,\n })\n\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n lastHotkeyString = hotkeyString\n lastTarget = resolvedTarget\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqFA,SAAgB,aACd,QACA,UACA,UAA6D,EAAE,EACzD;CACN,MAAM,iBAAiB,6BAA6B;CACpD,MAAM,UAAU,kBAAkB;CAClC,MAAM,aAAa,OAAO,WAAW;CAErC,IAAI,eAAgD;CACpD,IAAI,mBAAkC;CACtC,IAAI,aAAqD;AAEzD,YAAW,gBAAgB;AACzB,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAEjB,qBAAmB;AACnB,eAAa;GACb;AAEF,cAAa;EAEX,MAAM,iBAAiB,OAAO,WAAW,aAAa,QAAQ,GAAG;EACjE,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAEpE,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;EAGD,MAAM,WAAW,cAAc,YAAY,gBAAgB;EAC3D,MAAM,eACJ,OAAO,mBAAmB,WACtB,iBACC,aACC,wBAAwB,gBAAgB,SAAS,CAClD;EAIP,MAAM,iBACJ,YAAY,gBACP,cAAc,UAAU,OACzB,OAAO,aAAa,cAClB,WACA;AAER,MAAI,CAAC,gBAAgB;AACnB,OAAI,cAAc,UAAU;AAC1B,iBAAa,YAAY;AACzB,mBAAe;;AAEjB,sBAAmB;AACnB,gBAAa;AACb;;EAIF,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB;AAErD,MACE,cAAc,YACd,qBAAqB,gBACrB,eAAe,gBACf;AACA,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;AAC7C;;AAGF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAGjB,iBAAe,QAAQ,SAAS,cAAc,UAAU;GACtD,GAAG;GACH,QAAQ;GACT,CAAC;AAEF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAG/C,qBAAmB;AACnB,eAAa;GACb"}
@@ -16,7 +16,8 @@ let _angular_core = require("@angular/core");
16
16
  *
17
17
  * @param sequence - Array of hotkey strings that form the sequence (or getter function)
18
18
  * @param callback - Function to call when the sequence is completed
19
- * @param options - Options for the sequence behavior (or getter function)
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.
20
21
  *
21
22
  * @example
22
23
  * ```ts
@@ -35,24 +36,55 @@ let _angular_core = require("@angular/core");
35
36
  */
36
37
  function injectHotkeySequence(sequence, callback, options = {}) {
37
38
  const defaultOptions = require_hotkeys_provider.injectDefaultHotkeysOptions();
38
- (0, _angular_core.effect)((onCleanup) => {
39
+ const manager = (0, _tanstack_hotkeys.getSequenceManager)();
40
+ const destroyRef = (0, _angular_core.inject)(_angular_core.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
+ (0, _angular_core.effect)(() => {
39
53
  const resolvedSequence = typeof sequence === "function" ? sequence() : sequence;
40
54
  const resolvedOptions = typeof options === "function" ? options() : options;
41
55
  const { enabled = true, ...sequenceOptions } = {
42
56
  ...defaultOptions.hotkeySequence,
43
57
  ...resolvedOptions
44
58
  };
45
- if (!enabled || resolvedSequence.length === 0) return;
46
- const manager = (0, _tanstack_hotkeys.getSequenceManager)();
47
- const registerOptions = {
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 = (0, _tanstack_hotkeys.formatHotkeySequence)(resolvedSequence);
70
+ const registerPayload = {
48
71
  ...sequenceOptions,
49
- enabled: true,
50
- target: sequenceOptions.target ?? (typeof document !== "undefined" ? document : void 0)
72
+ enabled,
73
+ target: resolvedTarget
51
74
  };
52
- const handle = manager.register(resolvedSequence, callback, registerOptions);
53
- onCleanup(() => {
54
- if (handle.isActive) handle.unregister();
55
- });
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;
56
88
  });
57
89
  }
58
90
 
@@ -1 +1 @@
1
- {"version":3,"file":"injectHotkeySequence.cjs","names":["injectDefaultHotkeysOptions"],"sources":["../src/injectHotkeySequence.ts"],"sourcesContent":["import { effect } from '@angular/core'\nimport { getSequenceManager } from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n} from '@tanstack/hotkeys'\n\nexport interface InjectHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'enabled'\n> {\n /** Whether the sequence is enabled. Defaults to true. */\n enabled?: boolean\n}\n\n/**\n * Angular inject-based API for registering a keyboard shortcut sequence (Vim-style).\n *\n * Allows you to register multi-key sequences like 'g g' or 'd d' that trigger\n * when the full sequence is pressed within a timeout.\n *\n * Each step may include modifiers. You can chain the same modifier across\n * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,\n * Control, Alt, or Meta pressed alone) are ignored while matching—they do not\n * advance the sequence or reset progress.\n *\n * @param sequence - Array of hotkey strings that form the sequence (or getter function)\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior (or getter function)\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class VimEditorComponent {\n * lastSequence = signal<string | null>(null)\n *\n * constructor() {\n * injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))\n * injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))\n * injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })\n * injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))\n * }\n * }\n * ```\n */\nexport function injectHotkeySequence(\n sequence: HotkeySequence | (() => HotkeySequence),\n callback: HotkeyCallback,\n options:\n | InjectHotkeySequenceOptions\n | (() => InjectHotkeySequenceOptions) = {},\n): void {\n const defaultOptions = injectDefaultHotkeysOptions()\n\n effect((onCleanup) => {\n // Resolve reactive values\n const resolvedSequence =\n typeof sequence === 'function' ? sequence() : sequence\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as InjectHotkeySequenceOptions\n\n const { enabled = true, ...sequenceOptions } = mergedOptions\n\n if (!enabled || resolvedSequence.length === 0) {\n return\n }\n\n const manager = getSequenceManager()\n\n // Pass through options; default target to document when not provided\n const registerOptions: SequenceOptions = {\n ...sequenceOptions,\n enabled: true,\n target:\n sequenceOptions.target ??\n (typeof document !== 'undefined' ? document : undefined),\n }\n\n const handle = manager.register(resolvedSequence, callback, registerOptions)\n\n onCleanup(() => {\n if (handle.isActive) {\n handle.unregister()\n }\n })\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,SAAgB,qBACd,UACA,UACA,UAE0C,EAAE,EACtC;CACN,MAAM,iBAAiBA,sDAA6B;AAEpD,4BAAQ,cAAc;EAEpB,MAAM,mBACJ,OAAO,aAAa,aAAa,UAAU,GAAG;EAChD,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAOpE,MAAM,EAAE,UAAU,MAAM,GAAG,oBALL;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAID,MAAI,CAAC,WAAW,iBAAiB,WAAW,EAC1C;EAGF,MAAM,qDAA8B;EAGpC,MAAM,kBAAmC;GACvC,GAAG;GACH,SAAS;GACT,QACE,gBAAgB,WACf,OAAO,aAAa,cAAc,WAAW;GACjD;EAED,MAAM,SAAS,QAAQ,SAAS,kBAAkB,UAAU,gBAAgB;AAE5E,kBAAgB;AACd,OAAI,OAAO,SACT,QAAO,YAAY;IAErB;GACF"}
1
+ {"version":3,"file":"injectHotkeySequence.cjs","names":["injectDefaultHotkeysOptions","DestroyRef"],"sources":["../src/injectHotkeySequence.ts"],"sourcesContent":["import { DestroyRef, effect, inject } from '@angular/core'\nimport { formatHotkeySequence, getSequenceManager } from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\n\ntype SequenceTarget = Document | HTMLElement | Window\n\nexport interface InjectHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'enabled' | 'target'\n> {\n /** Whether the sequence is enabled. Defaults to true. */\n enabled?: boolean\n /**\n * The DOM element to attach the event listener to.\n * Can be a direct DOM element, an accessor target, or null.\n * Defaults to document when omitted.\n */\n target?: HTMLElement | Document | Window | null\n}\n\n/**\n * Angular inject-based API for registering a keyboard shortcut sequence (Vim-style).\n *\n * Allows you to register multi-key sequences like 'g g' or 'd d' that trigger\n * when the full sequence is pressed within a timeout.\n *\n * Each step may include modifiers. You can chain the same modifier across\n * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,\n * Control, Alt, or Meta pressed alone) are ignored while matching—they do not\n * advance the sequence or reset progress.\n *\n * @param sequence - Array of hotkey strings that form the sequence (or getter function)\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior (or getter function). `enabled: false` still registers\n * the sequence (visible in devtools); only execution is suppressed.\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class VimEditorComponent {\n * lastSequence = signal<string | null>(null)\n *\n * constructor() {\n * injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))\n * injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))\n * injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })\n * injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))\n * }\n * }\n * ```\n */\nexport function injectHotkeySequence(\n sequence: HotkeySequence | (() => HotkeySequence),\n callback: HotkeyCallback,\n options:\n | InjectHotkeySequenceOptions\n | (() => InjectHotkeySequenceOptions) = {},\n): void {\n const defaultOptions = injectDefaultHotkeysOptions()\n const manager = getSequenceManager()\n const destroyRef = inject(DestroyRef)\n\n let handle: SequenceRegistrationHandle | null = null\n let lastSequenceKey: string | null = null\n let lastTarget: SequenceTarget | null = null\n\n destroyRef.onDestroy(() => {\n if (handle?.isActive) {\n handle.unregister()\n handle = null\n }\n lastSequenceKey = null\n lastTarget = null\n })\n\n effect(() => {\n // Resolve reactive values\n const resolvedSequence =\n typeof sequence === 'function' ? sequence() : sequence\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as InjectHotkeySequenceOptions\n\n const { enabled = true, ...sequenceOptions } = mergedOptions\n\n const resolvedTarget =\n 'target' in sequenceOptions\n ? (sequenceOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (resolvedSequence.length === 0 || !resolvedTarget) {\n if (handle?.isActive) {\n handle.unregister()\n handle = null\n }\n lastSequenceKey = null\n lastTarget = null\n return\n }\n\n const sequenceKey = formatHotkeySequence(resolvedSequence)\n\n const registerPayload: SequenceOptions = {\n ...sequenceOptions,\n enabled,\n target: resolvedTarget,\n }\n const { target: _t, ...optionsWithoutTarget } = registerPayload\n\n if (\n handle?.isActive &&\n lastSequenceKey === sequenceKey &&\n lastTarget === resolvedTarget\n ) {\n handle.callback = callback\n handle.setOptions(optionsWithoutTarget)\n return\n }\n\n if (handle?.isActive) {\n handle.unregister()\n handle = null\n }\n\n handle = manager.register(resolvedSequence, callback, registerPayload)\n lastSequenceKey = sequenceKey\n lastTarget = resolvedTarget\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,SAAgB,qBACd,UACA,UACA,UAE0C,EAAE,EACtC;CACN,MAAM,iBAAiBA,sDAA6B;CACpD,MAAM,qDAA8B;CACpC,MAAM,uCAAoBC,yBAAW;CAErC,IAAI,SAA4C;CAChD,IAAI,kBAAiC;CACrC,IAAI,aAAoC;AAExC,YAAW,gBAAgB;AACzB,MAAI,QAAQ,UAAU;AACpB,UAAO,YAAY;AACnB,YAAS;;AAEX,oBAAkB;AAClB,eAAa;GACb;AAEF,iCAAa;EAEX,MAAM,mBACJ,OAAO,aAAa,aAAa,UAAU,GAAG;EAChD,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAOpE,MAAM,EAAE,UAAU,MAAM,GAAG,oBALL;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;EAID,MAAM,iBACJ,YAAY,kBACP,gBAAgB,UAAU,OAC3B,OAAO,aAAa,cAClB,WACA;AAER,MAAI,iBAAiB,WAAW,KAAK,CAAC,gBAAgB;AACpD,OAAI,QAAQ,UAAU;AACpB,WAAO,YAAY;AACnB,aAAS;;AAEX,qBAAkB;AAClB,gBAAa;AACb;;EAGF,MAAM,0DAAmC,iBAAiB;EAE1D,MAAM,kBAAmC;GACvC,GAAG;GACH;GACA,QAAQ;GACT;EACD,MAAM,EAAE,QAAQ,IAAI,GAAG,yBAAyB;AAEhD,MACE,QAAQ,YACR,oBAAoB,eACpB,eAAe,gBACf;AACA,UAAO,WAAW;AAClB,UAAO,WAAW,qBAAqB;AACvC;;AAGF,MAAI,QAAQ,UAAU;AACpB,UAAO,YAAY;AACnB,YAAS;;AAGX,WAAS,QAAQ,SAAS,kBAAkB,UAAU,gBAAgB;AACtE,oBAAkB;AAClB,eAAa;GACb"}
@@ -1,9 +1,15 @@
1
1
  import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
2
2
 
3
3
  //#region src/injectHotkeySequence.d.ts
4
- interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled'> {
4
+ interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled' | 'target'> {
5
5
  /** Whether the sequence is enabled. Defaults to true. */
6
6
  enabled?: boolean;
7
+ /**
8
+ * The DOM element to attach the event listener to.
9
+ * Can be a direct DOM element, an accessor target, or null.
10
+ * Defaults to document when omitted.
11
+ */
12
+ target?: HTMLElement | Document | Window | null;
7
13
  }
8
14
  /**
9
15
  * Angular inject-based API for registering a keyboard shortcut sequence (Vim-style).
@@ -18,7 +24,8 @@ interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled'> {
18
24
  *
19
25
  * @param sequence - Array of hotkey strings that form the sequence (or getter function)
20
26
  * @param callback - Function to call when the sequence is completed
21
- * @param options - Options for the sequence behavior (or getter function)
27
+ * @param options - Options for the sequence behavior (or getter function). `enabled: false` still registers
28
+ * the sequence (visible in devtools); only execution is suppressed.
22
29
  *
23
30
  * @example
24
31
  * ```ts
@@ -1,9 +1,15 @@
1
1
  import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
2
2
 
3
3
  //#region src/injectHotkeySequence.d.ts
4
- interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled'> {
4
+ interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled' | 'target'> {
5
5
  /** Whether the sequence is enabled. Defaults to true. */
6
6
  enabled?: boolean;
7
+ /**
8
+ * The DOM element to attach the event listener to.
9
+ * Can be a direct DOM element, an accessor target, or null.
10
+ * Defaults to document when omitted.
11
+ */
12
+ target?: HTMLElement | Document | Window | null;
7
13
  }
8
14
  /**
9
15
  * Angular inject-based API for registering a keyboard shortcut sequence (Vim-style).
@@ -18,7 +24,8 @@ interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled'> {
18
24
  *
19
25
  * @param sequence - Array of hotkey strings that form the sequence (or getter function)
20
26
  * @param callback - Function to call when the sequence is completed
21
- * @param options - Options for the sequence behavior (or getter function)
27
+ * @param options - Options for the sequence behavior (or getter function). `enabled: false` still registers
28
+ * the sequence (visible in devtools); only execution is suppressed.
22
29
  *
23
30
  * @example
24
31
  * ```ts
@@ -1,6 +1,6 @@
1
1
  import { injectDefaultHotkeysOptions } from "./hotkeys-provider.js";
2
- import { getSequenceManager } from "@tanstack/hotkeys";
3
- import { effect } from "@angular/core";
2
+ import { formatHotkeySequence, getSequenceManager } from "@tanstack/hotkeys";
3
+ import { DestroyRef, effect, inject } from "@angular/core";
4
4
 
5
5
  //#region src/injectHotkeySequence.ts
6
6
  /**
@@ -16,7 +16,8 @@ import { effect } from "@angular/core";
16
16
  *
17
17
  * @param sequence - Array of hotkey strings that form the sequence (or getter function)
18
18
  * @param callback - Function to call when the sequence is completed
19
- * @param options - Options for the sequence behavior (or getter function)
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.
20
21
  *
21
22
  * @example
22
23
  * ```ts
@@ -35,24 +36,55 @@ import { effect } from "@angular/core";
35
36
  */
36
37
  function injectHotkeySequence(sequence, callback, options = {}) {
37
38
  const defaultOptions = injectDefaultHotkeysOptions();
38
- effect((onCleanup) => {
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(() => {
39
53
  const resolvedSequence = typeof sequence === "function" ? sequence() : sequence;
40
54
  const resolvedOptions = typeof options === "function" ? options() : options;
41
55
  const { enabled = true, ...sequenceOptions } = {
42
56
  ...defaultOptions.hotkeySequence,
43
57
  ...resolvedOptions
44
58
  };
45
- if (!enabled || resolvedSequence.length === 0) return;
46
- const manager = getSequenceManager();
47
- const registerOptions = {
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 = {
48
71
  ...sequenceOptions,
49
- enabled: true,
50
- target: sequenceOptions.target ?? (typeof document !== "undefined" ? document : void 0)
72
+ enabled,
73
+ target: resolvedTarget
51
74
  };
52
- const handle = manager.register(resolvedSequence, callback, registerOptions);
53
- onCleanup(() => {
54
- if (handle.isActive) handle.unregister();
55
- });
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;
56
88
  });
57
89
  }
58
90
 
@@ -1 +1 @@
1
- {"version":3,"file":"injectHotkeySequence.js","names":[],"sources":["../src/injectHotkeySequence.ts"],"sourcesContent":["import { effect } from '@angular/core'\nimport { getSequenceManager } from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n} from '@tanstack/hotkeys'\n\nexport interface InjectHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'enabled'\n> {\n /** Whether the sequence is enabled. Defaults to true. */\n enabled?: boolean\n}\n\n/**\n * Angular inject-based API for registering a keyboard shortcut sequence (Vim-style).\n *\n * Allows you to register multi-key sequences like 'g g' or 'd d' that trigger\n * when the full sequence is pressed within a timeout.\n *\n * Each step may include modifiers. You can chain the same modifier across\n * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,\n * Control, Alt, or Meta pressed alone) are ignored while matching—they do not\n * advance the sequence or reset progress.\n *\n * @param sequence - Array of hotkey strings that form the sequence (or getter function)\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior (or getter function)\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class VimEditorComponent {\n * lastSequence = signal<string | null>(null)\n *\n * constructor() {\n * injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))\n * injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))\n * injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })\n * injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))\n * }\n * }\n * ```\n */\nexport function injectHotkeySequence(\n sequence: HotkeySequence | (() => HotkeySequence),\n callback: HotkeyCallback,\n options:\n | InjectHotkeySequenceOptions\n | (() => InjectHotkeySequenceOptions) = {},\n): void {\n const defaultOptions = injectDefaultHotkeysOptions()\n\n effect((onCleanup) => {\n // Resolve reactive values\n const resolvedSequence =\n typeof sequence === 'function' ? sequence() : sequence\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as InjectHotkeySequenceOptions\n\n const { enabled = true, ...sequenceOptions } = mergedOptions\n\n if (!enabled || resolvedSequence.length === 0) {\n return\n }\n\n const manager = getSequenceManager()\n\n // Pass through options; default target to document when not provided\n const registerOptions: SequenceOptions = {\n ...sequenceOptions,\n enabled: true,\n target:\n sequenceOptions.target ??\n (typeof document !== 'undefined' ? document : undefined),\n }\n\n const handle = manager.register(resolvedSequence, callback, registerOptions)\n\n onCleanup(() => {\n if (handle.isActive) {\n handle.unregister()\n }\n })\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,SAAgB,qBACd,UACA,UACA,UAE0C,EAAE,EACtC;CACN,MAAM,iBAAiB,6BAA6B;AAEpD,SAAQ,cAAc;EAEpB,MAAM,mBACJ,OAAO,aAAa,aAAa,UAAU,GAAG;EAChD,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAOpE,MAAM,EAAE,UAAU,MAAM,GAAG,oBALL;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAID,MAAI,CAAC,WAAW,iBAAiB,WAAW,EAC1C;EAGF,MAAM,UAAU,oBAAoB;EAGpC,MAAM,kBAAmC;GACvC,GAAG;GACH,SAAS;GACT,QACE,gBAAgB,WACf,OAAO,aAAa,cAAc,WAAW;GACjD;EAED,MAAM,SAAS,QAAQ,SAAS,kBAAkB,UAAU,gBAAgB;AAE5E,kBAAgB;AACd,OAAI,OAAO,SACT,QAAO,YAAY;IAErB;GACF"}
1
+ {"version":3,"file":"injectHotkeySequence.js","names":[],"sources":["../src/injectHotkeySequence.ts"],"sourcesContent":["import { DestroyRef, effect, inject } from '@angular/core'\nimport { formatHotkeySequence, getSequenceManager } from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\n\ntype SequenceTarget = Document | HTMLElement | Window\n\nexport interface InjectHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'enabled' | 'target'\n> {\n /** Whether the sequence is enabled. Defaults to true. */\n enabled?: boolean\n /**\n * The DOM element to attach the event listener to.\n * Can be a direct DOM element, an accessor target, or null.\n * Defaults to document when omitted.\n */\n target?: HTMLElement | Document | Window | null\n}\n\n/**\n * Angular inject-based API for registering a keyboard shortcut sequence (Vim-style).\n *\n * Allows you to register multi-key sequences like 'g g' or 'd d' that trigger\n * when the full sequence is pressed within a timeout.\n *\n * Each step may include modifiers. You can chain the same modifier across\n * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,\n * Control, Alt, or Meta pressed alone) are ignored while matching—they do not\n * advance the sequence or reset progress.\n *\n * @param sequence - Array of hotkey strings that form the sequence (or getter function)\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior (or getter function). `enabled: false` still registers\n * the sequence (visible in devtools); only execution is suppressed.\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class VimEditorComponent {\n * lastSequence = signal<string | null>(null)\n *\n * constructor() {\n * injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))\n * injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))\n * injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })\n * injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))\n * }\n * }\n * ```\n */\nexport function injectHotkeySequence(\n sequence: HotkeySequence | (() => HotkeySequence),\n callback: HotkeyCallback,\n options:\n | InjectHotkeySequenceOptions\n | (() => InjectHotkeySequenceOptions) = {},\n): void {\n const defaultOptions = injectDefaultHotkeysOptions()\n const manager = getSequenceManager()\n const destroyRef = inject(DestroyRef)\n\n let handle: SequenceRegistrationHandle | null = null\n let lastSequenceKey: string | null = null\n let lastTarget: SequenceTarget | null = null\n\n destroyRef.onDestroy(() => {\n if (handle?.isActive) {\n handle.unregister()\n handle = null\n }\n lastSequenceKey = null\n lastTarget = null\n })\n\n effect(() => {\n // Resolve reactive values\n const resolvedSequence =\n typeof sequence === 'function' ? sequence() : sequence\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as InjectHotkeySequenceOptions\n\n const { enabled = true, ...sequenceOptions } = mergedOptions\n\n const resolvedTarget =\n 'target' in sequenceOptions\n ? (sequenceOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (resolvedSequence.length === 0 || !resolvedTarget) {\n if (handle?.isActive) {\n handle.unregister()\n handle = null\n }\n lastSequenceKey = null\n lastTarget = null\n return\n }\n\n const sequenceKey = formatHotkeySequence(resolvedSequence)\n\n const registerPayload: SequenceOptions = {\n ...sequenceOptions,\n enabled,\n target: resolvedTarget,\n }\n const { target: _t, ...optionsWithoutTarget } = registerPayload\n\n if (\n handle?.isActive &&\n lastSequenceKey === sequenceKey &&\n lastTarget === resolvedTarget\n ) {\n handle.callback = callback\n handle.setOptions(optionsWithoutTarget)\n return\n }\n\n if (handle?.isActive) {\n handle.unregister()\n handle = null\n }\n\n handle = manager.register(resolvedSequence, callback, registerPayload)\n lastSequenceKey = sequenceKey\n lastTarget = resolvedTarget\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,SAAgB,qBACd,UACA,UACA,UAE0C,EAAE,EACtC;CACN,MAAM,iBAAiB,6BAA6B;CACpD,MAAM,UAAU,oBAAoB;CACpC,MAAM,aAAa,OAAO,WAAW;CAErC,IAAI,SAA4C;CAChD,IAAI,kBAAiC;CACrC,IAAI,aAAoC;AAExC,YAAW,gBAAgB;AACzB,MAAI,QAAQ,UAAU;AACpB,UAAO,YAAY;AACnB,YAAS;;AAEX,oBAAkB;AAClB,eAAa;GACb;AAEF,cAAa;EAEX,MAAM,mBACJ,OAAO,aAAa,aAAa,UAAU,GAAG;EAChD,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAOpE,MAAM,EAAE,UAAU,MAAM,GAAG,oBALL;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;EAID,MAAM,iBACJ,YAAY,kBACP,gBAAgB,UAAU,OAC3B,OAAO,aAAa,cAClB,WACA;AAER,MAAI,iBAAiB,WAAW,KAAK,CAAC,gBAAgB;AACpD,OAAI,QAAQ,UAAU;AACpB,WAAO,YAAY;AACnB,aAAS;;AAEX,qBAAkB;AAClB,gBAAa;AACb;;EAGF,MAAM,cAAc,qBAAqB,iBAAiB;EAE1D,MAAM,kBAAmC;GACvC,GAAG;GACH;GACA,QAAQ;GACT;EACD,MAAM,EAAE,QAAQ,IAAI,GAAG,yBAAyB;AAEhD,MACE,QAAQ,YACR,oBAAoB,eACpB,eAAe,gBACf;AACA,UAAO,WAAW;AAClB,UAAO,WAAW,qBAAqB;AACvC;;AAGF,MAAI,QAAQ,UAAU;AACpB,UAAO,YAAY;AACnB,YAAS;;AAGX,WAAS,QAAQ,SAAS,kBAAkB,UAAU,gBAAgB;AACtE,oBAAkB;AAClB,eAAa;GACb"}