@tanstack/solid-hotkeys 0.6.0 → 0.8.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/README.md +1 -1
- package/dist/createHotkey.cjs +28 -10
- package/dist/createHotkey.cjs.map +1 -1
- package/dist/createHotkey.js +29 -11
- package/dist/createHotkey.js.map +1 -1
- package/dist/createHotkeySequence.cjs +27 -8
- package/dist/createHotkeySequence.cjs.map +1 -1
- package/dist/createHotkeySequence.js +28 -9
- package/dist/createHotkeySequence.js.map +1 -1
- package/dist/createHotkeySequences.cjs +110 -0
- package/dist/createHotkeySequences.cjs.map +1 -0
- package/dist/createHotkeySequences.d.cts +57 -0
- package/dist/createHotkeySequences.d.ts +57 -0
- package/dist/createHotkeySequences.js +110 -0
- package/dist/createHotkeySequences.js.map +1 -0
- package/dist/createHotkeys.cjs +1 -1
- package/dist/createHotkeys.cjs.map +1 -1
- package/dist/createHotkeys.js +2 -2
- package/dist/createHotkeys.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/package.json +2 -2
- package/src/createHotkey.ts +34 -21
- package/src/createHotkeySequence.ts +33 -16
- package/src/createHotkeySequences.ts +176 -0
- package/src/createHotkeys.ts +2 -8
- package/src/index.ts +1 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { createEffect, onCleanup } from 'solid-js'
|
|
2
|
+
import { formatHotkeySequence, getSequenceManager } from '@tanstack/hotkeys'
|
|
3
|
+
import { useDefaultHotkeysOptions } from './HotkeysProvider'
|
|
4
|
+
import type { CreateHotkeySequenceOptions } from './createHotkeySequence'
|
|
5
|
+
import type {
|
|
6
|
+
HotkeyCallback,
|
|
7
|
+
HotkeySequence,
|
|
8
|
+
SequenceRegistrationHandle,
|
|
9
|
+
} from '@tanstack/hotkeys'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A single sequence definition for use with `createHotkeySequences`.
|
|
13
|
+
*/
|
|
14
|
+
export interface CreateHotkeySequenceDefinition {
|
|
15
|
+
/** Array of hotkey strings that form the sequence */
|
|
16
|
+
sequence: 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?: CreateHotkeySequenceOptions
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* SolidJS primitive for registering multiple keyboard shortcut sequences at once (Vim-style).
|
|
25
|
+
*
|
|
26
|
+
* Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or accessors,
|
|
27
|
+
* so you can react to variable-length lists.
|
|
28
|
+
*
|
|
29
|
+
* Options are merged in this order:
|
|
30
|
+
* HotkeysProvider defaults < commonOptions < per-definition options
|
|
31
|
+
*
|
|
32
|
+
* Definitions with an empty `sequence` are skipped (no registration).
|
|
33
|
+
*
|
|
34
|
+
* @param sequences - Array of sequence definitions, or accessor returning them
|
|
35
|
+
* @param commonOptions - Shared options for all sequences, or accessor
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* function VimPalette() {
|
|
40
|
+
* createHotkeySequences([
|
|
41
|
+
* { sequence: ['G', 'G'], callback: () => scrollToTop() },
|
|
42
|
+
* { sequence: ['D', 'D'], callback: () => deleteLine() },
|
|
43
|
+
* ])
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* function Dynamic(props) {
|
|
50
|
+
* createHotkeySequences(
|
|
51
|
+
* () => props.items.map((item) => ({
|
|
52
|
+
* sequence: item.chords,
|
|
53
|
+
* callback: item.action,
|
|
54
|
+
* options: { enabled: item.enabled },
|
|
55
|
+
* })),
|
|
56
|
+
* { preventDefault: true },
|
|
57
|
+
* )
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function createHotkeySequences(
|
|
62
|
+
sequences:
|
|
63
|
+
| Array<CreateHotkeySequenceDefinition>
|
|
64
|
+
| (() => Array<CreateHotkeySequenceDefinition>),
|
|
65
|
+
commonOptions:
|
|
66
|
+
| CreateHotkeySequenceOptions
|
|
67
|
+
| (() => CreateHotkeySequenceOptions) = {},
|
|
68
|
+
): void {
|
|
69
|
+
type RegistrationRecord = {
|
|
70
|
+
handle: SequenceRegistrationHandle
|
|
71
|
+
target: Document | HTMLElement | Window
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const defaultOptions = useDefaultHotkeysOptions()
|
|
75
|
+
const manager = getSequenceManager()
|
|
76
|
+
|
|
77
|
+
const registrations = new Map<string, RegistrationRecord>()
|
|
78
|
+
|
|
79
|
+
onCleanup(() => {
|
|
80
|
+
for (const { handle } of registrations.values()) {
|
|
81
|
+
if (handle.isActive) {
|
|
82
|
+
handle.unregister()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
registrations.clear()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
createEffect(() => {
|
|
89
|
+
const resolved = typeof sequences === 'function' ? sequences() : sequences
|
|
90
|
+
const resolvedCommonOptions =
|
|
91
|
+
typeof commonOptions === 'function' ? commonOptions() : commonOptions
|
|
92
|
+
|
|
93
|
+
const nextKeys = new Set<string>()
|
|
94
|
+
const prepared: Array<{
|
|
95
|
+
registrationKey: string
|
|
96
|
+
def: CreateHotkeySequenceDefinition
|
|
97
|
+
mergedOptions: CreateHotkeySequenceOptions
|
|
98
|
+
sequenceString: string
|
|
99
|
+
resolvedTarget: Document | HTMLElement | Window
|
|
100
|
+
}> = []
|
|
101
|
+
|
|
102
|
+
for (let i = 0; i < resolved.length; i++) {
|
|
103
|
+
const def = resolved[i]!
|
|
104
|
+
const resolvedDefOptions = def.options ?? {}
|
|
105
|
+
|
|
106
|
+
const mergedOptions = {
|
|
107
|
+
...defaultOptions.hotkeySequence,
|
|
108
|
+
...resolvedCommonOptions,
|
|
109
|
+
...resolvedDefOptions,
|
|
110
|
+
} as CreateHotkeySequenceOptions
|
|
111
|
+
|
|
112
|
+
if (def.sequence.length === 0) {
|
|
113
|
+
continue
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const sequenceString = formatHotkeySequence(def.sequence)
|
|
117
|
+
|
|
118
|
+
const resolvedTarget =
|
|
119
|
+
'target' in mergedOptions
|
|
120
|
+
? (mergedOptions.target ?? null)
|
|
121
|
+
: typeof document !== 'undefined'
|
|
122
|
+
? document
|
|
123
|
+
: null
|
|
124
|
+
|
|
125
|
+
if (!resolvedTarget) {
|
|
126
|
+
continue
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const registrationKey = `${i}:${sequenceString}`
|
|
130
|
+
nextKeys.add(registrationKey)
|
|
131
|
+
prepared.push({
|
|
132
|
+
registrationKey,
|
|
133
|
+
def,
|
|
134
|
+
mergedOptions,
|
|
135
|
+
sequenceString,
|
|
136
|
+
resolvedTarget,
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (const [key, record] of [...registrations.entries()]) {
|
|
141
|
+
if (!nextKeys.has(key)) {
|
|
142
|
+
if (record.handle.isActive) {
|
|
143
|
+
record.handle.unregister()
|
|
144
|
+
}
|
|
145
|
+
registrations.delete(key)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for (const p of prepared) {
|
|
150
|
+
const existing = registrations.get(p.registrationKey)
|
|
151
|
+
if (existing?.handle.isActive && existing.target === p.resolvedTarget) {
|
|
152
|
+
existing.handle.callback = p.def.callback
|
|
153
|
+
const { target: _target, ...optionsWithoutTarget } = p.mergedOptions
|
|
154
|
+
existing.handle.setOptions(optionsWithoutTarget)
|
|
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 { target: _target, ...optionsWithoutTarget } = p.mergedOptions
|
|
166
|
+
const handle = manager.register(p.def.sequence, p.def.callback, {
|
|
167
|
+
...optionsWithoutTarget,
|
|
168
|
+
target: p.resolvedTarget,
|
|
169
|
+
})
|
|
170
|
+
registrations.set(p.registrationKey, {
|
|
171
|
+
handle,
|
|
172
|
+
target: p.resolvedTarget,
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
}
|
package/src/createHotkeys.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { createEffect, onCleanup } from 'solid-js'
|
|
2
2
|
import {
|
|
3
3
|
detectPlatform,
|
|
4
|
-
formatHotkey,
|
|
5
4
|
getHotkeyManager,
|
|
6
|
-
|
|
5
|
+
normalizeRegisterableHotkey,
|
|
7
6
|
} from '@tanstack/hotkeys'
|
|
8
7
|
import { useDefaultHotkeysOptions } from './HotkeysProvider'
|
|
9
8
|
import type { CreateHotkeyOptions } from './createHotkey'
|
|
@@ -118,12 +117,7 @@ export function createHotkeys(
|
|
|
118
117
|
} as CreateHotkeyOptions
|
|
119
118
|
|
|
120
119
|
const platform = mergedOptions.platform ?? detectPlatform()
|
|
121
|
-
const hotkeyString
|
|
122
|
-
typeof def.hotkey === 'string'
|
|
123
|
-
? def.hotkey
|
|
124
|
-
: (formatHotkey(
|
|
125
|
-
rawHotkeyToParsedHotkey(def.hotkey, platform),
|
|
126
|
-
) as Hotkey)
|
|
120
|
+
const hotkeyString = normalizeRegisterableHotkey(def.hotkey, platform)
|
|
127
121
|
|
|
128
122
|
const resolvedTarget =
|
|
129
123
|
'target' in mergedOptions
|
package/src/index.ts
CHANGED
|
@@ -11,5 +11,6 @@ export * from './createHeldKeys'
|
|
|
11
11
|
export * from './createHeldKeyCodes'
|
|
12
12
|
export * from './createKeyHold'
|
|
13
13
|
export * from './createHotkeySequence'
|
|
14
|
+
export * from './createHotkeySequences'
|
|
14
15
|
export * from './createHotkeyRecorder'
|
|
15
16
|
export * from './createHotkeySequenceRecorder'
|