@tanstack/vue-hotkeys 0.5.1 → 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/HotkeysProviderContext.cjs.map +1 -1
- package/dist/HotkeysProviderContext.d.cts +2 -1
- package/dist/HotkeysProviderContext.d.ts +2 -1
- package/dist/HotkeysProviderContext.js.map +1 -1
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/useHotkey.cjs +22 -5
- package/dist/useHotkey.cjs.map +1 -1
- package/dist/useHotkey.js +22 -5
- 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/useHotkeySequenceRecorder.cjs +45 -0
- package/dist/useHotkeySequenceRecorder.cjs.map +1 -0
- package/dist/useHotkeySequenceRecorder.d.cts +20 -0
- package/dist/useHotkeySequenceRecorder.d.ts +20 -0
- package/dist/useHotkeySequenceRecorder.js +45 -0
- package/dist/useHotkeySequenceRecorder.js.map +1 -0
- 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/package.json +2 -2
- package/src/HotkeysProviderContext.ts +5 -1
- package/src/index.ts +2 -0
- package/src/useHotkey.ts +31 -9
- package/src/useHotkeySequence.ts +34 -15
- package/src/useHotkeySequenceRecorder.ts +75 -0
- package/src/useHotkeySequences.ts +221 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useDefaultHotkeysOptions } from "./HotkeysProviderContext.js";
|
|
2
|
+
import { HotkeySequenceRecorder } from "@tanstack/hotkeys";
|
|
3
|
+
import { onUnmounted, unref, watch } from "vue";
|
|
4
|
+
import { useStore } from "@tanstack/vue-store";
|
|
5
|
+
|
|
6
|
+
//#region src/useHotkeySequenceRecorder.ts
|
|
7
|
+
/**
|
|
8
|
+
* Vue composable for recording multi-chord sequences (Vim-style shortcuts).
|
|
9
|
+
*/
|
|
10
|
+
function useHotkeySequenceRecorder(options) {
|
|
11
|
+
const defaultOptions = useDefaultHotkeysOptions();
|
|
12
|
+
const recorder = new HotkeySequenceRecorder(resolveHotkeySequenceRecorderOptions(options, defaultOptions));
|
|
13
|
+
const isRecording = useStore(recorder.store, (state) => state.isRecording);
|
|
14
|
+
const steps = useStore(recorder.store, (state) => state.steps);
|
|
15
|
+
const recordedSequence = useStore(recorder.store, (state) => state.recordedSequence);
|
|
16
|
+
const stopWatcher = watch(() => resolveHotkeySequenceRecorderOptions(options, defaultOptions), (resolvedOptions) => {
|
|
17
|
+
recorder.setOptions(resolvedOptions);
|
|
18
|
+
}, { immediate: true });
|
|
19
|
+
onUnmounted(() => {
|
|
20
|
+
stopWatcher();
|
|
21
|
+
recorder.destroy();
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
isRecording,
|
|
25
|
+
steps,
|
|
26
|
+
recordedSequence,
|
|
27
|
+
startRecording: () => recorder.start(),
|
|
28
|
+
stopRecording: () => recorder.stop(),
|
|
29
|
+
cancelRecording: () => recorder.cancel(),
|
|
30
|
+
commitRecording: () => recorder.commit()
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function resolveHotkeySequenceRecorderOptions(options, defaultOptions) {
|
|
34
|
+
return {
|
|
35
|
+
...defaultOptions.hotkeySequenceRecorder,
|
|
36
|
+
...resolveMaybeRefOrGetter(options)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function resolveMaybeRefOrGetter(value) {
|
|
40
|
+
return typeof value === "function" ? value() : unref(value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { useHotkeySequenceRecorder };
|
|
45
|
+
//# sourceMappingURL=useHotkeySequenceRecorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHotkeySequenceRecorder.js","names":[],"sources":["../src/useHotkeySequenceRecorder.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport { useStore } from '@tanstack/vue-store'\nimport { HotkeySequenceRecorder } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type { MaybeRefOrGetter, Ref } from 'vue'\nimport type {\n HotkeySequence,\n HotkeySequenceRecorderOptions,\n} from '@tanstack/hotkeys'\n\nexport interface VueHotkeySequenceRecorder {\n isRecording: Ref<boolean>\n steps: Ref<HotkeySequence>\n recordedSequence: Ref<HotkeySequence | null>\n startRecording: () => void\n stopRecording: () => void\n cancelRecording: () => void\n commitRecording: () => void\n}\n\n/**\n * Vue composable for recording multi-chord sequences (Vim-style shortcuts).\n */\nexport function useHotkeySequenceRecorder(\n options: MaybeRefOrGetter<HotkeySequenceRecorderOptions>,\n): VueHotkeySequenceRecorder {\n const defaultOptions = useDefaultHotkeysOptions()\n const recorder = new HotkeySequenceRecorder(\n resolveHotkeySequenceRecorderOptions(options, defaultOptions),\n )\n\n const isRecording = useStore(recorder.store, (state) => state.isRecording)\n const steps = useStore(recorder.store, (state) => state.steps)\n const recordedSequence = useStore(\n recorder.store,\n (state) => state.recordedSequence,\n )\n\n const stopWatcher = watch(\n () => resolveHotkeySequenceRecorderOptions(options, defaultOptions),\n (resolvedOptions) => {\n recorder.setOptions(resolvedOptions)\n },\n { immediate: true },\n )\n\n onUnmounted(() => {\n stopWatcher()\n recorder.destroy()\n })\n\n return {\n isRecording: isRecording as Ref<boolean>,\n steps: steps as Ref<HotkeySequence>,\n recordedSequence: recordedSequence as Ref<HotkeySequence | null>,\n startRecording: () => recorder.start(),\n stopRecording: () => recorder.stop(),\n cancelRecording: () => recorder.cancel(),\n commitRecording: () => recorder.commit(),\n }\n}\n\nfunction resolveHotkeySequenceRecorderOptions(\n options: MaybeRefOrGetter<HotkeySequenceRecorderOptions>,\n defaultOptions: ReturnType<typeof useDefaultHotkeysOptions>,\n): HotkeySequenceRecorderOptions {\n return {\n ...defaultOptions.hotkeySequenceRecorder,\n ...resolveMaybeRefOrGetter(options),\n } as HotkeySequenceRecorderOptions\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,0BACd,SAC2B;CAC3B,MAAM,iBAAiB,0BAA0B;CACjD,MAAM,WAAW,IAAI,uBACnB,qCAAqC,SAAS,eAAe,CAC9D;CAED,MAAM,cAAc,SAAS,SAAS,QAAQ,UAAU,MAAM,YAAY;CAC1E,MAAM,QAAQ,SAAS,SAAS,QAAQ,UAAU,MAAM,MAAM;CAC9D,MAAM,mBAAmB,SACvB,SAAS,QACR,UAAU,MAAM,iBAClB;CAED,MAAM,cAAc,YACZ,qCAAqC,SAAS,eAAe,GAClE,oBAAoB;AACnB,WAAS,WAAW,gBAAgB;IAEtC,EAAE,WAAW,MAAM,CACpB;AAED,mBAAkB;AAChB,eAAa;AACb,WAAS,SAAS;GAClB;AAEF,QAAO;EACQ;EACN;EACW;EAClB,sBAAsB,SAAS,OAAO;EACtC,qBAAqB,SAAS,MAAM;EACpC,uBAAuB,SAAS,QAAQ;EACxC,uBAAuB,SAAS,QAAQ;EACzC;;AAGH,SAAS,qCACP,SACA,gBAC+B;AAC/B,QAAO;EACL,GAAG,eAAe;EAClB,GAAG,wBAAwB,QAAQ;EACpC;;AAGH,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,GAAG,MAAM,MAAM"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const require_HotkeysProviderContext = require('./HotkeysProviderContext.cjs');
|
|
2
|
+
let _tanstack_hotkeys = require("@tanstack/hotkeys");
|
|
3
|
+
let vue = require("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 = require_HotkeysProviderContext.useDefaultHotkeysOptions();
|
|
52
|
+
const manager = (0, _tanstack_hotkeys.getSequenceManager)();
|
|
53
|
+
const registrations = /* @__PURE__ */ new Map();
|
|
54
|
+
const stopWatcher = (0, vue.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 = (0, _tanstack_hotkeys.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
|
+
(0, vue.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() : (0, vue.unref)(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
//#endregion
|
|
139
|
+
exports.useHotkeySequences = useHotkeySequences;
|
|
140
|
+
//# sourceMappingURL=useHotkeySequences.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useHotkeySequences.cjs","names":["useDefaultHotkeysOptions"],"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,iBAAiBA,yDAA0B;CACjD,MAAM,qDAA8B;CAEpC,MAAM,gCAAgB,IAAI,KAAiC;CAE3D,MAAM,mCACE;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,6DAAsC,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,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"}
|
|
@@ -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/vue-hotkeys",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Vue adapter for TanStack Hotkeys",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@tanstack/vue-store": "^0.9.2",
|
|
46
|
-
"@tanstack/hotkeys": "0.
|
|
46
|
+
"@tanstack/hotkeys": "0.5.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@vitejs/plugin-vue": "^6.0.5",
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { inject, provide } from 'vue'
|
|
2
2
|
import type { InjectionKey } from 'vue'
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
HotkeyRecorderOptions,
|
|
5
|
+
HotkeySequenceRecorderOptions,
|
|
6
|
+
} from '@tanstack/hotkeys'
|
|
4
7
|
import type { UseHotkeyOptions } from './useHotkey'
|
|
5
8
|
import type { UseHotkeySequenceOptions } from './useHotkeySequence'
|
|
6
9
|
|
|
7
10
|
export interface HotkeysProviderOptions {
|
|
8
11
|
hotkey?: Partial<UseHotkeyOptions>
|
|
9
12
|
hotkeyRecorder?: Partial<HotkeyRecorderOptions>
|
|
13
|
+
hotkeySequenceRecorder?: Partial<HotkeySequenceRecorderOptions>
|
|
10
14
|
hotkeySequence?: Partial<UseHotkeySequenceOptions>
|
|
11
15
|
}
|
|
12
16
|
|
package/src/index.ts
CHANGED
package/src/useHotkey.ts
CHANGED
|
@@ -106,6 +106,8 @@ export function useHotkey(
|
|
|
106
106
|
const manager = getHotkeyManager()
|
|
107
107
|
|
|
108
108
|
let registration: HotkeyRegistrationHandle | null = null
|
|
109
|
+
let lastHotkeyString: Hotkey | null = null
|
|
110
|
+
let lastTarget: HTMLElement | Document | Window | null = null
|
|
109
111
|
|
|
110
112
|
// Watch for changes to reactive dependencies
|
|
111
113
|
const stopWatcher = watch(
|
|
@@ -144,18 +146,22 @@ export function useHotkey(
|
|
|
144
146
|
|
|
145
147
|
// Resolve target
|
|
146
148
|
const finalTarget =
|
|
147
|
-
resolvedTarget
|
|
149
|
+
resolvedTarget === undefined
|
|
150
|
+
? typeof document !== 'undefined'
|
|
151
|
+
? document
|
|
152
|
+
: null
|
|
153
|
+
: resolvedTarget
|
|
148
154
|
|
|
149
155
|
if (!finalTarget) {
|
|
156
|
+
if (registration?.isActive) {
|
|
157
|
+
registration.unregister()
|
|
158
|
+
registration = null
|
|
159
|
+
}
|
|
160
|
+
lastHotkeyString = null
|
|
161
|
+
lastTarget = null
|
|
150
162
|
return
|
|
151
163
|
}
|
|
152
164
|
|
|
153
|
-
// Unregister previous registration if it exists
|
|
154
|
-
if (registration?.isActive) {
|
|
155
|
-
registration.unregister()
|
|
156
|
-
registration = null
|
|
157
|
-
}
|
|
158
|
-
|
|
159
165
|
// Extract options without target (target is handled separately)
|
|
160
166
|
const {
|
|
161
167
|
target: _target,
|
|
@@ -167,17 +173,33 @@ export function useHotkey(
|
|
|
167
173
|
...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),
|
|
168
174
|
}
|
|
169
175
|
|
|
170
|
-
|
|
176
|
+
if (
|
|
177
|
+
registration?.isActive &&
|
|
178
|
+
lastHotkeyString === hotkeyString &&
|
|
179
|
+
lastTarget === finalTarget
|
|
180
|
+
) {
|
|
181
|
+
registration.callback = callback
|
|
182
|
+
registration.setOptions(optionsWithoutTarget)
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (registration?.isActive) {
|
|
187
|
+
registration.unregister()
|
|
188
|
+
registration = null
|
|
189
|
+
}
|
|
190
|
+
|
|
171
191
|
registration = manager.register(hotkeyString, callback, {
|
|
172
192
|
...optionsWithoutTarget,
|
|
173
193
|
target: finalTarget,
|
|
174
194
|
})
|
|
175
195
|
|
|
176
|
-
// Update callback and options
|
|
177
196
|
if (registration.isActive) {
|
|
178
197
|
registration.callback = callback
|
|
179
198
|
registration.setOptions(optionsWithoutTarget)
|
|
180
199
|
}
|
|
200
|
+
|
|
201
|
+
lastHotkeyString = hotkeyString
|
|
202
|
+
lastTarget = finalTarget
|
|
181
203
|
},
|
|
182
204
|
{ immediate: true },
|
|
183
205
|
)
|