@tanstack/angular-hotkeys 0.5.0 → 0.6.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/hotkeys-provider.cjs.map +1 -1
- package/dist/hotkeys-provider.d.cts +2 -1
- package/dist/hotkeys-provider.d.ts +2 -1
- package/dist/hotkeys-provider.js.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/injectHotkeySequence.cjs +6 -0
- package/dist/injectHotkeySequence.cjs.map +1 -1
- package/dist/injectHotkeySequence.d.cts +6 -0
- package/dist/injectHotkeySequence.d.ts +6 -0
- package/dist/injectHotkeySequence.js +6 -0
- package/dist/injectHotkeySequence.js.map +1 -1
- package/dist/injectHotkeySequenceRecorder.cjs +58 -0
- package/dist/injectHotkeySequenceRecorder.cjs.map +1 -0
- package/dist/injectHotkeySequenceRecorder.d.cts +19 -0
- package/dist/injectHotkeySequenceRecorder.d.ts +19 -0
- package/dist/injectHotkeySequenceRecorder.js +58 -0
- package/dist/injectHotkeySequenceRecorder.js.map +1 -0
- package/package.json +2 -2
- package/src/hotkeys-provider.ts +5 -1
- package/src/index.ts +1 -0
- package/src/injectHotkeySequence.ts +6 -0
- package/src/injectHotkeySequenceRecorder.ts +103 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hotkeys-provider.cjs","names":["InjectionToken"],"sources":["../src/hotkeys-provider.ts"],"sourcesContent":["import { InjectionToken, inject } from '@angular/core'\nimport type {
|
|
1
|
+
{"version":3,"file":"hotkeys-provider.cjs","names":["InjectionToken"],"sources":["../src/hotkeys-provider.ts"],"sourcesContent":["import { InjectionToken, inject } from '@angular/core'\nimport type {\n HotkeyRecorderOptions,\n HotkeySequenceRecorderOptions,\n} from '@tanstack/hotkeys'\nimport type { InjectHotkeyOptions } from './injectHotkey'\nimport type { InjectHotkeySequenceOptions } from './injectHotkeySequence'\nimport type { StaticProvider } from '@angular/core'\n\nexport interface HotkeysProviderOptions {\n hotkey?: Partial<InjectHotkeyOptions>\n hotkeyRecorder?: Partial<HotkeyRecorderOptions>\n hotkeySequenceRecorder?: Partial<HotkeySequenceRecorderOptions>\n hotkeySequence?: Partial<InjectHotkeySequenceOptions>\n}\n\nexport interface HotkeysContextValue {\n readonly defaultOptions: HotkeysProviderOptions\n}\n\nexport const HOTKEYS_INJECTION_TOKEN = new InjectionToken<HotkeysContextValue>(\n 'HOTKEYS_INJECTION_TOKEN',\n {\n providedIn: 'root',\n factory: () => ({ defaultOptions: {} }),\n },\n)\n\nexport function provideHotkeys(\n defaultOptions: HotkeysProviderOptions,\n): StaticProvider {\n return {\n provide: HOTKEYS_INJECTION_TOKEN,\n useValue: { defaultOptions },\n }\n}\n\nexport function injectHotkeysContext(): HotkeysContextValue {\n return inject(HOTKEYS_INJECTION_TOKEN)\n}\n\nexport function injectDefaultHotkeysOptions(): HotkeysProviderOptions {\n return injectHotkeysContext().defaultOptions\n}\n"],"mappings":";;;AAoBA,MAAa,0BAA0B,IAAIA,6BACzC,2BACA;CACE,YAAY;CACZ,gBAAgB,EAAE,gBAAgB,EAAE,EAAE;CACvC,CACF;AAED,SAAgB,eACd,gBACgB;AAChB,QAAO;EACL,SAAS;EACT,UAAU,EAAE,gBAAgB;EAC7B;;AAGH,SAAgB,uBAA4C;AAC1D,kCAAc,wBAAwB;;AAGxC,SAAgB,8BAAsD;AACpE,QAAO,sBAAsB,CAAC"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { InjectHotkeyOptions } from "./injectHotkey.cjs";
|
|
2
2
|
import { InjectHotkeySequenceOptions } from "./injectHotkeySequence.cjs";
|
|
3
|
-
import { HotkeyRecorderOptions } from "@tanstack/hotkeys";
|
|
3
|
+
import { HotkeyRecorderOptions, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
|
|
4
4
|
import { InjectionToken, StaticProvider } from "@angular/core";
|
|
5
5
|
|
|
6
6
|
//#region src/hotkeys-provider.d.ts
|
|
7
7
|
interface HotkeysProviderOptions {
|
|
8
8
|
hotkey?: Partial<InjectHotkeyOptions>;
|
|
9
9
|
hotkeyRecorder?: Partial<HotkeyRecorderOptions>;
|
|
10
|
+
hotkeySequenceRecorder?: Partial<HotkeySequenceRecorderOptions>;
|
|
10
11
|
hotkeySequence?: Partial<InjectHotkeySequenceOptions>;
|
|
11
12
|
}
|
|
12
13
|
interface HotkeysContextValue {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { InjectHotkeyOptions } from "./injectHotkey.js";
|
|
2
2
|
import { InjectHotkeySequenceOptions } from "./injectHotkeySequence.js";
|
|
3
|
-
import { HotkeyRecorderOptions } from "@tanstack/hotkeys";
|
|
3
|
+
import { HotkeyRecorderOptions, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
|
|
4
4
|
import { InjectionToken, StaticProvider } from "@angular/core";
|
|
5
5
|
|
|
6
6
|
//#region src/hotkeys-provider.d.ts
|
|
7
7
|
interface HotkeysProviderOptions {
|
|
8
8
|
hotkey?: Partial<InjectHotkeyOptions>;
|
|
9
9
|
hotkeyRecorder?: Partial<HotkeyRecorderOptions>;
|
|
10
|
+
hotkeySequenceRecorder?: Partial<HotkeySequenceRecorderOptions>;
|
|
10
11
|
hotkeySequence?: Partial<InjectHotkeySequenceOptions>;
|
|
11
12
|
}
|
|
12
13
|
interface HotkeysContextValue {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hotkeys-provider.js","names":[],"sources":["../src/hotkeys-provider.ts"],"sourcesContent":["import { InjectionToken, inject } from '@angular/core'\nimport type {
|
|
1
|
+
{"version":3,"file":"hotkeys-provider.js","names":[],"sources":["../src/hotkeys-provider.ts"],"sourcesContent":["import { InjectionToken, inject } from '@angular/core'\nimport type {\n HotkeyRecorderOptions,\n HotkeySequenceRecorderOptions,\n} from '@tanstack/hotkeys'\nimport type { InjectHotkeyOptions } from './injectHotkey'\nimport type { InjectHotkeySequenceOptions } from './injectHotkeySequence'\nimport type { StaticProvider } from '@angular/core'\n\nexport interface HotkeysProviderOptions {\n hotkey?: Partial<InjectHotkeyOptions>\n hotkeyRecorder?: Partial<HotkeyRecorderOptions>\n hotkeySequenceRecorder?: Partial<HotkeySequenceRecorderOptions>\n hotkeySequence?: Partial<InjectHotkeySequenceOptions>\n}\n\nexport interface HotkeysContextValue {\n readonly defaultOptions: HotkeysProviderOptions\n}\n\nexport const HOTKEYS_INJECTION_TOKEN = new InjectionToken<HotkeysContextValue>(\n 'HOTKEYS_INJECTION_TOKEN',\n {\n providedIn: 'root',\n factory: () => ({ defaultOptions: {} }),\n },\n)\n\nexport function provideHotkeys(\n defaultOptions: HotkeysProviderOptions,\n): StaticProvider {\n return {\n provide: HOTKEYS_INJECTION_TOKEN,\n useValue: { defaultOptions },\n }\n}\n\nexport function injectHotkeysContext(): HotkeysContextValue {\n return inject(HOTKEYS_INJECTION_TOKEN)\n}\n\nexport function injectDefaultHotkeysOptions(): HotkeysProviderOptions {\n return injectHotkeysContext().defaultOptions\n}\n"],"mappings":";;;AAoBA,MAAa,0BAA0B,IAAI,eACzC,2BACA;CACE,YAAY;CACZ,gBAAgB,EAAE,gBAAgB,EAAE,EAAE;CACvC,CACF;AAED,SAAgB,eACd,gBACgB;AAChB,QAAO;EACL,SAAS;EACT,UAAU,EAAE,gBAAgB;EAC7B;;AAGH,SAAgB,uBAA4C;AAC1D,QAAO,OAAO,wBAAwB;;AAGxC,SAAgB,8BAAsD;AACpE,QAAO,sBAAsB,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -7,6 +7,7 @@ const require_injectHeldKeys = require('./injectHeldKeys.cjs');
|
|
|
7
7
|
const require_injectHeldKeyCodes = require('./injectHeldKeyCodes.cjs');
|
|
8
8
|
const require_injectKeyHold = require('./injectKeyHold.cjs');
|
|
9
9
|
const require_injectHotkeyRecorder = require('./injectHotkeyRecorder.cjs');
|
|
10
|
+
const require_injectHotkeySequenceRecorder = require('./injectHotkeySequenceRecorder.cjs');
|
|
10
11
|
|
|
11
12
|
exports.HOTKEYS_INJECTION_TOKEN = require_hotkeys_provider.HOTKEYS_INJECTION_TOKEN;
|
|
12
13
|
exports.injectDefaultHotkeysOptions = require_hotkeys_provider.injectDefaultHotkeysOptions;
|
|
@@ -15,6 +16,7 @@ exports.injectHeldKeys = require_injectHeldKeys.injectHeldKeys;
|
|
|
15
16
|
exports.injectHotkey = require_injectHotkey.injectHotkey;
|
|
16
17
|
exports.injectHotkeyRecorder = require_injectHotkeyRecorder.injectHotkeyRecorder;
|
|
17
18
|
exports.injectHotkeySequence = require_injectHotkeySequence.injectHotkeySequence;
|
|
19
|
+
exports.injectHotkeySequenceRecorder = require_injectHotkeySequenceRecorder.injectHotkeySequenceRecorder;
|
|
18
20
|
exports.injectHotkeys = require_injectHotkeys.injectHotkeys;
|
|
19
21
|
exports.injectHotkeysContext = require_hotkeys_provider.injectHotkeysContext;
|
|
20
22
|
exports.injectKeyHold = require_injectKeyHold.injectKeyHold;
|
package/dist/index.d.cts
CHANGED
|
@@ -6,5 +6,6 @@ import { injectHeldKeys } from "./injectHeldKeys.cjs";
|
|
|
6
6
|
import { injectHeldKeyCodes } from "./injectHeldKeyCodes.cjs";
|
|
7
7
|
import { injectKeyHold } from "./injectKeyHold.cjs";
|
|
8
8
|
import { AngularHotkeyRecorder, injectHotkeyRecorder } from "./injectHotkeyRecorder.cjs";
|
|
9
|
+
import { AngularHotkeySequenceRecorder, injectHotkeySequenceRecorder } from "./injectHotkeySequenceRecorder.cjs";
|
|
9
10
|
export * from "@tanstack/hotkeys";
|
|
10
|
-
export { AngularHotkeyRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
|
|
11
|
+
export { AngularHotkeyRecorder, AngularHotkeySequenceRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeySequenceRecorder, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,6 @@ import { injectHeldKeys } from "./injectHeldKeys.js";
|
|
|
6
6
|
import { injectHeldKeyCodes } from "./injectHeldKeyCodes.js";
|
|
7
7
|
import { injectKeyHold } from "./injectKeyHold.js";
|
|
8
8
|
import { AngularHotkeyRecorder, injectHotkeyRecorder } from "./injectHotkeyRecorder.js";
|
|
9
|
+
import { AngularHotkeySequenceRecorder, injectHotkeySequenceRecorder } from "./injectHotkeySequenceRecorder.js";
|
|
9
10
|
export * from "@tanstack/hotkeys";
|
|
10
|
-
export { AngularHotkeyRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
|
|
11
|
+
export { AngularHotkeyRecorder, AngularHotkeySequenceRecorder, HOTKEYS_INJECTION_TOKEN, HotkeysContextValue, HotkeysProviderOptions, InjectHotkeyDefinition, InjectHotkeyOptions, InjectHotkeySequenceOptions, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeySequenceRecorder, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,8 @@ import { injectHeldKeys } from "./injectHeldKeys.js";
|
|
|
6
6
|
import { injectHeldKeyCodes } from "./injectHeldKeyCodes.js";
|
|
7
7
|
import { injectKeyHold } from "./injectKeyHold.js";
|
|
8
8
|
import { injectHotkeyRecorder } from "./injectHotkeyRecorder.js";
|
|
9
|
+
import { injectHotkeySequenceRecorder } from "./injectHotkeySequenceRecorder.js";
|
|
9
10
|
|
|
10
11
|
export * from "@tanstack/hotkeys"
|
|
11
12
|
|
|
12
|
-
export { HOTKEYS_INJECTION_TOKEN, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
|
|
13
|
+
export { HOTKEYS_INJECTION_TOKEN, injectDefaultHotkeysOptions, injectHeldKeyCodes, injectHeldKeys, injectHotkey, injectHotkeyRecorder, injectHotkeySequence, injectHotkeySequenceRecorder, injectHotkeys, injectHotkeysContext, injectKeyHold, provideHotkeys };
|
|
@@ -9,6 +9,11 @@ let _angular_core = require("@angular/core");
|
|
|
9
9
|
* Allows you to register multi-key sequences like 'g g' or 'd d' that trigger
|
|
10
10
|
* when the full sequence is pressed within a timeout.
|
|
11
11
|
*
|
|
12
|
+
* Each step may include modifiers. You can chain the same modifier across
|
|
13
|
+
* steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
|
|
14
|
+
* Control, Alt, or Meta pressed alone) are ignored while matching—they do not
|
|
15
|
+
* advance the sequence or reset progress.
|
|
16
|
+
*
|
|
12
17
|
* @param sequence - Array of hotkey strings that form the sequence (or getter function)
|
|
13
18
|
* @param callback - Function to call when the sequence is completed
|
|
14
19
|
* @param options - Options for the sequence behavior (or getter function)
|
|
@@ -23,6 +28,7 @@ let _angular_core = require("@angular/core");
|
|
|
23
28
|
* injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))
|
|
24
29
|
* injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))
|
|
25
30
|
* injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })
|
|
31
|
+
* injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))
|
|
26
32
|
* }
|
|
27
33
|
* }
|
|
28
34
|
* ```
|
|
@@ -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 * @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 * }\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":"
|
|
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"}
|
|
@@ -11,6 +11,11 @@ interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled'> {
|
|
|
11
11
|
* Allows you to register multi-key sequences like 'g g' or 'd d' that trigger
|
|
12
12
|
* when the full sequence is pressed within a timeout.
|
|
13
13
|
*
|
|
14
|
+
* Each step may include modifiers. You can chain the same modifier across
|
|
15
|
+
* steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
|
|
16
|
+
* Control, Alt, or Meta pressed alone) are ignored while matching—they do not
|
|
17
|
+
* advance the sequence or reset progress.
|
|
18
|
+
*
|
|
14
19
|
* @param sequence - Array of hotkey strings that form the sequence (or getter function)
|
|
15
20
|
* @param callback - Function to call when the sequence is completed
|
|
16
21
|
* @param options - Options for the sequence behavior (or getter function)
|
|
@@ -25,6 +30,7 @@ interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled'> {
|
|
|
25
30
|
* injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))
|
|
26
31
|
* injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))
|
|
27
32
|
* injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })
|
|
33
|
+
* injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))
|
|
28
34
|
* }
|
|
29
35
|
* }
|
|
30
36
|
* ```
|
|
@@ -11,6 +11,11 @@ interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled'> {
|
|
|
11
11
|
* Allows you to register multi-key sequences like 'g g' or 'd d' that trigger
|
|
12
12
|
* when the full sequence is pressed within a timeout.
|
|
13
13
|
*
|
|
14
|
+
* Each step may include modifiers. You can chain the same modifier across
|
|
15
|
+
* steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
|
|
16
|
+
* Control, Alt, or Meta pressed alone) are ignored while matching—they do not
|
|
17
|
+
* advance the sequence or reset progress.
|
|
18
|
+
*
|
|
14
19
|
* @param sequence - Array of hotkey strings that form the sequence (or getter function)
|
|
15
20
|
* @param callback - Function to call when the sequence is completed
|
|
16
21
|
* @param options - Options for the sequence behavior (or getter function)
|
|
@@ -25,6 +30,7 @@ interface InjectHotkeySequenceOptions extends Omit<SequenceOptions, 'enabled'> {
|
|
|
25
30
|
* injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))
|
|
26
31
|
* injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))
|
|
27
32
|
* injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })
|
|
33
|
+
* injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))
|
|
28
34
|
* }
|
|
29
35
|
* }
|
|
30
36
|
* ```
|
|
@@ -9,6 +9,11 @@ import { effect } from "@angular/core";
|
|
|
9
9
|
* Allows you to register multi-key sequences like 'g g' or 'd d' that trigger
|
|
10
10
|
* when the full sequence is pressed within a timeout.
|
|
11
11
|
*
|
|
12
|
+
* Each step may include modifiers. You can chain the same modifier across
|
|
13
|
+
* steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
|
|
14
|
+
* Control, Alt, or Meta pressed alone) are ignored while matching—they do not
|
|
15
|
+
* advance the sequence or reset progress.
|
|
16
|
+
*
|
|
12
17
|
* @param sequence - Array of hotkey strings that form the sequence (or getter function)
|
|
13
18
|
* @param callback - Function to call when the sequence is completed
|
|
14
19
|
* @param options - Options for the sequence behavior (or getter function)
|
|
@@ -23,6 +28,7 @@ import { effect } from "@angular/core";
|
|
|
23
28
|
* injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))
|
|
24
29
|
* injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))
|
|
25
30
|
* injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })
|
|
31
|
+
* injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))
|
|
26
32
|
* }
|
|
27
33
|
* }
|
|
28
34
|
* ```
|
|
@@ -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 * @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 * }\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":"
|
|
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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
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/injectHotkeySequenceRecorder.ts
|
|
6
|
+
/**
|
|
7
|
+
* Angular inject-based API for recording multi-chord sequences.
|
|
8
|
+
*/
|
|
9
|
+
function injectHotkeySequenceRecorder(options) {
|
|
10
|
+
const defaultOptions = require_hotkeys_provider.injectDefaultHotkeysOptions();
|
|
11
|
+
const destroyRef = (0, _angular_core.inject)(_angular_core.DestroyRef);
|
|
12
|
+
const recorderSignal = (0, _angular_core.computed)(() => (0, _angular_core.untracked)(() => {
|
|
13
|
+
const resolvedOptions = typeof options === "function" ? options() : options;
|
|
14
|
+
return new _tanstack_hotkeys.HotkeySequenceRecorder({
|
|
15
|
+
...defaultOptions.hotkeySequenceRecorder,
|
|
16
|
+
...resolvedOptions
|
|
17
|
+
});
|
|
18
|
+
}));
|
|
19
|
+
const recorderStore = (0, _angular_core.computed)(() => (0, _angular_core.untracked)(() => recorderSignal().store));
|
|
20
|
+
const isRecording = injectLazyStore(recorderStore, (state) => state.isRecording);
|
|
21
|
+
const steps = injectLazyStore(recorderStore, (state) => state.steps);
|
|
22
|
+
const recordedSequence = injectLazyStore(recorderStore, (state) => state.recordedSequence);
|
|
23
|
+
(0, _angular_core.effect)(() => {
|
|
24
|
+
const resolved = typeof options === "function" ? options() : options;
|
|
25
|
+
recorderSignal().setOptions({
|
|
26
|
+
...defaultOptions.hotkeySequenceRecorder,
|
|
27
|
+
...resolved
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
destroyRef.onDestroy(() => {
|
|
31
|
+
recorderSignal().destroy();
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
isRecording,
|
|
35
|
+
steps,
|
|
36
|
+
recordedSequence,
|
|
37
|
+
startRecording: () => recorderSignal().start(),
|
|
38
|
+
stopRecording: () => recorderSignal().stop(),
|
|
39
|
+
cancelRecording: () => recorderSignal().cancel(),
|
|
40
|
+
commitRecording: () => recorderSignal().commit()
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function injectLazyStore(storeSignal, selector) {
|
|
44
|
+
const slice = (0, _angular_core.linkedSignal)(() => selector(storeSignal().get()));
|
|
45
|
+
(0, _angular_core.effect)((onCleanup) => {
|
|
46
|
+
const currentStore = storeSignal();
|
|
47
|
+
slice.set(selector(currentStore.get()));
|
|
48
|
+
const { unsubscribe } = currentStore.subscribe((s) => {
|
|
49
|
+
slice.set(selector(s));
|
|
50
|
+
});
|
|
51
|
+
onCleanup(() => unsubscribe());
|
|
52
|
+
});
|
|
53
|
+
return slice.asReadonly();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
exports.injectHotkeySequenceRecorder = injectHotkeySequenceRecorder;
|
|
58
|
+
//# sourceMappingURL=injectHotkeySequenceRecorder.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injectHotkeySequenceRecorder.cjs","names":["injectDefaultHotkeysOptions","DestroyRef","HotkeySequenceRecorder"],"sources":["../src/injectHotkeySequenceRecorder.ts"],"sourcesContent":["import {\n DestroyRef,\n computed,\n effect,\n inject,\n linkedSignal,\n untracked,\n} from '@angular/core'\nimport { HotkeySequenceRecorder } from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type { Atom, ReadonlyAtom } from '@tanstack/angular-store'\nimport type { Signal } from '@angular/core'\nimport type {\n HotkeySequence,\n HotkeySequenceRecorderOptions,\n} from '@tanstack/hotkeys'\n\nexport interface AngularHotkeySequenceRecorder {\n readonly isRecording: () => boolean\n readonly steps: () => HotkeySequence\n readonly recordedSequence: () => HotkeySequence | null\n readonly startRecording: () => void\n readonly stopRecording: () => void\n readonly cancelRecording: () => void\n readonly commitRecording: () => void\n}\n\n/**\n * Angular inject-based API for recording multi-chord sequences.\n */\nexport function injectHotkeySequenceRecorder(\n options:\n | HotkeySequenceRecorderOptions\n | (() => HotkeySequenceRecorderOptions),\n): AngularHotkeySequenceRecorder {\n const defaultOptions = injectDefaultHotkeysOptions()\n const destroyRef = inject(DestroyRef)\n\n const recorderSignal = computed(() =>\n untracked(() => {\n const resolvedOptions =\n typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequenceRecorder,\n ...resolvedOptions,\n } as HotkeySequenceRecorderOptions\n\n return new HotkeySequenceRecorder(mergedOptions)\n }),\n )\n\n const recorderStore = computed(() => untracked(() => recorderSignal().store))\n const isRecording = injectLazyStore(\n recorderStore,\n (state) => state.isRecording,\n )\n const steps = injectLazyStore(recorderStore, (state) => state.steps)\n const recordedSequence = injectLazyStore(\n recorderStore,\n (state) => state.recordedSequence,\n )\n\n effect(() => {\n const resolved = typeof options === 'function' ? options() : options\n recorderSignal().setOptions({\n ...defaultOptions.hotkeySequenceRecorder,\n ...resolved,\n } as HotkeySequenceRecorderOptions)\n })\n\n destroyRef.onDestroy(() => {\n recorderSignal().destroy()\n })\n\n return {\n isRecording,\n steps,\n recordedSequence,\n startRecording: () => recorderSignal().start(),\n stopRecording: () => recorderSignal().stop(),\n cancelRecording: () => recorderSignal().cancel(),\n commitRecording: () => recorderSignal().commit(),\n }\n}\n\nfunction injectLazyStore<TState, TSelected = NoInfer<TState>>(\n storeSignal: Signal<Atom<TState> | ReadonlyAtom<TState>>,\n selector: (state: NoInfer<TState>) => TSelected,\n): Signal<TSelected> {\n const slice = linkedSignal(() => selector(storeSignal().get()))\n\n effect((onCleanup) => {\n const currentStore = storeSignal()\n slice.set(selector(currentStore.get()))\n const { unsubscribe } = currentStore.subscribe((s) => {\n slice.set(selector(s))\n })\n onCleanup(() => unsubscribe())\n })\n\n return slice.asReadonly()\n}\n"],"mappings":";;;;;;;;AA8BA,SAAgB,6BACd,SAG+B;CAC/B,MAAM,iBAAiBA,sDAA6B;CACpD,MAAM,uCAAoBC,yBAAW;CAErC,MAAM,sFACY;EACd,MAAM,kBACJ,OAAO,YAAY,aAAa,SAAS,GAAG;AAO9C,SAAO,IAAIC,yCALW;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ,CAE+C;GAChD,CACH;CAED,MAAM,qFAA+C,gBAAgB,CAAC,MAAM,CAAC;CAC7E,MAAM,cAAc,gBAClB,gBACC,UAAU,MAAM,YAClB;CACD,MAAM,QAAQ,gBAAgB,gBAAgB,UAAU,MAAM,MAAM;CACpE,MAAM,mBAAmB,gBACvB,gBACC,UAAU,MAAM,iBAClB;AAED,iCAAa;EACX,MAAM,WAAW,OAAO,YAAY,aAAa,SAAS,GAAG;AAC7D,kBAAgB,CAAC,WAAW;GAC1B,GAAG,eAAe;GAClB,GAAG;GACJ,CAAkC;GACnC;AAEF,YAAW,gBAAgB;AACzB,kBAAgB,CAAC,SAAS;GAC1B;AAEF,QAAO;EACL;EACA;EACA;EACA,sBAAsB,gBAAgB,CAAC,OAAO;EAC9C,qBAAqB,gBAAgB,CAAC,MAAM;EAC5C,uBAAuB,gBAAgB,CAAC,QAAQ;EAChD,uBAAuB,gBAAgB,CAAC,QAAQ;EACjD;;AAGH,SAAS,gBACP,aACA,UACmB;CACnB,MAAM,8CAA2B,SAAS,aAAa,CAAC,KAAK,CAAC,CAAC;AAE/D,4BAAQ,cAAc;EACpB,MAAM,eAAe,aAAa;AAClC,QAAM,IAAI,SAAS,aAAa,KAAK,CAAC,CAAC;EACvC,MAAM,EAAE,gBAAgB,aAAa,WAAW,MAAM;AACpD,SAAM,IAAI,SAAS,EAAE,CAAC;IACtB;AACF,kBAAgB,aAAa,CAAC;GAC9B;AAEF,QAAO,MAAM,YAAY"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { HotkeySequence, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
|
|
2
|
+
|
|
3
|
+
//#region src/injectHotkeySequenceRecorder.d.ts
|
|
4
|
+
interface AngularHotkeySequenceRecorder {
|
|
5
|
+
readonly isRecording: () => boolean;
|
|
6
|
+
readonly steps: () => HotkeySequence;
|
|
7
|
+
readonly recordedSequence: () => HotkeySequence | null;
|
|
8
|
+
readonly startRecording: () => void;
|
|
9
|
+
readonly stopRecording: () => void;
|
|
10
|
+
readonly cancelRecording: () => void;
|
|
11
|
+
readonly commitRecording: () => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Angular inject-based API for recording multi-chord sequences.
|
|
15
|
+
*/
|
|
16
|
+
declare function injectHotkeySequenceRecorder(options: HotkeySequenceRecorderOptions | (() => HotkeySequenceRecorderOptions)): AngularHotkeySequenceRecorder;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { AngularHotkeySequenceRecorder, injectHotkeySequenceRecorder };
|
|
19
|
+
//# sourceMappingURL=injectHotkeySequenceRecorder.d.cts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { HotkeySequence, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
|
|
2
|
+
|
|
3
|
+
//#region src/injectHotkeySequenceRecorder.d.ts
|
|
4
|
+
interface AngularHotkeySequenceRecorder {
|
|
5
|
+
readonly isRecording: () => boolean;
|
|
6
|
+
readonly steps: () => HotkeySequence;
|
|
7
|
+
readonly recordedSequence: () => HotkeySequence | null;
|
|
8
|
+
readonly startRecording: () => void;
|
|
9
|
+
readonly stopRecording: () => void;
|
|
10
|
+
readonly cancelRecording: () => void;
|
|
11
|
+
readonly commitRecording: () => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Angular inject-based API for recording multi-chord sequences.
|
|
15
|
+
*/
|
|
16
|
+
declare function injectHotkeySequenceRecorder(options: HotkeySequenceRecorderOptions | (() => HotkeySequenceRecorderOptions)): AngularHotkeySequenceRecorder;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { AngularHotkeySequenceRecorder, injectHotkeySequenceRecorder };
|
|
19
|
+
//# sourceMappingURL=injectHotkeySequenceRecorder.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { injectDefaultHotkeysOptions } from "./hotkeys-provider.js";
|
|
2
|
+
import { HotkeySequenceRecorder } from "@tanstack/hotkeys";
|
|
3
|
+
import { DestroyRef, computed, effect, inject, linkedSignal, untracked } from "@angular/core";
|
|
4
|
+
|
|
5
|
+
//#region src/injectHotkeySequenceRecorder.ts
|
|
6
|
+
/**
|
|
7
|
+
* Angular inject-based API for recording multi-chord sequences.
|
|
8
|
+
*/
|
|
9
|
+
function injectHotkeySequenceRecorder(options) {
|
|
10
|
+
const defaultOptions = injectDefaultHotkeysOptions();
|
|
11
|
+
const destroyRef = inject(DestroyRef);
|
|
12
|
+
const recorderSignal = computed(() => untracked(() => {
|
|
13
|
+
const resolvedOptions = typeof options === "function" ? options() : options;
|
|
14
|
+
return new HotkeySequenceRecorder({
|
|
15
|
+
...defaultOptions.hotkeySequenceRecorder,
|
|
16
|
+
...resolvedOptions
|
|
17
|
+
});
|
|
18
|
+
}));
|
|
19
|
+
const recorderStore = computed(() => untracked(() => recorderSignal().store));
|
|
20
|
+
const isRecording = injectLazyStore(recorderStore, (state) => state.isRecording);
|
|
21
|
+
const steps = injectLazyStore(recorderStore, (state) => state.steps);
|
|
22
|
+
const recordedSequence = injectLazyStore(recorderStore, (state) => state.recordedSequence);
|
|
23
|
+
effect(() => {
|
|
24
|
+
const resolved = typeof options === "function" ? options() : options;
|
|
25
|
+
recorderSignal().setOptions({
|
|
26
|
+
...defaultOptions.hotkeySequenceRecorder,
|
|
27
|
+
...resolved
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
destroyRef.onDestroy(() => {
|
|
31
|
+
recorderSignal().destroy();
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
isRecording,
|
|
35
|
+
steps,
|
|
36
|
+
recordedSequence,
|
|
37
|
+
startRecording: () => recorderSignal().start(),
|
|
38
|
+
stopRecording: () => recorderSignal().stop(),
|
|
39
|
+
cancelRecording: () => recorderSignal().cancel(),
|
|
40
|
+
commitRecording: () => recorderSignal().commit()
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function injectLazyStore(storeSignal, selector) {
|
|
44
|
+
const slice = linkedSignal(() => selector(storeSignal().get()));
|
|
45
|
+
effect((onCleanup) => {
|
|
46
|
+
const currentStore = storeSignal();
|
|
47
|
+
slice.set(selector(currentStore.get()));
|
|
48
|
+
const { unsubscribe } = currentStore.subscribe((s) => {
|
|
49
|
+
slice.set(selector(s));
|
|
50
|
+
});
|
|
51
|
+
onCleanup(() => unsubscribe());
|
|
52
|
+
});
|
|
53
|
+
return slice.asReadonly();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
export { injectHotkeySequenceRecorder };
|
|
58
|
+
//# sourceMappingURL=injectHotkeySequenceRecorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injectHotkeySequenceRecorder.js","names":[],"sources":["../src/injectHotkeySequenceRecorder.ts"],"sourcesContent":["import {\n DestroyRef,\n computed,\n effect,\n inject,\n linkedSignal,\n untracked,\n} from '@angular/core'\nimport { HotkeySequenceRecorder } from '@tanstack/hotkeys'\nimport { injectDefaultHotkeysOptions } from './hotkeys-provider'\nimport type { Atom, ReadonlyAtom } from '@tanstack/angular-store'\nimport type { Signal } from '@angular/core'\nimport type {\n HotkeySequence,\n HotkeySequenceRecorderOptions,\n} from '@tanstack/hotkeys'\n\nexport interface AngularHotkeySequenceRecorder {\n readonly isRecording: () => boolean\n readonly steps: () => HotkeySequence\n readonly recordedSequence: () => HotkeySequence | null\n readonly startRecording: () => void\n readonly stopRecording: () => void\n readonly cancelRecording: () => void\n readonly commitRecording: () => void\n}\n\n/**\n * Angular inject-based API for recording multi-chord sequences.\n */\nexport function injectHotkeySequenceRecorder(\n options:\n | HotkeySequenceRecorderOptions\n | (() => HotkeySequenceRecorderOptions),\n): AngularHotkeySequenceRecorder {\n const defaultOptions = injectDefaultHotkeysOptions()\n const destroyRef = inject(DestroyRef)\n\n const recorderSignal = computed(() =>\n untracked(() => {\n const resolvedOptions =\n typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequenceRecorder,\n ...resolvedOptions,\n } as HotkeySequenceRecorderOptions\n\n return new HotkeySequenceRecorder(mergedOptions)\n }),\n )\n\n const recorderStore = computed(() => untracked(() => recorderSignal().store))\n const isRecording = injectLazyStore(\n recorderStore,\n (state) => state.isRecording,\n )\n const steps = injectLazyStore(recorderStore, (state) => state.steps)\n const recordedSequence = injectLazyStore(\n recorderStore,\n (state) => state.recordedSequence,\n )\n\n effect(() => {\n const resolved = typeof options === 'function' ? options() : options\n recorderSignal().setOptions({\n ...defaultOptions.hotkeySequenceRecorder,\n ...resolved,\n } as HotkeySequenceRecorderOptions)\n })\n\n destroyRef.onDestroy(() => {\n recorderSignal().destroy()\n })\n\n return {\n isRecording,\n steps,\n recordedSequence,\n startRecording: () => recorderSignal().start(),\n stopRecording: () => recorderSignal().stop(),\n cancelRecording: () => recorderSignal().cancel(),\n commitRecording: () => recorderSignal().commit(),\n }\n}\n\nfunction injectLazyStore<TState, TSelected = NoInfer<TState>>(\n storeSignal: Signal<Atom<TState> | ReadonlyAtom<TState>>,\n selector: (state: NoInfer<TState>) => TSelected,\n): Signal<TSelected> {\n const slice = linkedSignal(() => selector(storeSignal().get()))\n\n effect((onCleanup) => {\n const currentStore = storeSignal()\n slice.set(selector(currentStore.get()))\n const { unsubscribe } = currentStore.subscribe((s) => {\n slice.set(selector(s))\n })\n onCleanup(() => unsubscribe())\n })\n\n return slice.asReadonly()\n}\n"],"mappings":";;;;;;;;AA8BA,SAAgB,6BACd,SAG+B;CAC/B,MAAM,iBAAiB,6BAA6B;CACpD,MAAM,aAAa,OAAO,WAAW;CAErC,MAAM,iBAAiB,eACrB,gBAAgB;EACd,MAAM,kBACJ,OAAO,YAAY,aAAa,SAAS,GAAG;AAO9C,SAAO,IAAI,uBALW;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ,CAE+C;GAChD,CACH;CAED,MAAM,gBAAgB,eAAe,gBAAgB,gBAAgB,CAAC,MAAM,CAAC;CAC7E,MAAM,cAAc,gBAClB,gBACC,UAAU,MAAM,YAClB;CACD,MAAM,QAAQ,gBAAgB,gBAAgB,UAAU,MAAM,MAAM;CACpE,MAAM,mBAAmB,gBACvB,gBACC,UAAU,MAAM,iBAClB;AAED,cAAa;EACX,MAAM,WAAW,OAAO,YAAY,aAAa,SAAS,GAAG;AAC7D,kBAAgB,CAAC,WAAW;GAC1B,GAAG,eAAe;GAClB,GAAG;GACJ,CAAkC;GACnC;AAEF,YAAW,gBAAgB;AACzB,kBAAgB,CAAC,SAAS;GAC1B;AAEF,QAAO;EACL;EACA;EACA;EACA,sBAAsB,gBAAgB,CAAC,OAAO;EAC9C,qBAAqB,gBAAgB,CAAC,MAAM;EAC5C,uBAAuB,gBAAgB,CAAC,QAAQ;EAChD,uBAAuB,gBAAgB,CAAC,QAAQ;EACjD;;AAGH,SAAS,gBACP,aACA,UACmB;CACnB,MAAM,QAAQ,mBAAmB,SAAS,aAAa,CAAC,KAAK,CAAC,CAAC;AAE/D,SAAQ,cAAc;EACpB,MAAM,eAAe,aAAa;AAClC,QAAM,IAAI,SAAS,aAAa,KAAK,CAAC,CAAC;EACvC,MAAM,EAAE,gBAAgB,aAAa,WAAW,MAAM;AACpD,SAAM,IAAI,SAAS,EAAE,CAAC;IACtB;AACF,kBAAgB,aAAa,CAAC;GAC9B;AAEF,QAAO,MAAM,YAAY"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/angular-hotkeys",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Angular adapter for TanStack Hotkeys",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@tanstack/angular-store": "^0.9.2",
|
|
45
|
-
"@tanstack/hotkeys": "0.
|
|
45
|
+
"@tanstack/hotkeys": "0.5.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@angular/common": ">=19.0.0",
|
package/src/hotkeys-provider.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { InjectionToken, inject } from '@angular/core'
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
HotkeyRecorderOptions,
|
|
4
|
+
HotkeySequenceRecorderOptions,
|
|
5
|
+
} from '@tanstack/hotkeys'
|
|
3
6
|
import type { InjectHotkeyOptions } from './injectHotkey'
|
|
4
7
|
import type { InjectHotkeySequenceOptions } from './injectHotkeySequence'
|
|
5
8
|
import type { StaticProvider } from '@angular/core'
|
|
@@ -7,6 +10,7 @@ import type { StaticProvider } from '@angular/core'
|
|
|
7
10
|
export interface HotkeysProviderOptions {
|
|
8
11
|
hotkey?: Partial<InjectHotkeyOptions>
|
|
9
12
|
hotkeyRecorder?: Partial<HotkeyRecorderOptions>
|
|
13
|
+
hotkeySequenceRecorder?: Partial<HotkeySequenceRecorderOptions>
|
|
10
14
|
hotkeySequence?: Partial<InjectHotkeySequenceOptions>
|
|
11
15
|
}
|
|
12
16
|
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,11 @@ export interface InjectHotkeySequenceOptions extends Omit<
|
|
|
21
21
|
* Allows you to register multi-key sequences like 'g g' or 'd d' that trigger
|
|
22
22
|
* when the full sequence is pressed within a timeout.
|
|
23
23
|
*
|
|
24
|
+
* Each step may include modifiers. You can chain the same modifier across
|
|
25
|
+
* steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
|
|
26
|
+
* Control, Alt, or Meta pressed alone) are ignored while matching—they do not
|
|
27
|
+
* advance the sequence or reset progress.
|
|
28
|
+
*
|
|
24
29
|
* @param sequence - Array of hotkey strings that form the sequence (or getter function)
|
|
25
30
|
* @param callback - Function to call when the sequence is completed
|
|
26
31
|
* @param options - Options for the sequence behavior (or getter function)
|
|
@@ -35,6 +40,7 @@ export interface InjectHotkeySequenceOptions extends Omit<
|
|
|
35
40
|
* injectHotkeySequence(['G', 'G'], () => this.lastSequence.set('gg → Go to top'))
|
|
36
41
|
* injectHotkeySequence(['D', 'D'], () => this.lastSequence.set('dd → Delete line'))
|
|
37
42
|
* injectHotkeySequence(['C', 'I', 'W'], () => this.lastSequence.set('ciw'), { timeout: 500 })
|
|
43
|
+
* injectHotkeySequence(['Shift+R', 'Shift+T'], () => this.lastSequence.set('⇧R⇧T'))
|
|
38
44
|
* }
|
|
39
45
|
* }
|
|
40
46
|
* ```
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DestroyRef,
|
|
3
|
+
computed,
|
|
4
|
+
effect,
|
|
5
|
+
inject,
|
|
6
|
+
linkedSignal,
|
|
7
|
+
untracked,
|
|
8
|
+
} from '@angular/core'
|
|
9
|
+
import { HotkeySequenceRecorder } from '@tanstack/hotkeys'
|
|
10
|
+
import { injectDefaultHotkeysOptions } from './hotkeys-provider'
|
|
11
|
+
import type { Atom, ReadonlyAtom } from '@tanstack/angular-store'
|
|
12
|
+
import type { Signal } from '@angular/core'
|
|
13
|
+
import type {
|
|
14
|
+
HotkeySequence,
|
|
15
|
+
HotkeySequenceRecorderOptions,
|
|
16
|
+
} from '@tanstack/hotkeys'
|
|
17
|
+
|
|
18
|
+
export interface AngularHotkeySequenceRecorder {
|
|
19
|
+
readonly isRecording: () => boolean
|
|
20
|
+
readonly steps: () => HotkeySequence
|
|
21
|
+
readonly recordedSequence: () => HotkeySequence | null
|
|
22
|
+
readonly startRecording: () => void
|
|
23
|
+
readonly stopRecording: () => void
|
|
24
|
+
readonly cancelRecording: () => void
|
|
25
|
+
readonly commitRecording: () => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Angular inject-based API for recording multi-chord sequences.
|
|
30
|
+
*/
|
|
31
|
+
export function injectHotkeySequenceRecorder(
|
|
32
|
+
options:
|
|
33
|
+
| HotkeySequenceRecorderOptions
|
|
34
|
+
| (() => HotkeySequenceRecorderOptions),
|
|
35
|
+
): AngularHotkeySequenceRecorder {
|
|
36
|
+
const defaultOptions = injectDefaultHotkeysOptions()
|
|
37
|
+
const destroyRef = inject(DestroyRef)
|
|
38
|
+
|
|
39
|
+
const recorderSignal = computed(() =>
|
|
40
|
+
untracked(() => {
|
|
41
|
+
const resolvedOptions =
|
|
42
|
+
typeof options === 'function' ? options() : options
|
|
43
|
+
|
|
44
|
+
const mergedOptions = {
|
|
45
|
+
...defaultOptions.hotkeySequenceRecorder,
|
|
46
|
+
...resolvedOptions,
|
|
47
|
+
} as HotkeySequenceRecorderOptions
|
|
48
|
+
|
|
49
|
+
return new HotkeySequenceRecorder(mergedOptions)
|
|
50
|
+
}),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
const recorderStore = computed(() => untracked(() => recorderSignal().store))
|
|
54
|
+
const isRecording = injectLazyStore(
|
|
55
|
+
recorderStore,
|
|
56
|
+
(state) => state.isRecording,
|
|
57
|
+
)
|
|
58
|
+
const steps = injectLazyStore(recorderStore, (state) => state.steps)
|
|
59
|
+
const recordedSequence = injectLazyStore(
|
|
60
|
+
recorderStore,
|
|
61
|
+
(state) => state.recordedSequence,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
effect(() => {
|
|
65
|
+
const resolved = typeof options === 'function' ? options() : options
|
|
66
|
+
recorderSignal().setOptions({
|
|
67
|
+
...defaultOptions.hotkeySequenceRecorder,
|
|
68
|
+
...resolved,
|
|
69
|
+
} as HotkeySequenceRecorderOptions)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
destroyRef.onDestroy(() => {
|
|
73
|
+
recorderSignal().destroy()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
isRecording,
|
|
78
|
+
steps,
|
|
79
|
+
recordedSequence,
|
|
80
|
+
startRecording: () => recorderSignal().start(),
|
|
81
|
+
stopRecording: () => recorderSignal().stop(),
|
|
82
|
+
cancelRecording: () => recorderSignal().cancel(),
|
|
83
|
+
commitRecording: () => recorderSignal().commit(),
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function injectLazyStore<TState, TSelected = NoInfer<TState>>(
|
|
88
|
+
storeSignal: Signal<Atom<TState> | ReadonlyAtom<TState>>,
|
|
89
|
+
selector: (state: NoInfer<TState>) => TSelected,
|
|
90
|
+
): Signal<TSelected> {
|
|
91
|
+
const slice = linkedSignal(() => selector(storeSignal().get()))
|
|
92
|
+
|
|
93
|
+
effect((onCleanup) => {
|
|
94
|
+
const currentStore = storeSignal()
|
|
95
|
+
slice.set(selector(currentStore.get()))
|
|
96
|
+
const { unsubscribe } = currentStore.subscribe((s) => {
|
|
97
|
+
slice.set(selector(s))
|
|
98
|
+
})
|
|
99
|
+
onCleanup(() => unsubscribe())
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return slice.asReadonly()
|
|
103
|
+
}
|