@tanstack/angular-hotkeys 0.4.2 → 0.5.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
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_hotkeys_provider = require('./hotkeys-provider.cjs');
3
3
  const require_injectHotkey = require('./injectHotkey.cjs');
4
+ const require_injectHotkeys = require('./injectHotkeys.cjs');
4
5
  const require_injectHotkeySequence = require('./injectHotkeySequence.cjs');
5
6
  const require_injectHeldKeys = require('./injectHeldKeys.cjs');
6
7
  const require_injectHeldKeyCodes = require('./injectHeldKeyCodes.cjs');
@@ -14,6 +15,7 @@ exports.injectHeldKeys = require_injectHeldKeys.injectHeldKeys;
14
15
  exports.injectHotkey = require_injectHotkey.injectHotkey;
15
16
  exports.injectHotkeyRecorder = require_injectHotkeyRecorder.injectHotkeyRecorder;
16
17
  exports.injectHotkeySequence = require_injectHotkeySequence.injectHotkeySequence;
18
+ exports.injectHotkeys = require_injectHotkeys.injectHotkeys;
17
19
  exports.injectHotkeysContext = require_hotkeys_provider.injectHotkeysContext;
18
20
  exports.injectKeyHold = require_injectKeyHold.injectKeyHold;
19
21
  exports.provideHotkeys = require_hotkeys_provider.provideHotkeys;
package/dist/index.d.cts CHANGED
@@ -1,9 +1,10 @@
1
1
  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
+ import { InjectHotkeyDefinition, injectHotkeys } from "./injectHotkeys.cjs";
4
5
  import { injectHeldKeys } from "./injectHeldKeys.cjs";
5
6
  import { injectHeldKeyCodes } from "./injectHeldKeyCodes.cjs";
6
7
  import { injectKeyHold } from "./injectKeyHold.cjs";
7
8
  import { AngularHotkeyRecorder, injectHotkeyRecorder } from "./injectHotkeyRecorder.cjs";
8
9
  export * from "@tanstack/hotkeys";
9
- export { AngularHotkeyRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeysContext, injectKeyHold, provideHotkeys };
10
+ export { AngularHotkeyRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  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
+ import { InjectHotkeyDefinition, injectHotkeys } from "./injectHotkeys.js";
4
5
  import { injectHeldKeys } from "./injectHeldKeys.js";
5
6
  import { injectHeldKeyCodes } from "./injectHeldKeyCodes.js";
6
7
  import { injectKeyHold } from "./injectKeyHold.js";
7
8
  import { AngularHotkeyRecorder, injectHotkeyRecorder } from "./injectHotkeyRecorder.js";
8
9
  export * from "@tanstack/hotkeys";
9
- export { AngularHotkeyRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeysContext, injectKeyHold, provideHotkeys };
10
+ export { AngularHotkeyRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { HOTKEYS_INJECTION_TOKEN, injectDefaultHotkeysOptions, injectHotkeysContext, provideHotkeys } from "./hotkeys-provider.js";
2
2
  import { injectHotkey } from "./injectHotkey.js";
3
+ import { injectHotkeys } from "./injectHotkeys.js";
3
4
  import { injectHotkeySequence } from "./injectHotkeySequence.js";
4
5
  import { injectHeldKeys } from "./injectHeldKeys.js";
5
6
  import { injectHeldKeyCodes } from "./injectHeldKeyCodes.js";
@@ -8,4 +9,4 @@ import { injectHotkeyRecorder } from "./injectHotkeyRecorder.js";
8
9
 
9
10
  export * from "@tanstack/hotkeys"
10
11
 
11
- export { HOTKEYS_INJECTION_TOKEN, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeysContext, injectKeyHold, provideHotkeys };
12
+ export { HOTKEYS_INJECTION_TOKEN, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
@@ -0,0 +1,121 @@
1
+ const require_hotkeys_provider = require('./hotkeys-provider.cjs');
2
+ let _tanstack_hotkeys = require("@tanstack/hotkeys");
3
+ let _angular_core = require("@angular/core");
4
+
5
+ //#region src/injectHotkeys.ts
6
+ /**
7
+ * Angular inject-based API for registering multiple keyboard hotkeys at once.
8
+ *
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
21
+ *
22
+ * @example
23
+ * ```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
+ * }
51
+ * ```
52
+ */
53
+ function injectHotkeys(hotkeys, commonOptions = {}) {
54
+ const defaultOptions = require_hotkeys_provider.injectDefaultHotkeysOptions();
55
+ const manager = (0, _tanstack_hotkeys.getHotkeyManager)();
56
+ const destroyRef = (0, _angular_core.inject)(_angular_core.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
+ });
62
+ (0, _angular_core.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 ?? (0, _tanstack_hotkeys.detectPlatform)();
77
+ const hotkeyString = typeof resolvedHotkey === "string" ? resolvedHotkey : (0, _tanstack_hotkeys.formatHotkey)((0, _tanstack_hotkeys.rawHotkeyToParsedHotkey)(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
+ }
116
+ });
117
+ }
118
+
119
+ //#endregion
120
+ exports.injectHotkeys = injectHotkeys;
121
+ //# sourceMappingURL=injectHotkeys.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"injectHotkeys.cjs","names":["injectDefaultHotkeysOptions","DestroyRef"],"sources":["../src/injectHotkeys.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 { InjectHotkeyOptions } from './injectHotkey'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\n\n/**\n * A single hotkey definition for use with `injectHotkeys`.\n */\nexport interface InjectHotkeyDefinition {\n /** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */\n hotkey: RegisterableHotkey | (() => RegisterableHotkey)\n /** The function to call when the hotkey is pressed */\n callback: HotkeyCallback\n /** Per-hotkey options (merged on top of commonOptions) */\n options?: InjectHotkeyOptions | (() => InjectHotkeyOptions)\n}\n\n/**\n * Angular inject-based API for registering multiple keyboard hotkeys at once.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * Accepts a dynamic array of hotkey definitions.\n *\n * Call in an injection context (e.g. constructor or field initializer).\n * Uses effect() to track reactive dependencies and update registrations\n * when options or the callback change.\n *\n * Options are merged in this order:\n * provideHotkeys defaults < commonOptions < per-definition options\n *\n * @param hotkeys - Array of hotkey definitions, or getter returning them\n * @param commonOptions - Shared options for all hotkeys, or getter\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class EditorComponent {\n * constructor() {\n * injectHotkeys([\n * { hotkey: 'Mod+S', callback: () => this.save() },\n * { hotkey: 'Mod+Z', callback: () => this.undo() },\n * { hotkey: 'Escape', callback: () => this.close() },\n * ])\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class DynamicShortcuts {\n * shortcuts = signal([...])\n *\n * constructor() {\n * injectHotkeys(\n * () => this.shortcuts().map((s) => ({\n * hotkey: s.key,\n * callback: s.action,\n * })),\n * )\n * }\n * }\n * ```\n */\nexport function injectHotkeys(\n hotkeys:\n | Array<InjectHotkeyDefinition>\n | (() => Array<InjectHotkeyDefinition>),\n commonOptions: InjectHotkeyOptions | (() => InjectHotkeyOptions) = {},\n): void {\n type RegistrationRecord = {\n handle: HotkeyRegistrationHandle\n target: Document | HTMLElement | Window\n }\n\n const defaultOptions = injectDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n const destroyRef = inject(DestroyRef)\n\n const registrations = new Map<string, RegistrationRecord>()\n\n destroyRef.onDestroy(() => {\n for (const { handle } of registrations.values()) {\n if (handle.isActive) {\n handle.unregister()\n }\n }\n registrations.clear()\n })\n\n effect(() => {\n const resolvedHotkeys = typeof hotkeys === 'function' ? hotkeys() : hotkeys\n const resolvedCommonOptions =\n typeof commonOptions === 'function' ? commonOptions() : commonOptions\n\n const nextKeys = new Set<string>()\n const prepared: Array<{\n registrationKey: string\n def: InjectHotkeyDefinition\n mergedOptions: InjectHotkeyOptions\n hotkeyString: Hotkey\n resolvedTarget: Document | HTMLElement | Window\n }> = []\n\n for (let i = 0; i < resolvedHotkeys.length; i++) {\n const def = resolvedHotkeys[i]!\n const resolvedHotkey =\n typeof def.hotkey === 'function' ? def.hotkey() : def.hotkey\n const resolvedDefOptions =\n typeof def.options === 'function' ? def.options() : (def.options ?? {})\n\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedCommonOptions,\n ...resolvedDefOptions,\n } as InjectHotkeyOptions\n\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 const resolvedTarget =\n 'target' in mergedOptions\n ? (mergedOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (!resolvedTarget) {\n continue\n }\n\n const registrationKey = `${i}:${hotkeyString}`\n nextKeys.add(registrationKey)\n prepared.push({\n registrationKey,\n def,\n mergedOptions,\n hotkeyString,\n resolvedTarget,\n })\n }\n\n for (const [key, record] of [...registrations.entries()]) {\n if (!nextKeys.has(key)) {\n if (record.handle.isActive) {\n record.handle.unregister()\n }\n registrations.delete(key)\n }\n }\n\n for (const p of prepared) {\n const existing = registrations.get(p.registrationKey)\n if (existing?.handle.isActive && existing.target === p.resolvedTarget) {\n existing.handle.callback = p.def.callback\n const { target: _target, ...optionsWithoutTarget } = p.mergedOptions\n existing.handle.setOptions(optionsWithoutTarget)\n continue\n }\n\n if (existing) {\n if (existing.handle.isActive) {\n existing.handle.unregister()\n }\n registrations.delete(p.registrationKey)\n }\n\n const { target: _target, ...optionsWithoutTarget } = p.mergedOptions\n const handle = manager.register(p.hotkeyString, p.def.callback, {\n ...optionsWithoutTarget,\n target: p.resolvedTarget,\n })\n registrations.set(p.registrationKey, {\n handle,\n target: p.resolvedTarget,\n })\n }\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,SAAgB,cACd,SAGA,gBAAmE,EAAE,EAC/D;CAMN,MAAM,iBAAiBA,sDAA6B;CACpD,MAAM,mDAA4B;CAClC,MAAM,uCAAoBC,yBAAW;CAErC,MAAM,gCAAgB,IAAI,KAAiC;AAE3D,YAAW,gBAAgB;AACzB,OAAK,MAAM,EAAE,YAAY,cAAc,QAAQ,CAC7C,KAAI,OAAO,SACT,QAAO,YAAY;AAGvB,gBAAc,OAAO;GACrB;AAEF,iCAAa;EACX,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EACpE,MAAM,wBACJ,OAAO,kBAAkB,aAAa,eAAe,GAAG;EAE1D,MAAM,2BAAW,IAAI,KAAa;EAClC,MAAM,WAMD,EAAE;AAEP,OAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;GAC/C,MAAM,MAAM,gBAAgB;GAC5B,MAAM,iBACJ,OAAO,IAAI,WAAW,aAAa,IAAI,QAAQ,GAAG,IAAI;GACxD,MAAM,qBACJ,OAAO,IAAI,YAAY,aAAa,IAAI,SAAS,GAAI,IAAI,WAAW,EAAE;GAExE,MAAM,gBAAgB;IACpB,GAAG,eAAe;IAClB,GAAG;IACH,GAAG;IACJ;GAED,MAAM,WAAW,cAAc,mDAA4B;GAC3D,MAAM,eACJ,OAAO,mBAAmB,WACtB,oGAE0B,gBAAgB,SAAS,CAClD;GAEP,MAAM,iBACJ,YAAY,gBACP,cAAc,UAAU,OACzB,OAAO,aAAa,cAClB,WACA;AAER,OAAI,CAAC,eACH;GAGF,MAAM,kBAAkB,GAAG,EAAE,GAAG;AAChC,YAAS,IAAI,gBAAgB;AAC7B,YAAS,KAAK;IACZ;IACA;IACA;IACA;IACA;IACD,CAAC;;AAGJ,OAAK,MAAM,CAAC,KAAK,WAAW,CAAC,GAAG,cAAc,SAAS,CAAC,CACtD,KAAI,CAAC,SAAS,IAAI,IAAI,EAAE;AACtB,OAAI,OAAO,OAAO,SAChB,QAAO,OAAO,YAAY;AAE5B,iBAAc,OAAO,IAAI;;AAI7B,OAAK,MAAM,KAAK,UAAU;GACxB,MAAM,WAAW,cAAc,IAAI,EAAE,gBAAgB;AACrD,OAAI,UAAU,OAAO,YAAY,SAAS,WAAW,EAAE,gBAAgB;AACrE,aAAS,OAAO,WAAW,EAAE,IAAI;IACjC,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB,EAAE;AACvD,aAAS,OAAO,WAAW,qBAAqB;AAChD;;AAGF,OAAI,UAAU;AACZ,QAAI,SAAS,OAAO,SAClB,UAAS,OAAO,YAAY;AAE9B,kBAAc,OAAO,EAAE,gBAAgB;;GAGzC,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB,EAAE;GACvD,MAAM,SAAS,QAAQ,SAAS,EAAE,cAAc,EAAE,IAAI,UAAU;IAC9D,GAAG;IACH,QAAQ,EAAE;IACX,CAAC;AACF,iBAAc,IAAI,EAAE,iBAAiB;IACnC;IACA,QAAQ,EAAE;IACX,CAAC;;GAEJ"}
@@ -0,0 +1,66 @@
1
+ import { InjectHotkeyOptions } from "./injectHotkey.cjs";
2
+ import { HotkeyCallback, RegisterableHotkey } from "@tanstack/hotkeys";
3
+
4
+ //#region src/injectHotkeys.d.ts
5
+ /**
6
+ * A single hotkey definition for use with `injectHotkeys`.
7
+ */
8
+ interface InjectHotkeyDefinition {
9
+ /** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */
10
+ hotkey: RegisterableHotkey | (() => RegisterableHotkey);
11
+ /** The function to call when the hotkey is pressed */
12
+ callback: HotkeyCallback;
13
+ /** Per-hotkey options (merged on top of commonOptions) */
14
+ options?: InjectHotkeyOptions | (() => InjectHotkeyOptions);
15
+ }
16
+ /**
17
+ * Angular inject-based API for registering multiple keyboard hotkeys at once.
18
+ *
19
+ * Uses the singleton HotkeyManager for efficient event handling.
20
+ * Accepts a dynamic array of hotkey definitions.
21
+ *
22
+ * Call in an injection context (e.g. constructor or field initializer).
23
+ * Uses effect() to track reactive dependencies and update registrations
24
+ * when options or the callback change.
25
+ *
26
+ * Options are merged in this order:
27
+ * provideHotkeys defaults < commonOptions < per-definition options
28
+ *
29
+ * @param hotkeys - Array of hotkey definitions, or getter returning them
30
+ * @param commonOptions - Shared options for all hotkeys, or getter
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * @Component({ ... })
35
+ * export class EditorComponent {
36
+ * constructor() {
37
+ * injectHotkeys([
38
+ * { hotkey: 'Mod+S', callback: () => this.save() },
39
+ * { hotkey: 'Mod+Z', callback: () => this.undo() },
40
+ * { hotkey: 'Escape', callback: () => this.close() },
41
+ * ])
42
+ * }
43
+ * }
44
+ * ```
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * @Component({ ... })
49
+ * export class DynamicShortcuts {
50
+ * shortcuts = signal([...])
51
+ *
52
+ * constructor() {
53
+ * injectHotkeys(
54
+ * () => this.shortcuts().map((s) => ({
55
+ * hotkey: s.key,
56
+ * callback: s.action,
57
+ * })),
58
+ * )
59
+ * }
60
+ * }
61
+ * ```
62
+ */
63
+ declare function injectHotkeys(hotkeys: Array<InjectHotkeyDefinition> | (() => Array<InjectHotkeyDefinition>), commonOptions?: InjectHotkeyOptions | (() => InjectHotkeyOptions)): void;
64
+ //#endregion
65
+ export { InjectHotkeyDefinition, injectHotkeys };
66
+ //# sourceMappingURL=injectHotkeys.d.cts.map
@@ -0,0 +1,66 @@
1
+ import { InjectHotkeyOptions } from "./injectHotkey.js";
2
+ import { HotkeyCallback, RegisterableHotkey } from "@tanstack/hotkeys";
3
+
4
+ //#region src/injectHotkeys.d.ts
5
+ /**
6
+ * A single hotkey definition for use with `injectHotkeys`.
7
+ */
8
+ interface InjectHotkeyDefinition {
9
+ /** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */
10
+ hotkey: RegisterableHotkey | (() => RegisterableHotkey);
11
+ /** The function to call when the hotkey is pressed */
12
+ callback: HotkeyCallback;
13
+ /** Per-hotkey options (merged on top of commonOptions) */
14
+ options?: InjectHotkeyOptions | (() => InjectHotkeyOptions);
15
+ }
16
+ /**
17
+ * Angular inject-based API for registering multiple keyboard hotkeys at once.
18
+ *
19
+ * Uses the singleton HotkeyManager for efficient event handling.
20
+ * Accepts a dynamic array of hotkey definitions.
21
+ *
22
+ * Call in an injection context (e.g. constructor or field initializer).
23
+ * Uses effect() to track reactive dependencies and update registrations
24
+ * when options or the callback change.
25
+ *
26
+ * Options are merged in this order:
27
+ * provideHotkeys defaults < commonOptions < per-definition options
28
+ *
29
+ * @param hotkeys - Array of hotkey definitions, or getter returning them
30
+ * @param commonOptions - Shared options for all hotkeys, or getter
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * @Component({ ... })
35
+ * export class EditorComponent {
36
+ * constructor() {
37
+ * injectHotkeys([
38
+ * { hotkey: 'Mod+S', callback: () => this.save() },
39
+ * { hotkey: 'Mod+Z', callback: () => this.undo() },
40
+ * { hotkey: 'Escape', callback: () => this.close() },
41
+ * ])
42
+ * }
43
+ * }
44
+ * ```
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * @Component({ ... })
49
+ * export class DynamicShortcuts {
50
+ * shortcuts = signal([...])
51
+ *
52
+ * constructor() {
53
+ * injectHotkeys(
54
+ * () => this.shortcuts().map((s) => ({
55
+ * hotkey: s.key,
56
+ * callback: s.action,
57
+ * })),
58
+ * )
59
+ * }
60
+ * }
61
+ * ```
62
+ */
63
+ declare function injectHotkeys(hotkeys: Array<InjectHotkeyDefinition> | (() => Array<InjectHotkeyDefinition>), commonOptions?: InjectHotkeyOptions | (() => InjectHotkeyOptions)): void;
64
+ //#endregion
65
+ export { InjectHotkeyDefinition, injectHotkeys };
66
+ //# sourceMappingURL=injectHotkeys.d.ts.map
@@ -0,0 +1,121 @@
1
+ import { injectDefaultHotkeysOptions } from "./hotkeys-provider.js";
2
+ import { detectPlatform, formatHotkey, getHotkeyManager, rawHotkeyToParsedHotkey } from "@tanstack/hotkeys";
3
+ import { DestroyRef, effect, inject } from "@angular/core";
4
+
5
+ //#region src/injectHotkeys.ts
6
+ /**
7
+ * Angular inject-based API for registering multiple keyboard hotkeys at once.
8
+ *
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
21
+ *
22
+ * @example
23
+ * ```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
+ * }
51
+ * ```
52
+ */
53
+ 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
+ });
62
+ 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 = typeof resolvedHotkey === "string" ? resolvedHotkey : formatHotkey(rawHotkeyToParsedHotkey(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
+ }
116
+ });
117
+ }
118
+
119
+ //#endregion
120
+ export { injectHotkeys };
121
+ //# sourceMappingURL=injectHotkeys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"injectHotkeys.js","names":[],"sources":["../src/injectHotkeys.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 { InjectHotkeyOptions } from './injectHotkey'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\n\n/**\n * A single hotkey definition for use with `injectHotkeys`.\n */\nexport interface InjectHotkeyDefinition {\n /** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */\n hotkey: RegisterableHotkey | (() => RegisterableHotkey)\n /** The function to call when the hotkey is pressed */\n callback: HotkeyCallback\n /** Per-hotkey options (merged on top of commonOptions) */\n options?: InjectHotkeyOptions | (() => InjectHotkeyOptions)\n}\n\n/**\n * Angular inject-based API for registering multiple keyboard hotkeys at once.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * Accepts a dynamic array of hotkey definitions.\n *\n * Call in an injection context (e.g. constructor or field initializer).\n * Uses effect() to track reactive dependencies and update registrations\n * when options or the callback change.\n *\n * Options are merged in this order:\n * provideHotkeys defaults < commonOptions < per-definition options\n *\n * @param hotkeys - Array of hotkey definitions, or getter returning them\n * @param commonOptions - Shared options for all hotkeys, or getter\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class EditorComponent {\n * constructor() {\n * injectHotkeys([\n * { hotkey: 'Mod+S', callback: () => this.save() },\n * { hotkey: 'Mod+Z', callback: () => this.undo() },\n * { hotkey: 'Escape', callback: () => this.close() },\n * ])\n * }\n * }\n * ```\n *\n * @example\n * ```ts\n * @Component({ ... })\n * export class DynamicShortcuts {\n * shortcuts = signal([...])\n *\n * constructor() {\n * injectHotkeys(\n * () => this.shortcuts().map((s) => ({\n * hotkey: s.key,\n * callback: s.action,\n * })),\n * )\n * }\n * }\n * ```\n */\nexport function injectHotkeys(\n hotkeys:\n | Array<InjectHotkeyDefinition>\n | (() => Array<InjectHotkeyDefinition>),\n commonOptions: InjectHotkeyOptions | (() => InjectHotkeyOptions) = {},\n): void {\n type RegistrationRecord = {\n handle: HotkeyRegistrationHandle\n target: Document | HTMLElement | Window\n }\n\n const defaultOptions = injectDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n const destroyRef = inject(DestroyRef)\n\n const registrations = new Map<string, RegistrationRecord>()\n\n destroyRef.onDestroy(() => {\n for (const { handle } of registrations.values()) {\n if (handle.isActive) {\n handle.unregister()\n }\n }\n registrations.clear()\n })\n\n effect(() => {\n const resolvedHotkeys = typeof hotkeys === 'function' ? hotkeys() : hotkeys\n const resolvedCommonOptions =\n typeof commonOptions === 'function' ? commonOptions() : commonOptions\n\n const nextKeys = new Set<string>()\n const prepared: Array<{\n registrationKey: string\n def: InjectHotkeyDefinition\n mergedOptions: InjectHotkeyOptions\n hotkeyString: Hotkey\n resolvedTarget: Document | HTMLElement | Window\n }> = []\n\n for (let i = 0; i < resolvedHotkeys.length; i++) {\n const def = resolvedHotkeys[i]!\n const resolvedHotkey =\n typeof def.hotkey === 'function' ? def.hotkey() : def.hotkey\n const resolvedDefOptions =\n typeof def.options === 'function' ? def.options() : (def.options ?? {})\n\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedCommonOptions,\n ...resolvedDefOptions,\n } as InjectHotkeyOptions\n\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 const resolvedTarget =\n 'target' in mergedOptions\n ? (mergedOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (!resolvedTarget) {\n continue\n }\n\n const registrationKey = `${i}:${hotkeyString}`\n nextKeys.add(registrationKey)\n prepared.push({\n registrationKey,\n def,\n mergedOptions,\n hotkeyString,\n resolvedTarget,\n })\n }\n\n for (const [key, record] of [...registrations.entries()]) {\n if (!nextKeys.has(key)) {\n if (record.handle.isActive) {\n record.handle.unregister()\n }\n registrations.delete(key)\n }\n }\n\n for (const p of prepared) {\n const existing = registrations.get(p.registrationKey)\n if (existing?.handle.isActive && existing.target === p.resolvedTarget) {\n existing.handle.callback = p.def.callback\n const { target: _target, ...optionsWithoutTarget } = p.mergedOptions\n existing.handle.setOptions(optionsWithoutTarget)\n continue\n }\n\n if (existing) {\n if (existing.handle.isActive) {\n existing.handle.unregister()\n }\n registrations.delete(p.registrationKey)\n }\n\n const { target: _target, ...optionsWithoutTarget } = p.mergedOptions\n const handle = manager.register(p.hotkeyString, p.def.callback, {\n ...optionsWithoutTarget,\n target: p.resolvedTarget,\n })\n registrations.set(p.registrationKey, {\n handle,\n target: p.resolvedTarget,\n })\n }\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,SAAgB,cACd,SAGA,gBAAmE,EAAE,EAC/D;CAMN,MAAM,iBAAiB,6BAA6B;CACpD,MAAM,UAAU,kBAAkB;CAClC,MAAM,aAAa,OAAO,WAAW;CAErC,MAAM,gCAAgB,IAAI,KAAiC;AAE3D,YAAW,gBAAgB;AACzB,OAAK,MAAM,EAAE,YAAY,cAAc,QAAQ,CAC7C,KAAI,OAAO,SACT,QAAO,YAAY;AAGvB,gBAAc,OAAO;GACrB;AAEF,cAAa;EACX,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EACpE,MAAM,wBACJ,OAAO,kBAAkB,aAAa,eAAe,GAAG;EAE1D,MAAM,2BAAW,IAAI,KAAa;EAClC,MAAM,WAMD,EAAE;AAEP,OAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;GAC/C,MAAM,MAAM,gBAAgB;GAC5B,MAAM,iBACJ,OAAO,IAAI,WAAW,aAAa,IAAI,QAAQ,GAAG,IAAI;GACxD,MAAM,qBACJ,OAAO,IAAI,YAAY,aAAa,IAAI,SAAS,GAAI,IAAI,WAAW,EAAE;GAExE,MAAM,gBAAgB;IACpB,GAAG,eAAe;IAClB,GAAG;IACH,GAAG;IACJ;GAED,MAAM,WAAW,cAAc,YAAY,gBAAgB;GAC3D,MAAM,eACJ,OAAO,mBAAmB,WACtB,iBACC,aACC,wBAAwB,gBAAgB,SAAS,CAClD;GAEP,MAAM,iBACJ,YAAY,gBACP,cAAc,UAAU,OACzB,OAAO,aAAa,cAClB,WACA;AAER,OAAI,CAAC,eACH;GAGF,MAAM,kBAAkB,GAAG,EAAE,GAAG;AAChC,YAAS,IAAI,gBAAgB;AAC7B,YAAS,KAAK;IACZ;IACA;IACA;IACA;IACA;IACD,CAAC;;AAGJ,OAAK,MAAM,CAAC,KAAK,WAAW,CAAC,GAAG,cAAc,SAAS,CAAC,CACtD,KAAI,CAAC,SAAS,IAAI,IAAI,EAAE;AACtB,OAAI,OAAO,OAAO,SAChB,QAAO,OAAO,YAAY;AAE5B,iBAAc,OAAO,IAAI;;AAI7B,OAAK,MAAM,KAAK,UAAU;GACxB,MAAM,WAAW,cAAc,IAAI,EAAE,gBAAgB;AACrD,OAAI,UAAU,OAAO,YAAY,SAAS,WAAW,EAAE,gBAAgB;AACrE,aAAS,OAAO,WAAW,EAAE,IAAI;IACjC,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB,EAAE;AACvD,aAAS,OAAO,WAAW,qBAAqB;AAChD;;AAGF,OAAI,UAAU;AACZ,QAAI,SAAS,OAAO,SAClB,UAAS,OAAO,YAAY;AAE9B,kBAAc,OAAO,EAAE,gBAAgB;;GAGzC,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB,EAAE;GACvD,MAAM,SAAS,QAAQ,SAAS,EAAE,cAAc,EAAE,IAAI,UAAU;IAC9D,GAAG;IACH,QAAQ,EAAE;IACX,CAAC;AACF,iBAAc,IAAI,EAAE,iBAAiB;IACnC;IACA,QAAQ,EAAE;IACX,CAAC;;GAEJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/angular-hotkeys",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Angular adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -49,9 +49,9 @@
49
49
  "@angular/core": ">=19.0.0"
50
50
  },
51
51
  "devDependencies": {
52
- "@angular/common": "^21.2.1",
53
- "@angular/core": "^21.2.1",
54
- "@angular/platform-browser-dynamic": "^21.2.1",
52
+ "@angular/common": "^21.2.5",
53
+ "@angular/core": "^21.2.5",
54
+ "@angular/platform-browser-dynamic": "^21.2.5",
55
55
  "typescript": "5.9.3"
56
56
  },
57
57
  "scripts": {
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ export * from './hotkeys-provider'
6
6
 
7
7
  // Angular-specific primitives
8
8
  export * from './injectHotkey'
9
+ export * from './injectHotkeys'
9
10
  export * from './injectHotkeySequence'
10
11
  export * from './injectHeldKeys'
11
12
  export * from './injectHeldKeyCodes'
@@ -0,0 +1,195 @@
1
+ import { DestroyRef, effect, inject } from '@angular/core'
2
+ import {
3
+ detectPlatform,
4
+ formatHotkey,
5
+ getHotkeyManager,
6
+ rawHotkeyToParsedHotkey,
7
+ } from '@tanstack/hotkeys'
8
+ import { injectDefaultHotkeysOptions } from './hotkeys-provider'
9
+ import type { InjectHotkeyOptions } from './injectHotkey'
10
+ import type {
11
+ Hotkey,
12
+ HotkeyCallback,
13
+ HotkeyRegistrationHandle,
14
+ RegisterableHotkey,
15
+ } from '@tanstack/hotkeys'
16
+
17
+ /**
18
+ * A single hotkey definition for use with `injectHotkeys`.
19
+ */
20
+ export interface InjectHotkeyDefinition {
21
+ /** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */
22
+ hotkey: RegisterableHotkey | (() => RegisterableHotkey)
23
+ /** The function to call when the hotkey is pressed */
24
+ callback: HotkeyCallback
25
+ /** Per-hotkey options (merged on top of commonOptions) */
26
+ options?: InjectHotkeyOptions | (() => InjectHotkeyOptions)
27
+ }
28
+
29
+ /**
30
+ * Angular inject-based API for registering multiple keyboard hotkeys at once.
31
+ *
32
+ * Uses the singleton HotkeyManager for efficient event handling.
33
+ * Accepts a dynamic array of hotkey definitions.
34
+ *
35
+ * Call in an injection context (e.g. constructor or field initializer).
36
+ * Uses effect() to track reactive dependencies and update registrations
37
+ * when options or the callback change.
38
+ *
39
+ * Options are merged in this order:
40
+ * provideHotkeys defaults < commonOptions < per-definition options
41
+ *
42
+ * @param hotkeys - Array of hotkey definitions, or getter returning them
43
+ * @param commonOptions - Shared options for all hotkeys, or getter
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * @Component({ ... })
48
+ * export class EditorComponent {
49
+ * constructor() {
50
+ * injectHotkeys([
51
+ * { hotkey: 'Mod+S', callback: () => this.save() },
52
+ * { hotkey: 'Mod+Z', callback: () => this.undo() },
53
+ * { hotkey: 'Escape', callback: () => this.close() },
54
+ * ])
55
+ * }
56
+ * }
57
+ * ```
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * @Component({ ... })
62
+ * export class DynamicShortcuts {
63
+ * shortcuts = signal([...])
64
+ *
65
+ * constructor() {
66
+ * injectHotkeys(
67
+ * () => this.shortcuts().map((s) => ({
68
+ * hotkey: s.key,
69
+ * callback: s.action,
70
+ * })),
71
+ * )
72
+ * }
73
+ * }
74
+ * ```
75
+ */
76
+ export function injectHotkeys(
77
+ hotkeys:
78
+ | Array<InjectHotkeyDefinition>
79
+ | (() => Array<InjectHotkeyDefinition>),
80
+ commonOptions: InjectHotkeyOptions | (() => InjectHotkeyOptions) = {},
81
+ ): void {
82
+ type RegistrationRecord = {
83
+ handle: HotkeyRegistrationHandle
84
+ target: Document | HTMLElement | Window
85
+ }
86
+
87
+ const defaultOptions = injectDefaultHotkeysOptions()
88
+ const manager = getHotkeyManager()
89
+ const destroyRef = inject(DestroyRef)
90
+
91
+ const registrations = new Map<string, RegistrationRecord>()
92
+
93
+ destroyRef.onDestroy(() => {
94
+ for (const { handle } of registrations.values()) {
95
+ if (handle.isActive) {
96
+ handle.unregister()
97
+ }
98
+ }
99
+ registrations.clear()
100
+ })
101
+
102
+ effect(() => {
103
+ const resolvedHotkeys = typeof hotkeys === 'function' ? hotkeys() : hotkeys
104
+ const resolvedCommonOptions =
105
+ typeof commonOptions === 'function' ? commonOptions() : commonOptions
106
+
107
+ const nextKeys = new Set<string>()
108
+ const prepared: Array<{
109
+ registrationKey: string
110
+ def: InjectHotkeyDefinition
111
+ mergedOptions: InjectHotkeyOptions
112
+ hotkeyString: Hotkey
113
+ resolvedTarget: Document | HTMLElement | Window
114
+ }> = []
115
+
116
+ for (let i = 0; i < resolvedHotkeys.length; i++) {
117
+ const def = resolvedHotkeys[i]!
118
+ const resolvedHotkey =
119
+ typeof def.hotkey === 'function' ? def.hotkey() : def.hotkey
120
+ const resolvedDefOptions =
121
+ typeof def.options === 'function' ? def.options() : (def.options ?? {})
122
+
123
+ const mergedOptions = {
124
+ ...defaultOptions.hotkey,
125
+ ...resolvedCommonOptions,
126
+ ...resolvedDefOptions,
127
+ } as InjectHotkeyOptions
128
+
129
+ const platform = mergedOptions.platform ?? detectPlatform()
130
+ const hotkeyString: Hotkey =
131
+ typeof resolvedHotkey === 'string'
132
+ ? resolvedHotkey
133
+ : (formatHotkey(
134
+ rawHotkeyToParsedHotkey(resolvedHotkey, platform),
135
+ ) as Hotkey)
136
+
137
+ const resolvedTarget =
138
+ 'target' in mergedOptions
139
+ ? (mergedOptions.target ?? null)
140
+ : typeof document !== 'undefined'
141
+ ? document
142
+ : null
143
+
144
+ if (!resolvedTarget) {
145
+ continue
146
+ }
147
+
148
+ const registrationKey = `${i}:${hotkeyString}`
149
+ nextKeys.add(registrationKey)
150
+ prepared.push({
151
+ registrationKey,
152
+ def,
153
+ mergedOptions,
154
+ hotkeyString,
155
+ resolvedTarget,
156
+ })
157
+ }
158
+
159
+ for (const [key, record] of [...registrations.entries()]) {
160
+ if (!nextKeys.has(key)) {
161
+ if (record.handle.isActive) {
162
+ record.handle.unregister()
163
+ }
164
+ registrations.delete(key)
165
+ }
166
+ }
167
+
168
+ for (const p of prepared) {
169
+ const existing = registrations.get(p.registrationKey)
170
+ if (existing?.handle.isActive && existing.target === p.resolvedTarget) {
171
+ existing.handle.callback = p.def.callback
172
+ const { target: _target, ...optionsWithoutTarget } = p.mergedOptions
173
+ existing.handle.setOptions(optionsWithoutTarget)
174
+ continue
175
+ }
176
+
177
+ if (existing) {
178
+ if (existing.handle.isActive) {
179
+ existing.handle.unregister()
180
+ }
181
+ registrations.delete(p.registrationKey)
182
+ }
183
+
184
+ const { target: _target, ...optionsWithoutTarget } = p.mergedOptions
185
+ const handle = manager.register(p.hotkeyString, p.def.callback, {
186
+ ...optionsWithoutTarget,
187
+ target: p.resolvedTarget,
188
+ })
189
+ registrations.set(p.registrationKey, {
190
+ handle,
191
+ target: p.resolvedTarget,
192
+ })
193
+ }
194
+ })
195
+ }