@tanstack/preact-hotkeys 0.9.1 → 0.11.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 +15 -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/useHeldKeyCodes.cjs +1 -1
- package/dist/useHeldKeyCodes.cjs.map +1 -1
- package/dist/useHeldKeyCodes.js +2 -2
- package/dist/useHeldKeyCodes.js.map +1 -1
- package/dist/useHeldKeys.cjs +2 -2
- package/dist/useHeldKeys.cjs.map +1 -1
- package/dist/useHeldKeys.d.cts +1 -1
- package/dist/useHeldKeys.d.ts +1 -1
- package/dist/useHeldKeys.js +3 -3
- package/dist/useHeldKeys.js.map +1 -1
- package/dist/useHotkey.cjs +9 -5
- package/dist/useHotkey.cjs.map +1 -1
- package/dist/useHotkey.js +9 -5
- package/dist/useHotkey.js.map +1 -1
- package/dist/useHotkeyHint.cjs +18 -0
- package/dist/useHotkeyHint.cjs.map +1 -0
- package/dist/useHotkeyHint.d.cts +14 -0
- package/dist/useHotkeyHint.d.ts +14 -0
- package/dist/useHotkeyHint.js +18 -0
- package/dist/useHotkeyHint.js.map +1 -0
- package/dist/useHotkeyRecorder.cjs +2 -2
- package/dist/useHotkeyRecorder.cjs.map +1 -1
- package/dist/useHotkeyRecorder.js +3 -3
- package/dist/useHotkeyRecorder.js.map +1 -1
- package/dist/useHotkeyRegistrations.cjs +5 -2
- package/dist/useHotkeyRegistrations.cjs.map +1 -1
- package/dist/useHotkeyRegistrations.js +6 -3
- package/dist/useHotkeyRegistrations.js.map +1 -1
- package/dist/useHotkeySequence.cjs +9 -5
- package/dist/useHotkeySequence.cjs.map +1 -1
- package/dist/useHotkeySequence.js +9 -5
- package/dist/useHotkeySequence.js.map +1 -1
- package/dist/useHotkeySequenceRecorder.cjs +3 -3
- package/dist/useHotkeySequenceRecorder.cjs.map +1 -1
- package/dist/useHotkeySequenceRecorder.js +4 -4
- package/dist/useHotkeySequenceRecorder.js.map +1 -1
- package/dist/useHotkeySequences.cjs +3 -15
- package/dist/useHotkeySequences.cjs.map +1 -1
- package/dist/useHotkeySequences.js +3 -15
- package/dist/useHotkeySequences.js.map +1 -1
- package/dist/useHotkeys.cjs +3 -15
- package/dist/useHotkeys.cjs.map +1 -1
- package/dist/useHotkeys.js +3 -15
- package/dist/useHotkeys.js.map +1 -1
- package/dist/useKeyHold.cjs +2 -2
- package/dist/useKeyHold.cjs.map +1 -1
- package/dist/useKeyHold.d.cts +1 -1
- package/dist/useKeyHold.d.ts +1 -1
- package/dist/useKeyHold.js +3 -3
- package/dist/useKeyHold.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +1 -0
- package/src/useHeldKeyCodes.ts +2 -2
- package/src/useHeldKeys.ts +3 -3
- package/src/useHotkey.ts +11 -8
- package/src/useHotkeyHint.ts +19 -0
- package/src/useHotkeyRecorder.ts +4 -4
- package/src/useHotkeyRegistrations.ts +12 -6
- package/src/useHotkeySequence.ts +14 -10
- package/src/useHotkeySequenceRecorder.ts +4 -4
- package/src/useHotkeySequences.ts +4 -18
- package/src/useHotkeys.ts +4 -18
- package/src/useKeyHold.ts +3 -3
package/src/useHeldKeyCodes.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useSelector } from '@tanstack/preact-store'
|
|
2
2
|
import { getKeyStateTracker } from '@tanstack/hotkeys'
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -29,5 +29,5 @@ import { getKeyStateTracker } from '@tanstack/hotkeys'
|
|
|
29
29
|
*/
|
|
30
30
|
export function useHeldKeyCodes(): Record<string, string> {
|
|
31
31
|
const tracker = getKeyStateTracker()
|
|
32
|
-
return
|
|
32
|
+
return useSelector(tracker.store, (state) => state.heldCodes)
|
|
33
33
|
}
|
package/src/useHeldKeys.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useSelector } from '@tanstack/preact-store'
|
|
2
2
|
import { getKeyStateTracker } from '@tanstack/hotkeys'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Preact hook that returns an array of currently held keyboard keys.
|
|
6
6
|
*
|
|
7
|
-
* This hook uses `
|
|
7
|
+
* This hook uses `useSelector` from `@tanstack/preact-store` to subscribe
|
|
8
8
|
* to the global KeyStateTracker and updates whenever keys are pressed
|
|
9
9
|
* or released.
|
|
10
10
|
*
|
|
@@ -25,5 +25,5 @@ import { getKeyStateTracker } from '@tanstack/hotkeys'
|
|
|
25
25
|
*/
|
|
26
26
|
export function useHeldKeys(): Array<string> {
|
|
27
27
|
const tracker = getKeyStateTracker()
|
|
28
|
-
return
|
|
28
|
+
return useSelector(tracker.store, (state) => state.heldKeys)
|
|
29
29
|
}
|
package/src/useHotkey.ts
CHANGED
|
@@ -169,7 +169,9 @@ export function useHotkey(
|
|
|
169
169
|
// Update tracking refs
|
|
170
170
|
prevTargetRef.current = resolvedTarget
|
|
171
171
|
prevHotkeyRef.current = hotkeyString
|
|
172
|
+
})
|
|
172
173
|
|
|
174
|
+
useEffect(() => {
|
|
173
175
|
// Cleanup on unmount
|
|
174
176
|
return () => {
|
|
175
177
|
if (registrationRef.current?.isActive) {
|
|
@@ -177,12 +179,13 @@ export function useHotkey(
|
|
|
177
179
|
registrationRef.current = null
|
|
178
180
|
}
|
|
179
181
|
}
|
|
180
|
-
}, [
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
182
|
+
}, [])
|
|
183
|
+
|
|
184
|
+
// Publish option changes after commit, never during render.
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
if (registrationRef.current?.isActive) {
|
|
187
|
+
registrationRef.current.callback = callback
|
|
188
|
+
registrationRef.current.setOptions(optionsWithoutTarget)
|
|
189
|
+
}
|
|
190
|
+
})
|
|
188
191
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useSelector } from '@tanstack/preact-store'
|
|
2
|
+
import { getKeyStateTracker, matchesHeldModifiers } from '@tanstack/hotkeys'
|
|
3
|
+
import type { HeldModifierOptions, RegisterableHotkey } from '@tanstack/hotkeys'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns a boolean indicating whether held modifiers reveal this shortcut.
|
|
7
|
+
* Only visibility changes from held-key updates cause a rerender.
|
|
8
|
+
* Uses the nonempty-subset/AltGr rules of `matchesHeldModifiers`; `exact` requires
|
|
9
|
+
* every modifier. Combine with the action's enabled state before showing a badge.
|
|
10
|
+
* This helper does not register a shortcut or check whether its target is focused.
|
|
11
|
+
*/
|
|
12
|
+
export function useHotkeyHint(
|
|
13
|
+
hotkey: RegisterableHotkey,
|
|
14
|
+
options: HeldModifierOptions = {},
|
|
15
|
+
): boolean {
|
|
16
|
+
return useSelector(getKeyStateTracker().store, (state) =>
|
|
17
|
+
matchesHeldModifiers(hotkey, state.heldKeys, options),
|
|
18
|
+
)
|
|
19
|
+
}
|
package/src/useHotkeyRecorder.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'preact/hooks'
|
|
2
|
-
import {
|
|
2
|
+
import { useSelector } from '@tanstack/preact-store'
|
|
3
3
|
import { HotkeyRecorder } from '@tanstack/hotkeys'
|
|
4
4
|
import { useDefaultHotkeysOptions } from './HotkeysProvider'
|
|
5
5
|
import type { Hotkey, HotkeyRecorderOptions } from '@tanstack/hotkeys'
|
|
@@ -74,12 +74,12 @@ export function useHotkeyRecorder(
|
|
|
74
74
|
// This ensures callbacks always have access to latest values
|
|
75
75
|
recorderRef.current.setOptions(mergedOptions)
|
|
76
76
|
|
|
77
|
-
// Subscribe to recorder state using
|
|
78
|
-
const isRecording =
|
|
77
|
+
// Subscribe to recorder state using useSelector (same pattern as useHeldKeys)
|
|
78
|
+
const isRecording = useSelector(
|
|
79
79
|
recorderRef.current.store,
|
|
80
80
|
(state) => state.isRecording,
|
|
81
81
|
)
|
|
82
|
-
const recordedHotkey =
|
|
82
|
+
const recordedHotkey = useSelector(
|
|
83
83
|
recorderRef.current.store,
|
|
84
84
|
(state) => state.recordedHotkey,
|
|
85
85
|
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useMemo } from 'preact/hooks'
|
|
2
|
+
import { useSelector } from '@tanstack/preact-store'
|
|
2
3
|
import {
|
|
3
4
|
getHotkeyManager,
|
|
4
5
|
getSequenceManager,
|
|
@@ -33,12 +34,17 @@ export function useHotkeyRegistrations(): HotkeyRegistrationsResult {
|
|
|
33
34
|
const hotkeyManager = getHotkeyManager()
|
|
34
35
|
const sequenceManager = getSequenceManager()
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
// Select stable store snapshots before deriving arrays; fresh selector arrays
|
|
38
|
+
// make Preact's external-store subscription render continuously.
|
|
39
|
+
const hotkeyState = useSelector(hotkeyManager.registrations)
|
|
40
|
+
const sequenceState = useSelector(sequenceManager.registrations)
|
|
41
|
+
const hotkeys = useMemo(
|
|
42
|
+
() => Array.from(hotkeyState.values()).map(toHotkeyRegistrationView),
|
|
43
|
+
[hotkeyState],
|
|
38
44
|
)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
const sequences = useMemo(
|
|
46
|
+
() => Array.from(sequenceState.values()),
|
|
47
|
+
[sequenceState],
|
|
42
48
|
)
|
|
43
49
|
|
|
44
50
|
return { hotkeys, sequences }
|
package/src/useHotkeySequence.ts
CHANGED
|
@@ -167,7 +167,9 @@ export function useHotkeySequence(
|
|
|
167
167
|
// Update tracking refs
|
|
168
168
|
prevTargetRef.current = resolvedTarget
|
|
169
169
|
prevSequenceRef.current = hotkeySequenceString
|
|
170
|
+
})
|
|
170
171
|
|
|
172
|
+
useEffect(() => {
|
|
171
173
|
// Cleanup on unmount
|
|
172
174
|
return () => {
|
|
173
175
|
if (registrationRef.current?.isActive) {
|
|
@@ -175,14 +177,16 @@ export function useHotkeySequence(
|
|
|
175
177
|
registrationRef.current = null
|
|
176
178
|
}
|
|
177
179
|
}
|
|
178
|
-
}, [
|
|
179
|
-
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
registrationRef.current
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
180
|
+
}, [])
|
|
181
|
+
|
|
182
|
+
// Publish option changes after commit, never during render.
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
if (registrationRef.current?.isActive) {
|
|
185
|
+
registrationRef.current.callback = (
|
|
186
|
+
event: KeyboardEvent,
|
|
187
|
+
context: HotkeyCallbackContext,
|
|
188
|
+
) => callbackRef.current(event, context)
|
|
189
|
+
registrationRef.current.setOptions(optionsWithoutTarget)
|
|
190
|
+
}
|
|
191
|
+
})
|
|
188
192
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'preact/hooks'
|
|
2
|
-
import {
|
|
2
|
+
import { useSelector } from '@tanstack/preact-store'
|
|
3
3
|
import { HotkeySequenceRecorder } from '@tanstack/hotkeys'
|
|
4
4
|
import { useDefaultHotkeysOptions } from './HotkeysProvider'
|
|
5
5
|
import type {
|
|
@@ -36,12 +36,12 @@ export function useHotkeySequenceRecorder(
|
|
|
36
36
|
|
|
37
37
|
recorderRef.current.setOptions(mergedOptions)
|
|
38
38
|
|
|
39
|
-
const isRecording =
|
|
39
|
+
const isRecording = useSelector(
|
|
40
40
|
recorderRef.current.store,
|
|
41
41
|
(state) => state.isRecording,
|
|
42
42
|
)
|
|
43
|
-
const steps =
|
|
44
|
-
const recordedSequence =
|
|
43
|
+
const steps = useSelector(recorderRef.current.store, (state) => state.steps)
|
|
44
|
+
const recordedSequence = useSelector(
|
|
45
45
|
recorderRef.current.store,
|
|
46
46
|
(state) => state.recordedSequence,
|
|
47
47
|
)
|
|
@@ -154,6 +154,10 @@ export function useHotkeySequences(
|
|
|
154
154
|
|
|
155
155
|
const existing = prevRegistrations.get(registrationKey)
|
|
156
156
|
if (existing?.handle.isActive && existing.target === resolvedTarget) {
|
|
157
|
+
// Publish callback/options only after commit, never during render.
|
|
158
|
+
existing.handle.callback = def.callback
|
|
159
|
+
const { target: _target, ...optionsWithoutTarget } = mergedOptions
|
|
160
|
+
existing.handle.setOptions(optionsWithoutTarget)
|
|
157
161
|
nextRegistrations.set(registrationKey, existing)
|
|
158
162
|
continue
|
|
159
163
|
}
|
|
@@ -185,22 +189,4 @@ export function useHotkeySequences(
|
|
|
185
189
|
registrationsRef.current = new Map()
|
|
186
190
|
}
|
|
187
191
|
}, [])
|
|
188
|
-
|
|
189
|
-
for (let i = 0; i < definitions.length; i++) {
|
|
190
|
-
const def = definitions[i]!
|
|
191
|
-
const seqStr = sequenceStrings[i]!
|
|
192
|
-
const registrationKey = `${i}:${seqStr}`
|
|
193
|
-
const handle = registrationsRef.current.get(registrationKey)?.handle
|
|
194
|
-
|
|
195
|
-
if (handle?.isActive && def.sequence.length > 0) {
|
|
196
|
-
handle.callback = def.callback
|
|
197
|
-
const mergedOptions = {
|
|
198
|
-
...defaultOptions,
|
|
199
|
-
...commonOptions,
|
|
200
|
-
...def.options,
|
|
201
|
-
} as UseHotkeySequenceOptions
|
|
202
|
-
const { target: _target, ...optionsWithoutTarget } = mergedOptions
|
|
203
|
-
handle.setOptions(optionsWithoutTarget)
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
192
|
}
|
package/src/useHotkeys.ts
CHANGED
|
@@ -155,6 +155,10 @@ export function useHotkeys(
|
|
|
155
155
|
|
|
156
156
|
const existing = prevRegistrations.get(registrationKey)
|
|
157
157
|
if (existing?.handle.isActive && existing.target === resolvedTarget) {
|
|
158
|
+
// Publish callback/options only after commit, never during render.
|
|
159
|
+
existing.handle.callback = def.callback
|
|
160
|
+
const { target: _target, ...optionsWithoutTarget } = mergedOptions
|
|
161
|
+
existing.handle.setOptions(optionsWithoutTarget)
|
|
158
162
|
nextRegistrations.set(registrationKey, existing)
|
|
159
163
|
continue
|
|
160
164
|
}
|
|
@@ -186,22 +190,4 @@ export function useHotkeys(
|
|
|
186
190
|
registrationsRef.current = new Map()
|
|
187
191
|
}
|
|
188
192
|
}, [])
|
|
189
|
-
|
|
190
|
-
for (let i = 0; i < hotkeys.length; i++) {
|
|
191
|
-
const def = hotkeys[i]!
|
|
192
|
-
const hotkeyStr = hotkeyStrings[i]!
|
|
193
|
-
const registrationKey = `${i}:${hotkeyStr}`
|
|
194
|
-
const handle = registrationsRef.current.get(registrationKey)?.handle
|
|
195
|
-
|
|
196
|
-
if (handle?.isActive) {
|
|
197
|
-
handle.callback = def.callback
|
|
198
|
-
const mergedOptions = {
|
|
199
|
-
...defaultOptions,
|
|
200
|
-
...commonOptions,
|
|
201
|
-
...def.options,
|
|
202
|
-
} as UseHotkeyOptions
|
|
203
|
-
const { target: _target, ...optionsWithoutTarget } = mergedOptions
|
|
204
|
-
handle.setOptions(optionsWithoutTarget)
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
193
|
}
|
package/src/useKeyHold.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useSelector } from '@tanstack/preact-store'
|
|
2
2
|
import { getKeyStateTracker } from '@tanstack/hotkeys'
|
|
3
3
|
import type { IndividualKey } from '@tanstack/hotkeys'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Preact hook that returns whether a specific key is currently being held.
|
|
7
7
|
*
|
|
8
|
-
* This hook uses `
|
|
8
|
+
* This hook uses `useSelector` from `@tanstack/preact-store` to subscribe
|
|
9
9
|
* to the global KeyStateTracker and uses a selector to determine if
|
|
10
10
|
* the specified key is held.
|
|
11
11
|
*
|
|
@@ -46,7 +46,7 @@ export function useKeyHold(key: IndividualKey): boolean {
|
|
|
46
46
|
const tracker = getKeyStateTracker()
|
|
47
47
|
const normalizedKey = key.toLowerCase()
|
|
48
48
|
|
|
49
|
-
return
|
|
49
|
+
return useSelector(tracker.store, (state) =>
|
|
50
50
|
state.heldKeys.some((heldKey) => heldKey.toLowerCase() === normalizedKey),
|
|
51
51
|
)
|
|
52
52
|
}
|