@tanstack/angular-hotkeys 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/injectHotkey.cjs +36 -6
- package/dist/injectHotkey.cjs.map +1 -1
- package/dist/injectHotkey.d.cts +2 -0
- package/dist/injectHotkey.d.ts +2 -0
- package/dist/injectHotkey.js +37 -7
- package/dist/injectHotkey.js.map +1 -1
- package/dist/injectHotkeySequence.cjs +43 -11
- package/dist/injectHotkeySequence.cjs.map +1 -1
- package/dist/injectHotkeySequence.d.cts +9 -2
- package/dist/injectHotkeySequence.d.ts +9 -2
- package/dist/injectHotkeySequence.js +45 -13
- package/dist/injectHotkeySequence.js.map +1 -1
- package/dist/injectHotkeySequences.cjs +105 -0
- package/dist/injectHotkeySequences.cjs.map +1 -0
- package/dist/injectHotkeySequences.d.cts +47 -0
- package/dist/injectHotkeySequences.d.ts +47 -0
- package/dist/injectHotkeySequences.js +105 -0
- package/dist/injectHotkeySequences.js.map +1 -0
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/injectHotkey.ts +43 -11
- package/src/injectHotkeySequence.ts +66 -19
- package/src/injectHotkeySequences.ts +176 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { DestroyRef, effect, inject } from '@angular/core'
|
|
2
|
+
import { formatHotkeySequence, getSequenceManager } from '@tanstack/hotkeys'
|
|
3
|
+
import { injectDefaultHotkeysOptions } from './hotkeys-provider'
|
|
4
|
+
import type { InjectHotkeySequenceOptions } from './injectHotkeySequence'
|
|
5
|
+
import type {
|
|
6
|
+
HotkeyCallback,
|
|
7
|
+
HotkeySequence,
|
|
8
|
+
SequenceRegistrationHandle,
|
|
9
|
+
} from '@tanstack/hotkeys'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A single sequence definition for use with `injectHotkeySequences`.
|
|
13
|
+
*/
|
|
14
|
+
export interface InjectHotkeySequenceDefinition {
|
|
15
|
+
/** Array of hotkey strings that form the sequence */
|
|
16
|
+
sequence: HotkeySequence | (() => HotkeySequence)
|
|
17
|
+
/** The function to call when the sequence is completed */
|
|
18
|
+
callback: HotkeyCallback
|
|
19
|
+
/** Per-sequence options (merged on top of commonOptions) */
|
|
20
|
+
options?: InjectHotkeySequenceOptions | (() => InjectHotkeySequenceOptions)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Angular inject-based API for registering multiple keyboard shortcut sequences at once (Vim-style).
|
|
25
|
+
*
|
|
26
|
+
* Uses the singleton SequenceManager. Call in an injection context (e.g. constructor).
|
|
27
|
+
* Uses `effect()` to track reactive dependencies when definitions or options are getters.
|
|
28
|
+
*
|
|
29
|
+
* Options are merged in this order:
|
|
30
|
+
* provideHotkeys defaults < commonOptions < per-definition options
|
|
31
|
+
*
|
|
32
|
+
* Definitions with an empty `sequence` are skipped. Disabled sequences (`enabled: false`)
|
|
33
|
+
* remain registered so they stay visible in devtools; the core manager suppresses execution.
|
|
34
|
+
*
|
|
35
|
+
* @param sequences - Array of sequence definitions, or getter returning them
|
|
36
|
+
* @param commonOptions - Shared options for all sequences, or getter
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* @Component({ ... })
|
|
41
|
+
* export class VimComponent {
|
|
42
|
+
* constructor() {
|
|
43
|
+
* injectHotkeySequences([
|
|
44
|
+
* { sequence: ['G', 'G'], callback: () => this.goTop() },
|
|
45
|
+
* { sequence: ['D', 'D'], callback: () => this.deleteLine() },
|
|
46
|
+
* ])
|
|
47
|
+
* }
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function injectHotkeySequences(
|
|
52
|
+
sequences:
|
|
53
|
+
| Array<InjectHotkeySequenceDefinition>
|
|
54
|
+
| (() => Array<InjectHotkeySequenceDefinition>),
|
|
55
|
+
commonOptions:
|
|
56
|
+
| InjectHotkeySequenceOptions
|
|
57
|
+
| (() => InjectHotkeySequenceOptions) = {},
|
|
58
|
+
): void {
|
|
59
|
+
type RegistrationRecord = {
|
|
60
|
+
handle: SequenceRegistrationHandle
|
|
61
|
+
target: Document | HTMLElement | Window
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const defaultOptions = injectDefaultHotkeysOptions()
|
|
65
|
+
const manager = getSequenceManager()
|
|
66
|
+
const destroyRef = inject(DestroyRef)
|
|
67
|
+
|
|
68
|
+
const registrations = new Map<string, RegistrationRecord>()
|
|
69
|
+
|
|
70
|
+
destroyRef.onDestroy(() => {
|
|
71
|
+
for (const { handle } of registrations.values()) {
|
|
72
|
+
if (handle.isActive) {
|
|
73
|
+
handle.unregister()
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
registrations.clear()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
effect(() => {
|
|
80
|
+
const resolvedSequences =
|
|
81
|
+
typeof sequences === 'function' ? sequences() : sequences
|
|
82
|
+
const resolvedCommonOptions =
|
|
83
|
+
typeof commonOptions === 'function' ? commonOptions() : commonOptions
|
|
84
|
+
|
|
85
|
+
const nextKeys = new Set<string>()
|
|
86
|
+
const prepared: Array<{
|
|
87
|
+
registrationKey: string
|
|
88
|
+
def: InjectHotkeySequenceDefinition
|
|
89
|
+
resolvedSequence: HotkeySequence
|
|
90
|
+
enabled: boolean
|
|
91
|
+
sequenceOpts: Omit<InjectHotkeySequenceOptions, 'enabled'>
|
|
92
|
+
resolvedTarget: Document | HTMLElement | Window
|
|
93
|
+
}> = []
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < resolvedSequences.length; i++) {
|
|
96
|
+
const def = resolvedSequences[i]!
|
|
97
|
+
const resolvedSequence =
|
|
98
|
+
typeof def.sequence === 'function' ? def.sequence() : def.sequence
|
|
99
|
+
const resolvedDefOptions =
|
|
100
|
+
typeof def.options === 'function' ? def.options() : (def.options ?? {})
|
|
101
|
+
|
|
102
|
+
const mergedOptions = {
|
|
103
|
+
...defaultOptions.hotkeySequence,
|
|
104
|
+
...resolvedCommonOptions,
|
|
105
|
+
...resolvedDefOptions,
|
|
106
|
+
} as InjectHotkeySequenceOptions
|
|
107
|
+
|
|
108
|
+
const { enabled = true, ...sequenceOpts } = mergedOptions
|
|
109
|
+
|
|
110
|
+
if (resolvedSequence.length === 0) {
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const resolvedTarget =
|
|
115
|
+
'target' in sequenceOpts
|
|
116
|
+
? (sequenceOpts.target ?? null)
|
|
117
|
+
: typeof document !== 'undefined'
|
|
118
|
+
? document
|
|
119
|
+
: null
|
|
120
|
+
|
|
121
|
+
if (!resolvedTarget) {
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const registrationKey = `${i}:${formatHotkeySequence(resolvedSequence)}`
|
|
126
|
+
nextKeys.add(registrationKey)
|
|
127
|
+
prepared.push({
|
|
128
|
+
registrationKey,
|
|
129
|
+
def,
|
|
130
|
+
resolvedSequence,
|
|
131
|
+
enabled,
|
|
132
|
+
sequenceOpts,
|
|
133
|
+
resolvedTarget,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const [key, record] of [...registrations.entries()]) {
|
|
138
|
+
if (!nextKeys.has(key)) {
|
|
139
|
+
if (record.handle.isActive) {
|
|
140
|
+
record.handle.unregister()
|
|
141
|
+
}
|
|
142
|
+
registrations.delete(key)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (const p of prepared) {
|
|
147
|
+
const existing = registrations.get(p.registrationKey)
|
|
148
|
+
if (existing?.handle.isActive && existing.target === p.resolvedTarget) {
|
|
149
|
+
existing.handle.callback = p.def.callback
|
|
150
|
+
const { target: _target, ...optionsWithoutTarget } = p.sequenceOpts
|
|
151
|
+
existing.handle.setOptions({
|
|
152
|
+
...optionsWithoutTarget,
|
|
153
|
+
enabled: p.enabled,
|
|
154
|
+
})
|
|
155
|
+
continue
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (existing) {
|
|
159
|
+
if (existing.handle.isActive) {
|
|
160
|
+
existing.handle.unregister()
|
|
161
|
+
}
|
|
162
|
+
registrations.delete(p.registrationKey)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const handle = manager.register(p.resolvedSequence, p.def.callback, {
|
|
166
|
+
...p.sequenceOpts,
|
|
167
|
+
enabled: p.enabled,
|
|
168
|
+
target: p.resolvedTarget,
|
|
169
|
+
})
|
|
170
|
+
registrations.set(p.registrationKey, {
|
|
171
|
+
handle,
|
|
172
|
+
target: p.resolvedTarget,
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
}
|