@tanstack/vue-hotkeys 0.6.0 → 0.8.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.
- package/README.md +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/useHotkey.cjs +23 -7
- package/dist/useHotkey.cjs.map +1 -1
- package/dist/useHotkey.js +24 -8
- package/dist/useHotkey.js.map +1 -1
- package/dist/useHotkeySequence.cjs +23 -6
- package/dist/useHotkeySequence.cjs.map +1 -1
- package/dist/useHotkeySequence.js +24 -7
- package/dist/useHotkeySequence.js.map +1 -1
- package/dist/useHotkeySequences.cjs +140 -0
- package/dist/useHotkeySequences.cjs.map +1 -0
- package/dist/useHotkeySequences.d.cts +64 -0
- package/dist/useHotkeySequences.d.ts +64 -0
- package/dist/useHotkeySequences.js +140 -0
- package/dist/useHotkeySequences.js.map +1 -0
- package/dist/useHotkeys.cjs +1 -2
- package/dist/useHotkeys.cjs.map +1 -1
- package/dist/useHotkeys.js +2 -3
- package/dist/useHotkeys.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/useHotkey.ts +33 -17
- package/src/useHotkeySequence.ts +34 -15
- package/src/useHotkeySequences.ts +221 -0
- package/src/useHotkeys.ts +5 -9
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { UseHotkeySequenceOptions } from "./useHotkeySequence.cjs";
|
|
2
|
+
import { HotkeyCallback, HotkeySequence } from "@tanstack/hotkeys";
|
|
3
|
+
import { MaybeRefOrGetter } from "vue";
|
|
4
|
+
|
|
5
|
+
//#region src/useHotkeySequences.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* A single sequence definition for use with `useHotkeySequences`.
|
|
8
|
+
*/
|
|
9
|
+
interface UseHotkeySequenceDefinition {
|
|
10
|
+
/** Array of hotkey strings that form the sequence */
|
|
11
|
+
sequence: MaybeRefOrGetter<HotkeySequence>;
|
|
12
|
+
/** The function to call when the sequence is completed */
|
|
13
|
+
callback: HotkeyCallback;
|
|
14
|
+
/** Per-sequence options (merged on top of commonOptions) */
|
|
15
|
+
options?: MaybeRefOrGetter<UseHotkeySequenceOptions>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Vue composable for registering multiple keyboard shortcut sequences at once (Vim-style).
|
|
19
|
+
*
|
|
20
|
+
* Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or a getter/ref
|
|
21
|
+
* that returns one, so you can register variable-length lists safely.
|
|
22
|
+
*
|
|
23
|
+
* Options are merged in this order:
|
|
24
|
+
* HotkeysProvider defaults < commonOptions < per-definition options
|
|
25
|
+
*
|
|
26
|
+
* Definitions with an empty `sequence` are skipped (no registration).
|
|
27
|
+
*
|
|
28
|
+
* @param definitions - Array of sequence definitions, or a getter/ref
|
|
29
|
+
* @param commonOptions - Shared options applied to all sequences (overridden by per-definition options)
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```vue
|
|
33
|
+
* <script setup>
|
|
34
|
+
* import { useHotkeySequences } from '@tanstack/vue-hotkeys'
|
|
35
|
+
*
|
|
36
|
+
* useHotkeySequences([
|
|
37
|
+
* { sequence: ['G', 'G'], callback: () => scrollToTop() },
|
|
38
|
+
* { sequence: ['D', 'D'], callback: () => deleteLine() },
|
|
39
|
+
* ])
|
|
40
|
+
* </script>
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```vue
|
|
45
|
+
* <script setup>
|
|
46
|
+
* import { computed } from 'vue'
|
|
47
|
+
* import { useHotkeySequences } from '@tanstack/vue-hotkeys'
|
|
48
|
+
*
|
|
49
|
+
* const items = computed(() => [...])
|
|
50
|
+
* useHotkeySequences(
|
|
51
|
+
* () => items.value.map((item) => ({
|
|
52
|
+
* sequence: item.chords,
|
|
53
|
+
* callback: item.action,
|
|
54
|
+
* options: { enabled: item.enabled },
|
|
55
|
+
* })),
|
|
56
|
+
* { preventDefault: true },
|
|
57
|
+
* )
|
|
58
|
+
* </script>
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function useHotkeySequences(definitions: MaybeRefOrGetter<Array<UseHotkeySequenceDefinition>>, commonOptions?: MaybeRefOrGetter<UseHotkeySequenceOptions>): void;
|
|
62
|
+
//#endregion
|
|
63
|
+
export { UseHotkeySequenceDefinition, useHotkeySequences };
|
|
64
|
+
//# sourceMappingURL=useHotkeySequences.d.cts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { UseHotkeySequenceOptions } from "./useHotkeySequence.js";
|
|
2
|
+
import { HotkeyCallback, HotkeySequence } from "@tanstack/hotkeys";
|
|
3
|
+
import { MaybeRefOrGetter } from "vue";
|
|
4
|
+
|
|
5
|
+
//#region src/useHotkeySequences.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* A single sequence definition for use with `useHotkeySequences`.
|
|
8
|
+
*/
|
|
9
|
+
interface UseHotkeySequenceDefinition {
|
|
10
|
+
/** Array of hotkey strings that form the sequence */
|
|
11
|
+
sequence: MaybeRefOrGetter<HotkeySequence>;
|
|
12
|
+
/** The function to call when the sequence is completed */
|
|
13
|
+
callback: HotkeyCallback;
|
|
14
|
+
/** Per-sequence options (merged on top of commonOptions) */
|
|
15
|
+
options?: MaybeRefOrGetter<UseHotkeySequenceOptions>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Vue composable for registering multiple keyboard shortcut sequences at once (Vim-style).
|
|
19
|
+
*
|
|
20
|
+
* Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or a getter/ref
|
|
21
|
+
* that returns one, so you can register variable-length lists safely.
|
|
22
|
+
*
|
|
23
|
+
* Options are merged in this order:
|
|
24
|
+
* HotkeysProvider defaults < commonOptions < per-definition options
|
|
25
|
+
*
|
|
26
|
+
* Definitions with an empty `sequence` are skipped (no registration).
|
|
27
|
+
*
|
|
28
|
+
* @param definitions - Array of sequence definitions, or a getter/ref
|
|
29
|
+
* @param commonOptions - Shared options applied to all sequences (overridden by per-definition options)
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```vue
|
|
33
|
+
* <script setup>
|
|
34
|
+
* import { useHotkeySequences } from '@tanstack/vue-hotkeys'
|
|
35
|
+
*
|
|
36
|
+
* useHotkeySequences([
|
|
37
|
+
* { sequence: ['G', 'G'], callback: () => scrollToTop() },
|
|
38
|
+
* { sequence: ['D', 'D'], callback: () => deleteLine() },
|
|
39
|
+
* ])
|
|
40
|
+
* </script>
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```vue
|
|
45
|
+
* <script setup>
|
|
46
|
+
* import { computed } from 'vue'
|
|
47
|
+
* import { useHotkeySequences } from '@tanstack/vue-hotkeys'
|
|
48
|
+
*
|
|
49
|
+
* const items = computed(() => [...])
|
|
50
|
+
* useHotkeySequences(
|
|
51
|
+
* () => items.value.map((item) => ({
|
|
52
|
+
* sequence: item.chords,
|
|
53
|
+
* callback: item.action,
|
|
54
|
+
* options: { enabled: item.enabled },
|
|
55
|
+
* })),
|
|
56
|
+
* { preventDefault: true },
|
|
57
|
+
* )
|
|
58
|
+
* </script>
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function useHotkeySequences(definitions: MaybeRefOrGetter<Array<UseHotkeySequenceDefinition>>, commonOptions?: MaybeRefOrGetter<UseHotkeySequenceOptions>): void;
|
|
62
|
+
//#endregion
|
|
63
|
+
export { UseHotkeySequenceDefinition, useHotkeySequences };
|
|
64
|
+
//# sourceMappingURL=useHotkeySequences.d.ts.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useDefaultHotkeysOptions } from "./HotkeysProviderContext.js";
|
|
2
|
+
import { formatHotkeySequence, getSequenceManager } from "@tanstack/hotkeys";
|
|
3
|
+
import { onUnmounted, unref, watch } from "vue";
|
|
4
|
+
|
|
5
|
+
//#region src/useHotkeySequences.ts
|
|
6
|
+
/**
|
|
7
|
+
* Vue composable for registering multiple keyboard shortcut sequences at once (Vim-style).
|
|
8
|
+
*
|
|
9
|
+
* Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or a getter/ref
|
|
10
|
+
* that returns one, so you can register variable-length lists safely.
|
|
11
|
+
*
|
|
12
|
+
* Options are merged in this order:
|
|
13
|
+
* HotkeysProvider defaults < commonOptions < per-definition options
|
|
14
|
+
*
|
|
15
|
+
* Definitions with an empty `sequence` are skipped (no registration).
|
|
16
|
+
*
|
|
17
|
+
* @param definitions - Array of sequence definitions, or a getter/ref
|
|
18
|
+
* @param commonOptions - Shared options applied to all sequences (overridden by per-definition options)
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```vue
|
|
22
|
+
* <script setup>
|
|
23
|
+
* import { useHotkeySequences } from '@tanstack/vue-hotkeys'
|
|
24
|
+
*
|
|
25
|
+
* useHotkeySequences([
|
|
26
|
+
* { sequence: ['G', 'G'], callback: () => scrollToTop() },
|
|
27
|
+
* { sequence: ['D', 'D'], callback: () => deleteLine() },
|
|
28
|
+
* ])
|
|
29
|
+
* <\/script>
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```vue
|
|
34
|
+
* <script setup>
|
|
35
|
+
* import { computed } from 'vue'
|
|
36
|
+
* import { useHotkeySequences } from '@tanstack/vue-hotkeys'
|
|
37
|
+
*
|
|
38
|
+
* const items = computed(() => [...])
|
|
39
|
+
* useHotkeySequences(
|
|
40
|
+
* () => items.value.map((item) => ({
|
|
41
|
+
* sequence: item.chords,
|
|
42
|
+
* callback: item.action,
|
|
43
|
+
* options: { enabled: item.enabled },
|
|
44
|
+
* })),
|
|
45
|
+
* { preventDefault: true },
|
|
46
|
+
* )
|
|
47
|
+
* <\/script>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
function useHotkeySequences(definitions, commonOptions = {}) {
|
|
51
|
+
const defaultOptions = useDefaultHotkeysOptions();
|
|
52
|
+
const manager = getSequenceManager();
|
|
53
|
+
const registrations = /* @__PURE__ */ new Map();
|
|
54
|
+
const stopWatcher = watch(() => {
|
|
55
|
+
const resolvedDefinitions = resolveMaybeRefOrGetter(definitions);
|
|
56
|
+
const resolvedCommonOptions = resolveMaybeRefOrGetter(commonOptions);
|
|
57
|
+
return resolvedDefinitions.map((def, i) => {
|
|
58
|
+
const resolvedSequence = resolveMaybeRefOrGetter(def.sequence);
|
|
59
|
+
const resolvedDefOptions = def.options ? resolveMaybeRefOrGetter(def.options) : {};
|
|
60
|
+
const mergedOptions = {
|
|
61
|
+
...defaultOptions.hotkeySequence,
|
|
62
|
+
...resolvedCommonOptions,
|
|
63
|
+
...resolvedDefOptions
|
|
64
|
+
};
|
|
65
|
+
const sequenceString = formatHotkeySequence(resolvedSequence);
|
|
66
|
+
const resolvedEnabled = mergedOptions.enabled === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.enabled);
|
|
67
|
+
const resolvedTarget = mergedOptions.target === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.target);
|
|
68
|
+
return {
|
|
69
|
+
index: i,
|
|
70
|
+
resolvedSequence,
|
|
71
|
+
sequenceString,
|
|
72
|
+
callback: def.callback,
|
|
73
|
+
mergedOptions,
|
|
74
|
+
resolvedEnabled,
|
|
75
|
+
resolvedTarget
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}, (resolved) => {
|
|
79
|
+
const nextKeys = /* @__PURE__ */ new Set();
|
|
80
|
+
const prepared = [];
|
|
81
|
+
for (const entry of resolved) {
|
|
82
|
+
if (entry.resolvedSequence.length === 0) continue;
|
|
83
|
+
const finalTarget = entry.resolvedTarget ?? (typeof document !== "undefined" ? document : null);
|
|
84
|
+
if (!finalTarget) continue;
|
|
85
|
+
const registrationKey = `${entry.index}:${entry.sequenceString}`;
|
|
86
|
+
nextKeys.add(registrationKey);
|
|
87
|
+
prepared.push({
|
|
88
|
+
registrationKey,
|
|
89
|
+
entry,
|
|
90
|
+
finalTarget
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
for (const [key, record] of [...registrations.entries()]) if (!nextKeys.has(key)) {
|
|
94
|
+
if (record.handle.isActive) record.handle.unregister();
|
|
95
|
+
registrations.delete(key);
|
|
96
|
+
}
|
|
97
|
+
for (const { registrationKey, entry, finalTarget } of prepared) {
|
|
98
|
+
const existing = registrations.get(registrationKey);
|
|
99
|
+
if (existing?.handle.isActive && existing.target === finalTarget) {
|
|
100
|
+
existing.handle.callback = entry.callback;
|
|
101
|
+
const { target: _target, enabled: _enabled, ...restOptions } = entry.mergedOptions;
|
|
102
|
+
const optionsWithoutTarget = {
|
|
103
|
+
...restOptions,
|
|
104
|
+
...entry.resolvedEnabled === void 0 ? {} : { enabled: entry.resolvedEnabled }
|
|
105
|
+
};
|
|
106
|
+
existing.handle.setOptions(optionsWithoutTarget);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (existing) {
|
|
110
|
+
if (existing.handle.isActive) existing.handle.unregister();
|
|
111
|
+
registrations.delete(registrationKey);
|
|
112
|
+
}
|
|
113
|
+
const { target: _target, enabled: _enabled, ...restOptions } = entry.mergedOptions;
|
|
114
|
+
const optionsWithoutTarget = {
|
|
115
|
+
...restOptions,
|
|
116
|
+
...entry.resolvedEnabled === void 0 ? {} : { enabled: entry.resolvedEnabled }
|
|
117
|
+
};
|
|
118
|
+
const handle = manager.register(entry.resolvedSequence, entry.callback, {
|
|
119
|
+
...optionsWithoutTarget,
|
|
120
|
+
target: finalTarget
|
|
121
|
+
});
|
|
122
|
+
registrations.set(registrationKey, {
|
|
123
|
+
handle,
|
|
124
|
+
target: finalTarget
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}, { immediate: true });
|
|
128
|
+
onUnmounted(() => {
|
|
129
|
+
stopWatcher();
|
|
130
|
+
for (const { handle } of registrations.values()) if (handle.isActive) handle.unregister();
|
|
131
|
+
registrations.clear();
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function resolveMaybeRefOrGetter(value) {
|
|
135
|
+
return typeof value === "function" ? value() : unref(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//#endregion
|
|
139
|
+
export { useHotkeySequences };
|
|
140
|
+
//# sourceMappingURL=useHotkeySequences.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHotkeySequences.js","names":[],"sources":["../src/useHotkeySequences.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport { formatHotkeySequence, getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type { UseHotkeySequenceOptions } from './useHotkeySequence'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\nimport type { MaybeRefOrGetter } from 'vue'\n\n/**\n * A single sequence definition for use with `useHotkeySequences`.\n */\nexport interface UseHotkeySequenceDefinition {\n /** Array of hotkey strings that form the sequence */\n sequence: MaybeRefOrGetter<HotkeySequence>\n /** The function to call when the sequence is completed */\n callback: HotkeyCallback\n /** Per-sequence options (merged on top of commonOptions) */\n options?: MaybeRefOrGetter<UseHotkeySequenceOptions>\n}\n\n/**\n * Vue composable for registering multiple keyboard shortcut sequences at once (Vim-style).\n *\n * Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or a getter/ref\n * that returns one, so you can register variable-length lists safely.\n *\n * Options are merged in this order:\n * HotkeysProvider defaults < commonOptions < per-definition options\n *\n * Definitions with an empty `sequence` are skipped (no registration).\n *\n * @param definitions - Array of sequence definitions, or a getter/ref\n * @param commonOptions - Shared options applied to all sequences (overridden by per-definition options)\n *\n * @example\n * ```vue\n * <script setup>\n * import { useHotkeySequences } from '@tanstack/vue-hotkeys'\n *\n * useHotkeySequences([\n * { sequence: ['G', 'G'], callback: () => scrollToTop() },\n * { sequence: ['D', 'D'], callback: () => deleteLine() },\n * ])\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { computed } from 'vue'\n * import { useHotkeySequences } from '@tanstack/vue-hotkeys'\n *\n * const items = computed(() => [...])\n * useHotkeySequences(\n * () => items.value.map((item) => ({\n * sequence: item.chords,\n * callback: item.action,\n * options: { enabled: item.enabled },\n * })),\n * { preventDefault: true },\n * )\n * </script>\n * ```\n */\nexport function useHotkeySequences(\n definitions: MaybeRefOrGetter<Array<UseHotkeySequenceDefinition>>,\n commonOptions: MaybeRefOrGetter<UseHotkeySequenceOptions> = {},\n): void {\n type RegistrationRecord = {\n handle: SequenceRegistrationHandle\n target: Document | HTMLElement | Window\n }\n\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getSequenceManager()\n\n const registrations = new Map<string, RegistrationRecord>()\n\n const stopWatcher = watch(\n () => {\n const resolvedDefinitions = resolveMaybeRefOrGetter(definitions)\n const resolvedCommonOptions = resolveMaybeRefOrGetter(commonOptions)\n\n return resolvedDefinitions.map((def, i) => {\n const resolvedSequence = resolveMaybeRefOrGetter(def.sequence)\n const resolvedDefOptions = def.options\n ? resolveMaybeRefOrGetter(def.options)\n : {}\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedCommonOptions,\n ...resolvedDefOptions,\n } as UseHotkeySequenceOptions\n\n const sequenceString = formatHotkeySequence(resolvedSequence)\n\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target as any)\n\n return {\n index: i,\n resolvedSequence,\n sequenceString,\n callback: def.callback,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n })\n },\n (resolved) => {\n const nextKeys = new Set<string>()\n const prepared: Array<{\n registrationKey: string\n entry: (typeof resolved)[number]\n finalTarget: Document | HTMLElement | Window\n }> = []\n\n for (const entry of resolved) {\n if (entry.resolvedSequence.length === 0) {\n continue\n }\n\n const finalTarget =\n entry.resolvedTarget ??\n (typeof document !== 'undefined' ? document : null)\n\n if (!finalTarget) {\n continue\n }\n\n const registrationKey = `${entry.index}:${entry.sequenceString}`\n nextKeys.add(registrationKey)\n prepared.push({ registrationKey, entry, finalTarget })\n }\n\n for (const [key, record] of [...registrations.entries()]) {\n if (!nextKeys.has(key)) {\n if (record.handle.isActive) {\n record.handle.unregister()\n }\n registrations.delete(key)\n }\n }\n\n for (const { registrationKey, entry, finalTarget } of prepared) {\n const existing = registrations.get(registrationKey)\n if (existing?.handle.isActive && existing.target === finalTarget) {\n existing.handle.callback = entry.callback\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = entry.mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(entry.resolvedEnabled === undefined\n ? {}\n : { enabled: entry.resolvedEnabled }),\n }\n existing.handle.setOptions(optionsWithoutTarget)\n continue\n }\n\n if (existing) {\n if (existing.handle.isActive) {\n existing.handle.unregister()\n }\n registrations.delete(registrationKey)\n }\n\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = entry.mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(entry.resolvedEnabled === undefined\n ? {}\n : { enabled: entry.resolvedEnabled }),\n }\n\n const handle = manager.register(\n entry.resolvedSequence,\n entry.callback,\n {\n ...optionsWithoutTarget,\n target: finalTarget,\n },\n )\n registrations.set(registrationKey, { handle, target: finalTarget })\n }\n },\n { immediate: true },\n )\n\n onUnmounted(() => {\n stopWatcher()\n for (const { handle } of registrations.values()) {\n if (handle.isActive) {\n handle.unregister()\n }\n }\n registrations.clear()\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,SAAgB,mBACd,aACA,gBAA4D,EAAE,EACxD;CAMN,MAAM,iBAAiB,0BAA0B;CACjD,MAAM,UAAU,oBAAoB;CAEpC,MAAM,gCAAgB,IAAI,KAAiC;CAE3D,MAAM,cAAc,YACZ;EACJ,MAAM,sBAAsB,wBAAwB,YAAY;EAChE,MAAM,wBAAwB,wBAAwB,cAAc;AAEpE,SAAO,oBAAoB,KAAK,KAAK,MAAM;GACzC,MAAM,mBAAmB,wBAAwB,IAAI,SAAS;GAC9D,MAAM,qBAAqB,IAAI,UAC3B,wBAAwB,IAAI,QAAQ,GACpC,EAAE;GAEN,MAAM,gBAAgB;IACpB,GAAG,eAAe;IAClB,GAAG;IACH,GAAG;IACJ;GAED,MAAM,iBAAiB,qBAAqB,iBAAiB;GAE7D,MAAM,kBACJ,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GACpD,MAAM,iBACJ,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAc;AAE1D,UAAO;IACL,OAAO;IACP;IACA;IACA,UAAU,IAAI;IACd;IACA;IACA;IACD;IACD;KAEH,aAAa;EACZ,MAAM,2BAAW,IAAI,KAAa;EAClC,MAAM,WAID,EAAE;AAEP,OAAK,MAAM,SAAS,UAAU;AAC5B,OAAI,MAAM,iBAAiB,WAAW,EACpC;GAGF,MAAM,cACJ,MAAM,mBACL,OAAO,aAAa,cAAc,WAAW;AAEhD,OAAI,CAAC,YACH;GAGF,MAAM,kBAAkB,GAAG,MAAM,MAAM,GAAG,MAAM;AAChD,YAAS,IAAI,gBAAgB;AAC7B,YAAS,KAAK;IAAE;IAAiB;IAAO;IAAa,CAAC;;AAGxD,OAAK,MAAM,CAAC,KAAK,WAAW,CAAC,GAAG,cAAc,SAAS,CAAC,CACtD,KAAI,CAAC,SAAS,IAAI,IAAI,EAAE;AACtB,OAAI,OAAO,OAAO,SAChB,QAAO,OAAO,YAAY;AAE5B,iBAAc,OAAO,IAAI;;AAI7B,OAAK,MAAM,EAAE,iBAAiB,OAAO,iBAAiB,UAAU;GAC9D,MAAM,WAAW,cAAc,IAAI,gBAAgB;AACnD,OAAI,UAAU,OAAO,YAAY,SAAS,WAAW,aAAa;AAChE,aAAS,OAAO,WAAW,MAAM;IACjC,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD,MAAM;IACV,MAAM,uBAAuB;KAC3B,GAAG;KACH,GAAI,MAAM,oBAAoB,SAC1B,EAAE,GACF,EAAE,SAAS,MAAM,iBAAiB;KACvC;AACD,aAAS,OAAO,WAAW,qBAAqB;AAChD;;AAGF,OAAI,UAAU;AACZ,QAAI,SAAS,OAAO,SAClB,UAAS,OAAO,YAAY;AAE9B,kBAAc,OAAO,gBAAgB;;GAGvC,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD,MAAM;GACV,MAAM,uBAAuB;IAC3B,GAAG;IACH,GAAI,MAAM,oBAAoB,SAC1B,EAAE,GACF,EAAE,SAAS,MAAM,iBAAiB;IACvC;GAED,MAAM,SAAS,QAAQ,SACrB,MAAM,kBACN,MAAM,UACN;IACE,GAAG;IACH,QAAQ;IACT,CACF;AACD,iBAAc,IAAI,iBAAiB;IAAE;IAAQ,QAAQ;IAAa,CAAC;;IAGvE,EAAE,WAAW,MAAM,CACpB;AAED,mBAAkB;AAChB,eAAa;AACb,OAAK,MAAM,EAAE,YAAY,cAAc,QAAQ,CAC7C,KAAI,OAAO,SACT,QAAO,YAAY;AAGvB,gBAAc,OAAO;GACrB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,GAAG,MAAM,MAAM"}
|
package/dist/useHotkeys.cjs
CHANGED
|
@@ -63,8 +63,7 @@ function useHotkeys(hotkeys, commonOptions = {}) {
|
|
|
63
63
|
...resolvedCommonOptions,
|
|
64
64
|
...resolvedDefOptions
|
|
65
65
|
};
|
|
66
|
-
const
|
|
67
|
-
const hotkeyString = typeof resolvedHotkey === "string" ? resolvedHotkey : (0, _tanstack_hotkeys.formatHotkey)((0, _tanstack_hotkeys.rawHotkeyToParsedHotkey)(resolvedHotkey, platform));
|
|
66
|
+
const hotkeyString = (0, _tanstack_hotkeys.normalizeRegisterableHotkey)(resolvedHotkey, mergedOptions.platform ?? (0, _tanstack_hotkeys.detectPlatform)());
|
|
68
67
|
const resolvedEnabled = mergedOptions.enabled === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.enabled);
|
|
69
68
|
const resolvedTarget = mergedOptions.target === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.target);
|
|
70
69
|
return {
|
package/dist/useHotkeys.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHotkeys.cjs","names":["useDefaultHotkeysOptions"],"sources":["../src/useHotkeys.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport {\n detectPlatform,\n
|
|
1
|
+
{"version":3,"file":"useHotkeys.cjs","names":["useDefaultHotkeysOptions"],"sources":["../src/useHotkeys.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport {\n detectPlatform,\n getHotkeyManager,\n normalizeRegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type { UseHotkeyOptions } from './useHotkey'\nimport type {\n HotkeyCallback,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport type { MaybeRefOrGetter } from 'vue'\n\n/**\n * A single hotkey definition for use with `useHotkeys`.\n */\nexport interface UseHotkeyDefinition {\n /** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */\n hotkey: MaybeRefOrGetter<RegisterableHotkey>\n /** The function to call when the hotkey is pressed */\n callback: HotkeyCallback\n /** Per-hotkey options (merged on top of commonOptions) */\n options?: MaybeRefOrGetter<UseHotkeyOptions>\n}\n\n/**\n * Vue composable for registering multiple keyboard hotkeys at once.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * Accepts a dynamic array of hotkey definitions, making it safe to use\n * with variable-length lists.\n *\n * Options are merged in this order:\n * HotkeysProvider defaults < commonOptions < per-definition options\n *\n * @param hotkeys - Array of hotkey definitions to register, or a getter/ref\n * @param commonOptions - Shared options applied to all hotkeys (overridden by per-definition options)\n *\n * @example\n * ```vue\n * <script setup>\n * import { useHotkeys } from '@tanstack/vue-hotkeys'\n *\n * useHotkeys([\n * { hotkey: 'Mod+S', callback: () => save() },\n * { hotkey: 'Mod+Z', callback: () => undo() },\n * { hotkey: 'Escape', callback: () => close() },\n * ])\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { computed } from 'vue'\n * import { useHotkeys } from '@tanstack/vue-hotkeys'\n *\n * const items = computed(() => [...])\n * // Dynamic hotkeys from reactive data\n * useHotkeys(\n * () => items.value.map((item) => ({\n * hotkey: item.shortcut,\n * callback: item.action,\n * options: { enabled: item.enabled },\n * })),\n * { preventDefault: true },\n * )\n * </script>\n * ```\n */\nexport function useHotkeys(\n hotkeys: MaybeRefOrGetter<Array<UseHotkeyDefinition>>,\n commonOptions: MaybeRefOrGetter<UseHotkeyOptions> = {},\n): void {\n type RegistrationRecord = {\n handle: HotkeyRegistrationHandle\n target: Document | HTMLElement | Window\n }\n\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n\n const registrations = new Map<string, RegistrationRecord>()\n\n const stopWatcher = watch(\n () => {\n const resolvedHotkeys = resolveMaybeRefOrGetter(hotkeys)\n const resolvedCommonOptions = resolveMaybeRefOrGetter(commonOptions)\n\n return resolvedHotkeys.map((def, i) => {\n const resolvedHotkey = resolveMaybeRefOrGetter(def.hotkey)\n const resolvedDefOptions = def.options\n ? resolveMaybeRefOrGetter(def.options)\n : {}\n\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedCommonOptions,\n ...resolvedDefOptions,\n } as UseHotkeyOptions\n\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString = normalizeRegisterableHotkey(\n resolvedHotkey,\n platform,\n )\n\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target as any)\n\n return {\n index: i,\n hotkeyString,\n callback: def.callback,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n })\n },\n (resolved) => {\n const nextKeys = new Set<string>()\n const prepared: Array<{\n registrationKey: string\n entry: (typeof resolved)[number]\n finalTarget: Document | HTMLElement | Window\n }> = []\n\n for (const entry of resolved) {\n const finalTarget =\n entry.resolvedTarget ??\n (typeof document !== 'undefined' ? document : null)\n\n if (!finalTarget) {\n continue\n }\n\n const registrationKey = `${entry.index}:${entry.hotkeyString}`\n nextKeys.add(registrationKey)\n prepared.push({ registrationKey, entry, finalTarget })\n }\n\n for (const [key, record] of [...registrations.entries()]) {\n if (!nextKeys.has(key)) {\n if (record.handle.isActive) {\n record.handle.unregister()\n }\n registrations.delete(key)\n }\n }\n\n for (const { registrationKey, entry, finalTarget } of prepared) {\n const existing = registrations.get(registrationKey)\n if (existing?.handle.isActive && existing.target === finalTarget) {\n existing.handle.callback = entry.callback\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = entry.mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(entry.resolvedEnabled === undefined\n ? {}\n : { enabled: entry.resolvedEnabled }),\n }\n existing.handle.setOptions(optionsWithoutTarget)\n continue\n }\n\n if (existing) {\n if (existing.handle.isActive) {\n existing.handle.unregister()\n }\n registrations.delete(registrationKey)\n }\n\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = entry.mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(entry.resolvedEnabled === undefined\n ? {}\n : { enabled: entry.resolvedEnabled }),\n }\n\n const handle = manager.register(entry.hotkeyString, entry.callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n registrations.set(registrationKey, { handle, target: finalTarget })\n }\n },\n { immediate: true },\n )\n\n onUnmounted(() => {\n stopWatcher()\n for (const { handle } of registrations.values()) {\n if (handle.isActive) {\n handle.unregister()\n }\n }\n registrations.clear()\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEA,SAAgB,WACd,SACA,gBAAoD,EAAE,EAChD;CAMN,MAAM,iBAAiBA,yDAA0B;CACjD,MAAM,mDAA4B;CAElC,MAAM,gCAAgB,IAAI,KAAiC;CAE3D,MAAM,mCACE;EACJ,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,wBAAwB,wBAAwB,cAAc;AAEpE,SAAO,gBAAgB,KAAK,KAAK,MAAM;GACrC,MAAM,iBAAiB,wBAAwB,IAAI,OAAO;GAC1D,MAAM,qBAAqB,IAAI,UAC3B,wBAAwB,IAAI,QAAQ,GACpC,EAAE;GAEN,MAAM,gBAAgB;IACpB,GAAG,eAAe;IAClB,GAAG;IACH,GAAG;IACJ;GAGD,MAAM,kEACJ,gBAFe,cAAc,mDAA4B,CAI1D;GAED,MAAM,kBACJ,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GACpD,MAAM,iBACJ,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAc;AAE1D,UAAO;IACL,OAAO;IACP;IACA,UAAU,IAAI;IACd;IACA;IACA;IACD;IACD;KAEH,aAAa;EACZ,MAAM,2BAAW,IAAI,KAAa;EAClC,MAAM,WAID,EAAE;AAEP,OAAK,MAAM,SAAS,UAAU;GAC5B,MAAM,cACJ,MAAM,mBACL,OAAO,aAAa,cAAc,WAAW;AAEhD,OAAI,CAAC,YACH;GAGF,MAAM,kBAAkB,GAAG,MAAM,MAAM,GAAG,MAAM;AAChD,YAAS,IAAI,gBAAgB;AAC7B,YAAS,KAAK;IAAE;IAAiB;IAAO;IAAa,CAAC;;AAGxD,OAAK,MAAM,CAAC,KAAK,WAAW,CAAC,GAAG,cAAc,SAAS,CAAC,CACtD,KAAI,CAAC,SAAS,IAAI,IAAI,EAAE;AACtB,OAAI,OAAO,OAAO,SAChB,QAAO,OAAO,YAAY;AAE5B,iBAAc,OAAO,IAAI;;AAI7B,OAAK,MAAM,EAAE,iBAAiB,OAAO,iBAAiB,UAAU;GAC9D,MAAM,WAAW,cAAc,IAAI,gBAAgB;AACnD,OAAI,UAAU,OAAO,YAAY,SAAS,WAAW,aAAa;AAChE,aAAS,OAAO,WAAW,MAAM;IACjC,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD,MAAM;IACV,MAAM,uBAAuB;KAC3B,GAAG;KACH,GAAI,MAAM,oBAAoB,SAC1B,EAAE,GACF,EAAE,SAAS,MAAM,iBAAiB;KACvC;AACD,aAAS,OAAO,WAAW,qBAAqB;AAChD;;AAGF,OAAI,UAAU;AACZ,QAAI,SAAS,OAAO,SAClB,UAAS,OAAO,YAAY;AAE9B,kBAAc,OAAO,gBAAgB;;GAGvC,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD,MAAM;GACV,MAAM,uBAAuB;IAC3B,GAAG;IACH,GAAI,MAAM,oBAAoB,SAC1B,EAAE,GACF,EAAE,SAAS,MAAM,iBAAiB;IACvC;GAED,MAAM,SAAS,QAAQ,SAAS,MAAM,cAAc,MAAM,UAAU;IAClE,GAAG;IACH,QAAQ;IACT,CAAC;AACF,iBAAc,IAAI,iBAAiB;IAAE;IAAQ,QAAQ;IAAa,CAAC;;IAGvE,EAAE,WAAW,MAAM,CACpB;AAED,4BAAkB;AAChB,eAAa;AACb,OAAK,MAAM,EAAE,YAAY,cAAc,QAAQ,CAC7C,KAAI,OAAO,SACT,QAAO,YAAY;AAGvB,gBAAc,OAAO;GACrB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,kBAAS,MAAM"}
|
package/dist/useHotkeys.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useDefaultHotkeysOptions } from "./HotkeysProviderContext.js";
|
|
2
|
-
import { detectPlatform,
|
|
2
|
+
import { detectPlatform, getHotkeyManager, normalizeRegisterableHotkey } from "@tanstack/hotkeys";
|
|
3
3
|
import { onUnmounted, unref, watch } from "vue";
|
|
4
4
|
|
|
5
5
|
//#region src/useHotkeys.ts
|
|
@@ -63,8 +63,7 @@ function useHotkeys(hotkeys, commonOptions = {}) {
|
|
|
63
63
|
...resolvedCommonOptions,
|
|
64
64
|
...resolvedDefOptions
|
|
65
65
|
};
|
|
66
|
-
const
|
|
67
|
-
const hotkeyString = typeof resolvedHotkey === "string" ? resolvedHotkey : formatHotkey(rawHotkeyToParsedHotkey(resolvedHotkey, platform));
|
|
66
|
+
const hotkeyString = normalizeRegisterableHotkey(resolvedHotkey, mergedOptions.platform ?? detectPlatform());
|
|
68
67
|
const resolvedEnabled = mergedOptions.enabled === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.enabled);
|
|
69
68
|
const resolvedTarget = mergedOptions.target === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.target);
|
|
70
69
|
return {
|
package/dist/useHotkeys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHotkeys.js","names":[],"sources":["../src/useHotkeys.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport {\n detectPlatform,\n
|
|
1
|
+
{"version":3,"file":"useHotkeys.js","names":[],"sources":["../src/useHotkeys.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport {\n detectPlatform,\n getHotkeyManager,\n normalizeRegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type { UseHotkeyOptions } from './useHotkey'\nimport type {\n HotkeyCallback,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport type { MaybeRefOrGetter } from 'vue'\n\n/**\n * A single hotkey definition for use with `useHotkeys`.\n */\nexport interface UseHotkeyDefinition {\n /** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */\n hotkey: MaybeRefOrGetter<RegisterableHotkey>\n /** The function to call when the hotkey is pressed */\n callback: HotkeyCallback\n /** Per-hotkey options (merged on top of commonOptions) */\n options?: MaybeRefOrGetter<UseHotkeyOptions>\n}\n\n/**\n * Vue composable for registering multiple keyboard hotkeys at once.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * Accepts a dynamic array of hotkey definitions, making it safe to use\n * with variable-length lists.\n *\n * Options are merged in this order:\n * HotkeysProvider defaults < commonOptions < per-definition options\n *\n * @param hotkeys - Array of hotkey definitions to register, or a getter/ref\n * @param commonOptions - Shared options applied to all hotkeys (overridden by per-definition options)\n *\n * @example\n * ```vue\n * <script setup>\n * import { useHotkeys } from '@tanstack/vue-hotkeys'\n *\n * useHotkeys([\n * { hotkey: 'Mod+S', callback: () => save() },\n * { hotkey: 'Mod+Z', callback: () => undo() },\n * { hotkey: 'Escape', callback: () => close() },\n * ])\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { computed } from 'vue'\n * import { useHotkeys } from '@tanstack/vue-hotkeys'\n *\n * const items = computed(() => [...])\n * // Dynamic hotkeys from reactive data\n * useHotkeys(\n * () => items.value.map((item) => ({\n * hotkey: item.shortcut,\n * callback: item.action,\n * options: { enabled: item.enabled },\n * })),\n * { preventDefault: true },\n * )\n * </script>\n * ```\n */\nexport function useHotkeys(\n hotkeys: MaybeRefOrGetter<Array<UseHotkeyDefinition>>,\n commonOptions: MaybeRefOrGetter<UseHotkeyOptions> = {},\n): void {\n type RegistrationRecord = {\n handle: HotkeyRegistrationHandle\n target: Document | HTMLElement | Window\n }\n\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n\n const registrations = new Map<string, RegistrationRecord>()\n\n const stopWatcher = watch(\n () => {\n const resolvedHotkeys = resolveMaybeRefOrGetter(hotkeys)\n const resolvedCommonOptions = resolveMaybeRefOrGetter(commonOptions)\n\n return resolvedHotkeys.map((def, i) => {\n const resolvedHotkey = resolveMaybeRefOrGetter(def.hotkey)\n const resolvedDefOptions = def.options\n ? resolveMaybeRefOrGetter(def.options)\n : {}\n\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedCommonOptions,\n ...resolvedDefOptions,\n } as UseHotkeyOptions\n\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString = normalizeRegisterableHotkey(\n resolvedHotkey,\n platform,\n )\n\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target as any)\n\n return {\n index: i,\n hotkeyString,\n callback: def.callback,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n })\n },\n (resolved) => {\n const nextKeys = new Set<string>()\n const prepared: Array<{\n registrationKey: string\n entry: (typeof resolved)[number]\n finalTarget: Document | HTMLElement | Window\n }> = []\n\n for (const entry of resolved) {\n const finalTarget =\n entry.resolvedTarget ??\n (typeof document !== 'undefined' ? document : null)\n\n if (!finalTarget) {\n continue\n }\n\n const registrationKey = `${entry.index}:${entry.hotkeyString}`\n nextKeys.add(registrationKey)\n prepared.push({ registrationKey, entry, finalTarget })\n }\n\n for (const [key, record] of [...registrations.entries()]) {\n if (!nextKeys.has(key)) {\n if (record.handle.isActive) {\n record.handle.unregister()\n }\n registrations.delete(key)\n }\n }\n\n for (const { registrationKey, entry, finalTarget } of prepared) {\n const existing = registrations.get(registrationKey)\n if (existing?.handle.isActive && existing.target === finalTarget) {\n existing.handle.callback = entry.callback\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = entry.mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(entry.resolvedEnabled === undefined\n ? {}\n : { enabled: entry.resolvedEnabled }),\n }\n existing.handle.setOptions(optionsWithoutTarget)\n continue\n }\n\n if (existing) {\n if (existing.handle.isActive) {\n existing.handle.unregister()\n }\n registrations.delete(registrationKey)\n }\n\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = entry.mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(entry.resolvedEnabled === undefined\n ? {}\n : { enabled: entry.resolvedEnabled }),\n }\n\n const handle = manager.register(entry.hotkeyString, entry.callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n registrations.set(registrationKey, { handle, target: finalTarget })\n }\n },\n { immediate: true },\n )\n\n onUnmounted(() => {\n stopWatcher()\n for (const { handle } of registrations.values()) {\n if (handle.isActive) {\n handle.unregister()\n }\n }\n registrations.clear()\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEA,SAAgB,WACd,SACA,gBAAoD,EAAE,EAChD;CAMN,MAAM,iBAAiB,0BAA0B;CACjD,MAAM,UAAU,kBAAkB;CAElC,MAAM,gCAAgB,IAAI,KAAiC;CAE3D,MAAM,cAAc,YACZ;EACJ,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,wBAAwB,wBAAwB,cAAc;AAEpE,SAAO,gBAAgB,KAAK,KAAK,MAAM;GACrC,MAAM,iBAAiB,wBAAwB,IAAI,OAAO;GAC1D,MAAM,qBAAqB,IAAI,UAC3B,wBAAwB,IAAI,QAAQ,GACpC,EAAE;GAEN,MAAM,gBAAgB;IACpB,GAAG,eAAe;IAClB,GAAG;IACH,GAAG;IACJ;GAGD,MAAM,eAAe,4BACnB,gBAFe,cAAc,YAAY,gBAAgB,CAI1D;GAED,MAAM,kBACJ,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GACpD,MAAM,iBACJ,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAc;AAE1D,UAAO;IACL,OAAO;IACP;IACA,UAAU,IAAI;IACd;IACA;IACA;IACD;IACD;KAEH,aAAa;EACZ,MAAM,2BAAW,IAAI,KAAa;EAClC,MAAM,WAID,EAAE;AAEP,OAAK,MAAM,SAAS,UAAU;GAC5B,MAAM,cACJ,MAAM,mBACL,OAAO,aAAa,cAAc,WAAW;AAEhD,OAAI,CAAC,YACH;GAGF,MAAM,kBAAkB,GAAG,MAAM,MAAM,GAAG,MAAM;AAChD,YAAS,IAAI,gBAAgB;AAC7B,YAAS,KAAK;IAAE;IAAiB;IAAO;IAAa,CAAC;;AAGxD,OAAK,MAAM,CAAC,KAAK,WAAW,CAAC,GAAG,cAAc,SAAS,CAAC,CACtD,KAAI,CAAC,SAAS,IAAI,IAAI,EAAE;AACtB,OAAI,OAAO,OAAO,SAChB,QAAO,OAAO,YAAY;AAE5B,iBAAc,OAAO,IAAI;;AAI7B,OAAK,MAAM,EAAE,iBAAiB,OAAO,iBAAiB,UAAU;GAC9D,MAAM,WAAW,cAAc,IAAI,gBAAgB;AACnD,OAAI,UAAU,OAAO,YAAY,SAAS,WAAW,aAAa;AAChE,aAAS,OAAO,WAAW,MAAM;IACjC,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD,MAAM;IACV,MAAM,uBAAuB;KAC3B,GAAG;KACH,GAAI,MAAM,oBAAoB,SAC1B,EAAE,GACF,EAAE,SAAS,MAAM,iBAAiB;KACvC;AACD,aAAS,OAAO,WAAW,qBAAqB;AAChD;;AAGF,OAAI,UAAU;AACZ,QAAI,SAAS,OAAO,SAClB,UAAS,OAAO,YAAY;AAE9B,kBAAc,OAAO,gBAAgB;;GAGvC,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD,MAAM;GACV,MAAM,uBAAuB;IAC3B,GAAG;IACH,GAAI,MAAM,oBAAoB,SAC1B,EAAE,GACF,EAAE,SAAS,MAAM,iBAAiB;IACvC;GAED,MAAM,SAAS,QAAQ,SAAS,MAAM,cAAc,MAAM,UAAU;IAClE,GAAG;IACH,QAAQ;IACT,CAAC;AACF,iBAAc,IAAI,iBAAiB;IAAE;IAAQ,QAAQ;IAAa,CAAC;;IAGvE,EAAE,WAAW,MAAM,CACpB;AAED,mBAAkB;AAChB,eAAa;AACb,OAAK,MAAM,EAAE,YAAY,cAAc,QAAQ,CAC7C,KAAI,OAAO,SACT,QAAO,YAAY;AAGvB,gBAAc,OAAO;GACrB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,GAAG,MAAM,MAAM"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/vue-hotkeys",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Vue adapter for TanStack Hotkeys",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"src"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@tanstack/vue-store": "^0.9.
|
|
46
|
-
"@tanstack/hotkeys": "0.
|
|
45
|
+
"@tanstack/vue-store": "^0.9.3",
|
|
46
|
+
"@tanstack/hotkeys": "0.6.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@vitejs/plugin-vue": "^6.0.5",
|
package/src/index.ts
CHANGED
package/src/useHotkey.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { onUnmounted, unref, watch } from 'vue'
|
|
2
2
|
import {
|
|
3
3
|
detectPlatform,
|
|
4
|
-
formatHotkey,
|
|
5
4
|
getHotkeyManager,
|
|
6
|
-
|
|
5
|
+
normalizeRegisterableHotkey,
|
|
7
6
|
} from '@tanstack/hotkeys'
|
|
8
7
|
import { useDefaultHotkeysOptions } from './HotkeysProviderContext'
|
|
9
8
|
import type {
|
|
@@ -106,6 +105,8 @@ export function useHotkey(
|
|
|
106
105
|
const manager = getHotkeyManager()
|
|
107
106
|
|
|
108
107
|
let registration: HotkeyRegistrationHandle | null = null
|
|
108
|
+
let lastHotkeyString: Hotkey | null = null
|
|
109
|
+
let lastTarget: HTMLElement | Document | Window | null = null
|
|
109
110
|
|
|
110
111
|
// Watch for changes to reactive dependencies
|
|
111
112
|
const stopWatcher = watch(
|
|
@@ -135,27 +136,26 @@ export function useHotkey(
|
|
|
135
136
|
({ resolvedHotkey, mergedOptions, resolvedEnabled, resolvedTarget }) => {
|
|
136
137
|
// Normalize to hotkey string
|
|
137
138
|
const platform = mergedOptions.platform ?? detectPlatform()
|
|
138
|
-
const hotkeyString
|
|
139
|
-
typeof resolvedHotkey === 'string'
|
|
140
|
-
? resolvedHotkey
|
|
141
|
-
: (formatHotkey(
|
|
142
|
-
rawHotkeyToParsedHotkey(resolvedHotkey as any, platform),
|
|
143
|
-
) as Hotkey)
|
|
139
|
+
const hotkeyString = normalizeRegisterableHotkey(resolvedHotkey, platform)
|
|
144
140
|
|
|
145
141
|
// Resolve target
|
|
146
142
|
const finalTarget =
|
|
147
|
-
resolvedTarget
|
|
143
|
+
resolvedTarget === undefined
|
|
144
|
+
? typeof document !== 'undefined'
|
|
145
|
+
? document
|
|
146
|
+
: null
|
|
147
|
+
: resolvedTarget
|
|
148
148
|
|
|
149
149
|
if (!finalTarget) {
|
|
150
|
+
if (registration?.isActive) {
|
|
151
|
+
registration.unregister()
|
|
152
|
+
registration = null
|
|
153
|
+
}
|
|
154
|
+
lastHotkeyString = null
|
|
155
|
+
lastTarget = null
|
|
150
156
|
return
|
|
151
157
|
}
|
|
152
158
|
|
|
153
|
-
// Unregister previous registration if it exists
|
|
154
|
-
if (registration?.isActive) {
|
|
155
|
-
registration.unregister()
|
|
156
|
-
registration = null
|
|
157
|
-
}
|
|
158
|
-
|
|
159
159
|
// Extract options without target (target is handled separately)
|
|
160
160
|
const {
|
|
161
161
|
target: _target,
|
|
@@ -167,17 +167,33 @@ export function useHotkey(
|
|
|
167
167
|
...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
if (
|
|
171
|
+
registration?.isActive &&
|
|
172
|
+
lastHotkeyString === hotkeyString &&
|
|
173
|
+
lastTarget === finalTarget
|
|
174
|
+
) {
|
|
175
|
+
registration.callback = callback
|
|
176
|
+
registration.setOptions(optionsWithoutTarget)
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (registration?.isActive) {
|
|
181
|
+
registration.unregister()
|
|
182
|
+
registration = null
|
|
183
|
+
}
|
|
184
|
+
|
|
171
185
|
registration = manager.register(hotkeyString, callback, {
|
|
172
186
|
...optionsWithoutTarget,
|
|
173
187
|
target: finalTarget,
|
|
174
188
|
})
|
|
175
189
|
|
|
176
|
-
// Update callback and options
|
|
177
190
|
if (registration.isActive) {
|
|
178
191
|
registration.callback = callback
|
|
179
192
|
registration.setOptions(optionsWithoutTarget)
|
|
180
193
|
}
|
|
194
|
+
|
|
195
|
+
lastHotkeyString = hotkeyString
|
|
196
|
+
lastTarget = finalTarget
|
|
181
197
|
},
|
|
182
198
|
{ immediate: true },
|
|
183
199
|
)
|
package/src/useHotkeySequence.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { onUnmounted, unref, watch } from 'vue'
|
|
2
|
-
import { getSequenceManager } from '@tanstack/hotkeys'
|
|
2
|
+
import { formatHotkeySequence, getSequenceManager } from '@tanstack/hotkeys'
|
|
3
3
|
import { useDefaultHotkeysOptions } from './HotkeysProviderContext'
|
|
4
4
|
import type { MaybeRefOrGetter } from 'vue'
|
|
5
5
|
import type {
|
|
@@ -82,6 +82,8 @@ export function useHotkeySequence(
|
|
|
82
82
|
const manager = getSequenceManager()
|
|
83
83
|
|
|
84
84
|
let registration: SequenceRegistrationHandle | null = null
|
|
85
|
+
let lastSequenceKey: string | null = null
|
|
86
|
+
let lastTarget: HTMLElement | Document | Window | null = null
|
|
85
87
|
|
|
86
88
|
// Watch for changes to reactive dependencies
|
|
87
89
|
const stopWatcher = watch(
|
|
@@ -109,23 +111,24 @@ export function useHotkeySequence(
|
|
|
109
111
|
}
|
|
110
112
|
},
|
|
111
113
|
({ resolvedSequence, mergedOptions, resolvedEnabled, resolvedTarget }) => {
|
|
112
|
-
if (resolvedEnabled === false || resolvedSequence.length === 0) {
|
|
113
|
-
return
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Resolve target
|
|
117
114
|
const finalTarget =
|
|
118
|
-
resolvedTarget
|
|
115
|
+
resolvedTarget === undefined
|
|
116
|
+
? typeof document !== 'undefined'
|
|
117
|
+
? document
|
|
118
|
+
: null
|
|
119
|
+
: resolvedTarget
|
|
119
120
|
|
|
120
|
-
if (!finalTarget) {
|
|
121
|
+
if (resolvedSequence.length === 0 || !finalTarget) {
|
|
122
|
+
if (registration?.isActive) {
|
|
123
|
+
registration.unregister()
|
|
124
|
+
registration = null
|
|
125
|
+
}
|
|
126
|
+
lastSequenceKey = null
|
|
127
|
+
lastTarget = null
|
|
121
128
|
return
|
|
122
129
|
}
|
|
123
130
|
|
|
124
|
-
|
|
125
|
-
if (registration?.isActive) {
|
|
126
|
-
registration.unregister()
|
|
127
|
-
registration = null
|
|
128
|
-
}
|
|
131
|
+
const sequenceKey = formatHotkeySequence(resolvedSequence)
|
|
129
132
|
|
|
130
133
|
// Extract options without target (target is handled separately)
|
|
131
134
|
const {
|
|
@@ -138,17 +141,33 @@ export function useHotkeySequence(
|
|
|
138
141
|
...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),
|
|
139
142
|
}
|
|
140
143
|
|
|
141
|
-
|
|
144
|
+
if (
|
|
145
|
+
registration?.isActive &&
|
|
146
|
+
lastSequenceKey === sequenceKey &&
|
|
147
|
+
lastTarget === finalTarget
|
|
148
|
+
) {
|
|
149
|
+
registration.callback = callback
|
|
150
|
+
registration.setOptions(optionsWithoutTarget)
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (registration?.isActive) {
|
|
155
|
+
registration.unregister()
|
|
156
|
+
registration = null
|
|
157
|
+
}
|
|
158
|
+
|
|
142
159
|
registration = manager.register(resolvedSequence, callback, {
|
|
143
160
|
...optionsWithoutTarget,
|
|
144
161
|
target: finalTarget,
|
|
145
162
|
})
|
|
146
163
|
|
|
147
|
-
// Update callback and options
|
|
148
164
|
if (registration.isActive) {
|
|
149
165
|
registration.callback = callback
|
|
150
166
|
registration.setOptions(optionsWithoutTarget)
|
|
151
167
|
}
|
|
168
|
+
|
|
169
|
+
lastSequenceKey = sequenceKey
|
|
170
|
+
lastTarget = finalTarget
|
|
152
171
|
},
|
|
153
172
|
{ immediate: true },
|
|
154
173
|
)
|