@tanstack/vue-hotkeys 0.0.1

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.
Files changed (64) hide show
  1. package/README.md +122 -0
  2. package/dist/HotkeysProvider.cjs +31 -0
  3. package/dist/HotkeysProvider.cjs.map +1 -0
  4. package/dist/HotkeysProvider.d.cts +34 -0
  5. package/dist/HotkeysProvider.d.ts +34 -0
  6. package/dist/HotkeysProvider.js +31 -0
  7. package/dist/HotkeysProvider.js.map +1 -0
  8. package/dist/HotkeysProviderContext.cjs +20 -0
  9. package/dist/HotkeysProviderContext.cjs.map +1 -0
  10. package/dist/HotkeysProviderContext.d.cts +19 -0
  11. package/dist/HotkeysProviderContext.d.ts +19 -0
  12. package/dist/HotkeysProviderContext.js +18 -0
  13. package/dist/HotkeysProviderContext.js.map +1 -0
  14. package/dist/index.cjs +27 -0
  15. package/dist/index.d.cts +10 -0
  16. package/dist/index.d.ts +10 -0
  17. package/dist/index.js +12 -0
  18. package/dist/useHeldKeyCodes.cjs +37 -0
  19. package/dist/useHeldKeyCodes.cjs.map +1 -0
  20. package/dist/useHeldKeyCodes.d.cts +33 -0
  21. package/dist/useHeldKeyCodes.d.ts +33 -0
  22. package/dist/useHeldKeyCodes.js +37 -0
  23. package/dist/useHeldKeyCodes.js.map +1 -0
  24. package/dist/useHeldKeys.cjs +35 -0
  25. package/dist/useHeldKeys.cjs.map +1 -0
  26. package/dist/useHeldKeys.d.cts +31 -0
  27. package/dist/useHeldKeys.d.ts +31 -0
  28. package/dist/useHeldKeys.js +35 -0
  29. package/dist/useHeldKeys.js.map +1 -0
  30. package/dist/useHotkey.cjs +124 -0
  31. package/dist/useHotkey.cjs.map +1 -0
  32. package/dist/useHotkey.d.cts +86 -0
  33. package/dist/useHotkey.d.ts +86 -0
  34. package/dist/useHotkey.js +124 -0
  35. package/dist/useHotkey.js.map +1 -0
  36. package/dist/useHotkeyRecorder.cjs +80 -0
  37. package/dist/useHotkeyRecorder.cjs.map +1 -0
  38. package/dist/useHotkeyRecorder.d.cts +61 -0
  39. package/dist/useHotkeyRecorder.d.ts +61 -0
  40. package/dist/useHotkeyRecorder.js +80 -0
  41. package/dist/useHotkeyRecorder.js.map +1 -0
  42. package/dist/useHotkeySequence.cjs +95 -0
  43. package/dist/useHotkeySequence.cjs.map +1 -0
  44. package/dist/useHotkeySequence.d.cts +58 -0
  45. package/dist/useHotkeySequence.d.ts +58 -0
  46. package/dist/useHotkeySequence.js +95 -0
  47. package/dist/useHotkeySequence.js.map +1 -0
  48. package/dist/useKeyHold.cjs +60 -0
  49. package/dist/useKeyHold.cjs.map +1 -0
  50. package/dist/useKeyHold.d.cts +52 -0
  51. package/dist/useKeyHold.d.ts +52 -0
  52. package/dist/useKeyHold.js +60 -0
  53. package/dist/useKeyHold.js.map +1 -0
  54. package/package.json +66 -0
  55. package/src/HotkeysProvider.tsx +29 -0
  56. package/src/HotkeysProviderContext.ts +37 -0
  57. package/src/index.ts +14 -0
  58. package/src/useHeldKeyCodes.ts +34 -0
  59. package/src/useHeldKeys.ts +32 -0
  60. package/src/useHotkey.ts +197 -0
  61. package/src/useHotkeyRecorder.ts +112 -0
  62. package/src/useHotkeySequence.ts +158 -0
  63. package/src/useKeyHold.ts +65 -0
  64. package/src/vue.d.ts +6 -0
@@ -0,0 +1,158 @@
1
+ import { onUnmounted, unref, watch } from 'vue'
2
+ import { getSequenceManager } from '@tanstack/hotkeys'
3
+ import { useDefaultHotkeysOptions } from './HotkeysProviderContext'
4
+ import type { MaybeRefOrGetter } from 'vue'
5
+ import type {
6
+ HotkeyCallback,
7
+ HotkeySequence,
8
+ SequenceOptions,
9
+ SequenceRegistrationHandle,
10
+ } from '@tanstack/hotkeys'
11
+
12
+ export interface UseHotkeySequenceOptions extends Omit<
13
+ SequenceOptions,
14
+ 'enabled' | 'target'
15
+ > {
16
+ /**
17
+ * Whether the sequence is active.
18
+ * Can be a Ref, a getter function, or a boolean value.
19
+ * Defaults to true.
20
+ */
21
+ enabled?: MaybeRefOrGetter<boolean>
22
+ /**
23
+ * The DOM element to attach the event listener to.
24
+ * Can be a Ref, a getter function, direct DOM element, or null.
25
+ * Defaults to document.
26
+ */
27
+ target?: MaybeRefOrGetter<HTMLElement | Document | Window | null>
28
+ }
29
+
30
+ /**
31
+ * Vue composable for registering a keyboard shortcut sequence (Vim-style).
32
+ *
33
+ * This composable allows you to register multi-key sequences like 'g g' or 'd d'
34
+ * that trigger when the full sequence is pressed within a timeout.
35
+ *
36
+ * @param sequence - Array of hotkey strings that form the sequence
37
+ * @param callback - Function to call when the sequence is completed
38
+ * @param options - Options for the sequence behavior
39
+ *
40
+ * @example
41
+ * ```vue
42
+ * <script setup>
43
+ * import { useHotkeySequence } from '@tanstack/vue-hotkeys'
44
+ *
45
+ * // 'g g' to go to top
46
+ * useHotkeySequence(['G', 'G'], () => {
47
+ * scrollToTop()
48
+ * })
49
+ *
50
+ * // 'd d' to delete line
51
+ * useHotkeySequence(['D', 'D'], () => {
52
+ * deleteLine()
53
+ * })
54
+ *
55
+ * // 'd i w' to delete inner word
56
+ * useHotkeySequence(['D', 'I', 'W'], () => {
57
+ * deleteInnerWord()
58
+ * }, { timeout: 500 })
59
+ * </script>
60
+ *
61
+ * <template>
62
+ * <div>...</div>
63
+ * </template>
64
+ * ```
65
+ */
66
+ export function useHotkeySequence(
67
+ sequence: MaybeRefOrGetter<HotkeySequence>,
68
+ callback: HotkeyCallback,
69
+ options: MaybeRefOrGetter<UseHotkeySequenceOptions> = {},
70
+ ): void {
71
+ const defaultOptions = useDefaultHotkeysOptions()
72
+ const manager = getSequenceManager()
73
+
74
+ let registration: SequenceRegistrationHandle | null = null
75
+
76
+ // Watch for changes to reactive dependencies
77
+ const stopWatcher = watch(
78
+ () => {
79
+ const resolvedSequence = resolveMaybeRefOrGetter(sequence)
80
+ const resolvedOptions = resolveMaybeRefOrGetter(options)
81
+ const mergedOptions = {
82
+ ...defaultOptions.hotkeySequence,
83
+ ...resolvedOptions,
84
+ } as UseHotkeySequenceOptions
85
+ const resolvedEnabled =
86
+ mergedOptions.enabled === undefined
87
+ ? undefined
88
+ : resolveMaybeRefOrGetter(mergedOptions.enabled)
89
+ const resolvedTarget =
90
+ mergedOptions.target === undefined
91
+ ? undefined
92
+ : resolveMaybeRefOrGetter(mergedOptions.target)
93
+
94
+ return {
95
+ resolvedSequence,
96
+ mergedOptions,
97
+ resolvedEnabled,
98
+ resolvedTarget,
99
+ }
100
+ },
101
+ ({ resolvedSequence, mergedOptions, resolvedEnabled, resolvedTarget }) => {
102
+ if (resolvedEnabled === false || resolvedSequence.length === 0) {
103
+ return
104
+ }
105
+
106
+ // Resolve target
107
+ const finalTarget =
108
+ resolvedTarget ?? (typeof document !== 'undefined' ? document : null)
109
+
110
+ if (!finalTarget) {
111
+ return
112
+ }
113
+
114
+ // Unregister previous registration if it exists
115
+ if (registration?.isActive) {
116
+ registration.unregister()
117
+ registration = null
118
+ }
119
+
120
+ // Extract options without target (target is handled separately)
121
+ const {
122
+ target: _target,
123
+ enabled: _enabled,
124
+ ...restOptions
125
+ } = mergedOptions
126
+ const optionsWithoutTarget = {
127
+ ...restOptions,
128
+ ...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),
129
+ }
130
+
131
+ // Register the sequence
132
+ registration = manager.register(resolvedSequence, callback, {
133
+ ...optionsWithoutTarget,
134
+ target: finalTarget,
135
+ })
136
+
137
+ // Update callback and options
138
+ if (registration.isActive) {
139
+ registration.callback = callback
140
+ registration.setOptions(optionsWithoutTarget)
141
+ }
142
+ },
143
+ { immediate: true },
144
+ )
145
+
146
+ // Cleanup on unmount
147
+ onUnmounted(() => {
148
+ stopWatcher()
149
+ if (registration?.isActive) {
150
+ registration.unregister()
151
+ registration = null
152
+ }
153
+ })
154
+ }
155
+
156
+ function resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {
157
+ return typeof value === 'function' ? (value as () => T)() : unref(value)
158
+ }
@@ -0,0 +1,65 @@
1
+ import { useStore } from '@tanstack/vue-store'
2
+ import { getKeyStateTracker } from '@tanstack/hotkeys'
3
+ import { unref } from 'vue'
4
+ import type { MaybeRefOrGetter, Ref } from 'vue'
5
+ import type { HeldKey } from '@tanstack/hotkeys'
6
+
7
+ /**
8
+ * Vue composable that returns a reactive ref indicating whether a specific key is currently being held.
9
+ *
10
+ * This composable uses `useStore` from `@tanstack/vue-store` to subscribe
11
+ * to the global KeyStateTracker and uses a selector to determine if
12
+ * the specified key is held.
13
+ *
14
+ * @param key - The key to check (e.g., 'Shift', 'Control', 'A')
15
+ * @returns Reactive ref that is true if the key is currently held down
16
+ *
17
+ * @example
18
+ * ```vue
19
+ * <script setup>
20
+ * import { useKeyHold } from '@tanstack/vue-hotkeys'
21
+ *
22
+ * const isShiftHeld = useKeyHold('Shift')
23
+ * </script>
24
+ *
25
+ * <template>
26
+ * <div :style="{ opacity: isShiftHeld ? 1 : 0.5 }">
27
+ * {{ isShiftHeld ? 'Shift is pressed!' : 'Press Shift' }}
28
+ * </div>
29
+ * </template>
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```vue
34
+ * <script setup>
35
+ * import { useKeyHold } from '@tanstack/vue-hotkeys'
36
+ *
37
+ * const ctrl = useKeyHold('Control')
38
+ * const shift = useKeyHold('Shift')
39
+ * const alt = useKeyHold('Alt')
40
+ * </script>
41
+ *
42
+ * <template>
43
+ * <div>
44
+ * <span :style="{ opacity: ctrl ? 1 : 0.3 }">Ctrl</span>
45
+ * <span :style="{ opacity: shift ? 1 : 0.3 }">Shift</span>
46
+ * <span :style="{ opacity: alt ? 1 : 0.3 }">Alt</span>
47
+ * </div>
48
+ * </template>
49
+ * ```
50
+ */
51
+ export function useKeyHold(key: MaybeRefOrGetter<HeldKey>): Ref<boolean> {
52
+ const tracker = getKeyStateTracker()
53
+
54
+ const isHeld = useStore(tracker.store, (state) => {
55
+ const keyValue = unref(key)
56
+ const normalizedKey = (
57
+ typeof keyValue === 'string' ? keyValue : String(keyValue)
58
+ ).toLowerCase()
59
+ return state.heldKeys.some(
60
+ (heldKey) => heldKey.toLowerCase() === normalizedKey,
61
+ )
62
+ })
63
+
64
+ return isHeld as Ref<boolean>
65
+ }
package/src/vue.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ declare module '*.vue' {
2
+ import type { DefineComponent } from 'vue'
3
+
4
+ const component: DefineComponent<{}, {}, any>
5
+ export default component
6
+ }