@tanstack/solid-hotkeys 0.1.3 → 0.3.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/createHotkeySequence.cjs +25 -7
- package/dist/createHotkeySequence.cjs.map +1 -1
- package/dist/createHotkeySequence.d.cts +6 -3
- package/dist/createHotkeySequence.d.ts +6 -3
- package/dist/createHotkeySequence.js +25 -7
- package/dist/createHotkeySequence.js.map +1 -1
- package/package.json +3 -3
- package/src/createHotkeySequence.ts +48 -18
|
@@ -37,19 +37,37 @@ let solid_js = require("solid-js");
|
|
|
37
37
|
*/
|
|
38
38
|
function createHotkeySequence(sequence, callback, options = {}) {
|
|
39
39
|
const defaultOptions = require_HotkeysProvider.useDefaultHotkeysOptions();
|
|
40
|
+
const manager = (0, _tanstack_hotkeys.getSequenceManager)();
|
|
41
|
+
let registration = null;
|
|
40
42
|
(0, solid_js.createEffect)(() => {
|
|
41
43
|
const resolvedSequence = typeof sequence === "function" ? sequence() : sequence;
|
|
42
44
|
const resolvedOptions = typeof options === "function" ? options() : options;
|
|
43
|
-
const
|
|
45
|
+
const mergedOptions = {
|
|
44
46
|
...defaultOptions.hotkeySequence,
|
|
45
47
|
...resolvedOptions
|
|
46
48
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
51
|
-
if (
|
|
52
|
-
|
|
49
|
+
const { target: _target, ...optionsWithoutTarget } = mergedOptions;
|
|
50
|
+
if (resolvedSequence.length === 0) return;
|
|
51
|
+
const resolvedTarget = "target" in mergedOptions ? mergedOptions.target ?? null : typeof document !== "undefined" ? document : null;
|
|
52
|
+
if (!resolvedTarget) return;
|
|
53
|
+
if (registration?.isActive) {
|
|
54
|
+
registration.unregister();
|
|
55
|
+
registration = null;
|
|
56
|
+
}
|
|
57
|
+
registration = manager.register(resolvedSequence, callback, {
|
|
58
|
+
...mergedOptions,
|
|
59
|
+
target: resolvedTarget
|
|
60
|
+
});
|
|
61
|
+
if (registration.isActive) {
|
|
62
|
+
registration.callback = callback;
|
|
63
|
+
registration.setOptions(optionsWithoutTarget);
|
|
64
|
+
}
|
|
65
|
+
(0, solid_js.onCleanup)(() => {
|
|
66
|
+
if (registration?.isActive) {
|
|
67
|
+
registration.unregister();
|
|
68
|
+
registration = null;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
53
71
|
});
|
|
54
72
|
}
|
|
55
73
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createHotkeySequence.cjs","names":["useDefaultHotkeysOptions"],"sources":["../src/createHotkeySequence.ts"],"sourcesContent":["import { createEffect, onCleanup } from 'solid-js'\nimport { getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProvider'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n} from '@tanstack/hotkeys'\n\nexport interface CreateHotkeySequenceOptions extends Omit<\n SequenceOptions,\n '
|
|
1
|
+
{"version":3,"file":"createHotkeySequence.cjs","names":["useDefaultHotkeysOptions"],"sources":["../src/createHotkeySequence.ts"],"sourcesContent":["import { createEffect, onCleanup } from 'solid-js'\nimport { getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProvider'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\n\nexport interface CreateHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'target'\n> {\n /**\n * The DOM element to attach the event listener to.\n * Can be a direct DOM element, an accessor, or null. Defaults to document.\n */\n target?: HTMLElement | Document | Window | null\n}\n\n/**\n * SolidJS primitive for registering a keyboard shortcut sequence (Vim-style).\n *\n * This primitive allows you to register multi-key sequences like 'g g' or 'd d'\n * that trigger when the full sequence is pressed within a timeout.\n *\n * @param sequence - Array of hotkey strings that form the sequence (or accessor function)\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior (or accessor function)\n *\n * @example\n * ```tsx\n * function VimEditor() {\n * // 'g g' to go to top\n * createHotkeySequence(['G', 'G'], () => {\n * scrollToTop()\n * })\n *\n * // 'd d' to delete line\n * createHotkeySequence(['D', 'D'], () => {\n * deleteLine()\n * })\n *\n * // 'd i w' to delete inner word\n * createHotkeySequence(['D', 'I', 'W'], () => {\n * deleteInnerWord()\n * }, { timeout: 500 })\n *\n * return <div>...</div>\n * }\n * ```\n */\nexport function createHotkeySequence(\n sequence: HotkeySequence | (() => HotkeySequence),\n callback: HotkeyCallback,\n options:\n | CreateHotkeySequenceOptions\n | (() => CreateHotkeySequenceOptions) = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getSequenceManager()\n\n let registration: SequenceRegistrationHandle | null = null\n\n createEffect(() => {\n // Resolve reactive values\n const resolvedSequence =\n typeof sequence === 'function' ? sequence() : sequence\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as CreateHotkeySequenceOptions\n\n // Extract options without target (target is handled separately)\n const { target: _target, ...optionsWithoutTarget } = mergedOptions\n\n if (resolvedSequence.length === 0) {\n return\n }\n\n // Resolve target: when explicitly provided (even as null), use it and skip if null.\n // When not provided, default to document. Matches createHotkey.\n const resolvedTarget =\n 'target' in mergedOptions\n ? (mergedOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (!resolvedTarget) {\n return\n }\n\n // Unregister previous registration if it exists\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n // Register the sequence\n registration = manager.register(resolvedSequence, callback, {\n ...mergedOptions,\n target: resolvedTarget,\n })\n\n // Sync callback and options on every effect run\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n // Cleanup on disposal\n onCleanup(() => {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAgB,qBACd,UACA,UACA,UAE0C,EAAE,EACtC;CACN,MAAM,iBAAiBA,kDAA0B;CACjD,MAAM,qDAA8B;CAEpC,IAAI,eAAkD;AAEtD,kCAAmB;EAEjB,MAAM,mBACJ,OAAO,aAAa,aAAa,UAAU,GAAG;EAChD,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAEpE,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;EAGD,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB;AAErD,MAAI,iBAAiB,WAAW,EAC9B;EAKF,MAAM,iBACJ,YAAY,gBACP,cAAc,UAAU,OACzB,OAAO,aAAa,cAClB,WACA;AAER,MAAI,CAAC,eACH;AAIF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAIjB,iBAAe,QAAQ,SAAS,kBAAkB,UAAU;GAC1D,GAAG;GACH,QAAQ;GACT,CAAC;AAGF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAI/C,gCAAgB;AACd,OAAI,cAAc,UAAU;AAC1B,iBAAa,YAAY;AACzB,mBAAe;;IAEjB;GACF"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
|
|
2
2
|
|
|
3
3
|
//#region src/createHotkeySequence.d.ts
|
|
4
|
-
interface CreateHotkeySequenceOptions extends Omit<SequenceOptions, '
|
|
5
|
-
/**
|
|
6
|
-
|
|
4
|
+
interface CreateHotkeySequenceOptions extends Omit<SequenceOptions, 'target'> {
|
|
5
|
+
/**
|
|
6
|
+
* The DOM element to attach the event listener to.
|
|
7
|
+
* Can be a direct DOM element, an accessor, or null. Defaults to document.
|
|
8
|
+
*/
|
|
9
|
+
target?: HTMLElement | Document | Window | null;
|
|
7
10
|
}
|
|
8
11
|
/**
|
|
9
12
|
* SolidJS primitive for registering a keyboard shortcut sequence (Vim-style).
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
|
|
2
2
|
|
|
3
3
|
//#region src/createHotkeySequence.d.ts
|
|
4
|
-
interface CreateHotkeySequenceOptions extends Omit<SequenceOptions, '
|
|
5
|
-
/**
|
|
6
|
-
|
|
4
|
+
interface CreateHotkeySequenceOptions extends Omit<SequenceOptions, 'target'> {
|
|
5
|
+
/**
|
|
6
|
+
* The DOM element to attach the event listener to.
|
|
7
|
+
* Can be a direct DOM element, an accessor, or null. Defaults to document.
|
|
8
|
+
*/
|
|
9
|
+
target?: HTMLElement | Document | Window | null;
|
|
7
10
|
}
|
|
8
11
|
/**
|
|
9
12
|
* SolidJS primitive for registering a keyboard shortcut sequence (Vim-style).
|
|
@@ -37,19 +37,37 @@ import { createEffect, onCleanup } from "solid-js";
|
|
|
37
37
|
*/
|
|
38
38
|
function createHotkeySequence(sequence, callback, options = {}) {
|
|
39
39
|
const defaultOptions = useDefaultHotkeysOptions();
|
|
40
|
+
const manager = getSequenceManager();
|
|
41
|
+
let registration = null;
|
|
40
42
|
createEffect(() => {
|
|
41
43
|
const resolvedSequence = typeof sequence === "function" ? sequence() : sequence;
|
|
42
44
|
const resolvedOptions = typeof options === "function" ? options() : options;
|
|
43
|
-
const
|
|
45
|
+
const mergedOptions = {
|
|
44
46
|
...defaultOptions.hotkeySequence,
|
|
45
47
|
...resolvedOptions
|
|
46
48
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
51
|
-
if (
|
|
52
|
-
|
|
49
|
+
const { target: _target, ...optionsWithoutTarget } = mergedOptions;
|
|
50
|
+
if (resolvedSequence.length === 0) return;
|
|
51
|
+
const resolvedTarget = "target" in mergedOptions ? mergedOptions.target ?? null : typeof document !== "undefined" ? document : null;
|
|
52
|
+
if (!resolvedTarget) return;
|
|
53
|
+
if (registration?.isActive) {
|
|
54
|
+
registration.unregister();
|
|
55
|
+
registration = null;
|
|
56
|
+
}
|
|
57
|
+
registration = manager.register(resolvedSequence, callback, {
|
|
58
|
+
...mergedOptions,
|
|
59
|
+
target: resolvedTarget
|
|
60
|
+
});
|
|
61
|
+
if (registration.isActive) {
|
|
62
|
+
registration.callback = callback;
|
|
63
|
+
registration.setOptions(optionsWithoutTarget);
|
|
64
|
+
}
|
|
65
|
+
onCleanup(() => {
|
|
66
|
+
if (registration?.isActive) {
|
|
67
|
+
registration.unregister();
|
|
68
|
+
registration = null;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
53
71
|
});
|
|
54
72
|
}
|
|
55
73
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createHotkeySequence.js","names":[],"sources":["../src/createHotkeySequence.ts"],"sourcesContent":["import { createEffect, onCleanup } from 'solid-js'\nimport { getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProvider'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n} from '@tanstack/hotkeys'\n\nexport interface CreateHotkeySequenceOptions extends Omit<\n SequenceOptions,\n '
|
|
1
|
+
{"version":3,"file":"createHotkeySequence.js","names":[],"sources":["../src/createHotkeySequence.ts"],"sourcesContent":["import { createEffect, onCleanup } from 'solid-js'\nimport { getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProvider'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\n\nexport interface CreateHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'target'\n> {\n /**\n * The DOM element to attach the event listener to.\n * Can be a direct DOM element, an accessor, or null. Defaults to document.\n */\n target?: HTMLElement | Document | Window | null\n}\n\n/**\n * SolidJS primitive for registering a keyboard shortcut sequence (Vim-style).\n *\n * This primitive allows you to register multi-key sequences like 'g g' or 'd d'\n * that trigger when the full sequence is pressed within a timeout.\n *\n * @param sequence - Array of hotkey strings that form the sequence (or accessor function)\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior (or accessor function)\n *\n * @example\n * ```tsx\n * function VimEditor() {\n * // 'g g' to go to top\n * createHotkeySequence(['G', 'G'], () => {\n * scrollToTop()\n * })\n *\n * // 'd d' to delete line\n * createHotkeySequence(['D', 'D'], () => {\n * deleteLine()\n * })\n *\n * // 'd i w' to delete inner word\n * createHotkeySequence(['D', 'I', 'W'], () => {\n * deleteInnerWord()\n * }, { timeout: 500 })\n *\n * return <div>...</div>\n * }\n * ```\n */\nexport function createHotkeySequence(\n sequence: HotkeySequence | (() => HotkeySequence),\n callback: HotkeyCallback,\n options:\n | CreateHotkeySequenceOptions\n | (() => CreateHotkeySequenceOptions) = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getSequenceManager()\n\n let registration: SequenceRegistrationHandle | null = null\n\n createEffect(() => {\n // Resolve reactive values\n const resolvedSequence =\n typeof sequence === 'function' ? sequence() : sequence\n const resolvedOptions = typeof options === 'function' ? options() : options\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as CreateHotkeySequenceOptions\n\n // Extract options without target (target is handled separately)\n const { target: _target, ...optionsWithoutTarget } = mergedOptions\n\n if (resolvedSequence.length === 0) {\n return\n }\n\n // Resolve target: when explicitly provided (even as null), use it and skip if null.\n // When not provided, default to document. Matches createHotkey.\n const resolvedTarget =\n 'target' in mergedOptions\n ? (mergedOptions.target ?? null)\n : typeof document !== 'undefined'\n ? document\n : null\n\n if (!resolvedTarget) {\n return\n }\n\n // Unregister previous registration if it exists\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n // Register the sequence\n registration = manager.register(resolvedSequence, callback, {\n ...mergedOptions,\n target: resolvedTarget,\n })\n\n // Sync callback and options on every effect run\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n // Cleanup on disposal\n onCleanup(() => {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAgB,qBACd,UACA,UACA,UAE0C,EAAE,EACtC;CACN,MAAM,iBAAiB,0BAA0B;CACjD,MAAM,UAAU,oBAAoB;CAEpC,IAAI,eAAkD;AAEtD,oBAAmB;EAEjB,MAAM,mBACJ,OAAO,aAAa,aAAa,UAAU,GAAG;EAChD,MAAM,kBAAkB,OAAO,YAAY,aAAa,SAAS,GAAG;EAEpE,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;EAGD,MAAM,EAAE,QAAQ,SAAS,GAAG,yBAAyB;AAErD,MAAI,iBAAiB,WAAW,EAC9B;EAKF,MAAM,iBACJ,YAAY,gBACP,cAAc,UAAU,OACzB,OAAO,aAAa,cAClB,WACA;AAER,MAAI,CAAC,eACH;AAIF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAIjB,iBAAe,QAAQ,SAAS,kBAAkB,UAAU;GAC1D,GAAG;GACH,QAAQ;GACT,CAAC;AAGF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAI/C,kBAAgB;AACd,OAAI,cAAc,UAAU;AAC1B,iBAAa,YAAY;AACzB,mBAAe;;IAEjB;GACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/solid-hotkeys",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "SolidJS 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/solid-store": "^0.
|
|
46
|
-
"@tanstack/hotkeys": "0.
|
|
45
|
+
"@tanstack/solid-store": "^0.9.1",
|
|
46
|
+
"@tanstack/hotkeys": "0.3.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@solidjs/testing-library": "^0.8.10",
|
|
@@ -5,14 +5,18 @@ import type {
|
|
|
5
5
|
HotkeyCallback,
|
|
6
6
|
HotkeySequence,
|
|
7
7
|
SequenceOptions,
|
|
8
|
+
SequenceRegistrationHandle,
|
|
8
9
|
} from '@tanstack/hotkeys'
|
|
9
10
|
|
|
10
11
|
export interface CreateHotkeySequenceOptions extends Omit<
|
|
11
12
|
SequenceOptions,
|
|
12
|
-
'
|
|
13
|
+
'target'
|
|
13
14
|
> {
|
|
14
|
-
/**
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The DOM element to attach the event listener to.
|
|
17
|
+
* Can be a direct DOM element, an accessor, or null. Defaults to document.
|
|
18
|
+
*/
|
|
19
|
+
target?: HTMLElement | Document | Window | null
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
/**
|
|
@@ -55,6 +59,9 @@ export function createHotkeySequence(
|
|
|
55
59
|
| (() => CreateHotkeySequenceOptions) = {},
|
|
56
60
|
): void {
|
|
57
61
|
const defaultOptions = useDefaultHotkeysOptions()
|
|
62
|
+
const manager = getSequenceManager()
|
|
63
|
+
|
|
64
|
+
let registration: SequenceRegistrationHandle | null = null
|
|
58
65
|
|
|
59
66
|
createEffect(() => {
|
|
60
67
|
// Resolve reactive values
|
|
@@ -67,27 +74,50 @@ export function createHotkeySequence(
|
|
|
67
74
|
...resolvedOptions,
|
|
68
75
|
} as CreateHotkeySequenceOptions
|
|
69
76
|
|
|
70
|
-
|
|
77
|
+
// Extract options without target (target is handled separately)
|
|
78
|
+
const { target: _target, ...optionsWithoutTarget } = mergedOptions
|
|
71
79
|
|
|
72
|
-
if (
|
|
80
|
+
if (resolvedSequence.length === 0) {
|
|
73
81
|
return
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
|
|
84
|
+
// Resolve target: when explicitly provided (even as null), use it and skip if null.
|
|
85
|
+
// When not provided, default to document. Matches createHotkey.
|
|
86
|
+
const resolvedTarget =
|
|
87
|
+
'target' in mergedOptions
|
|
88
|
+
? (mergedOptions.target ?? null)
|
|
89
|
+
: typeof document !== 'undefined'
|
|
90
|
+
? document
|
|
91
|
+
: null
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
registerOptions.timeout = sequenceOptions.timeout
|
|
82
|
-
if (sequenceOptions.platform !== undefined)
|
|
83
|
-
registerOptions.platform = sequenceOptions.platform
|
|
93
|
+
if (!resolvedTarget) {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
84
96
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
// Unregister previous registration if it exists
|
|
98
|
+
if (registration?.isActive) {
|
|
99
|
+
registration.unregister()
|
|
100
|
+
registration = null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Register the sequence
|
|
104
|
+
registration = manager.register(resolvedSequence, callback, {
|
|
105
|
+
...mergedOptions,
|
|
106
|
+
target: resolvedTarget,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// Sync callback and options on every effect run
|
|
110
|
+
if (registration.isActive) {
|
|
111
|
+
registration.callback = callback
|
|
112
|
+
registration.setOptions(optionsWithoutTarget)
|
|
113
|
+
}
|
|
90
114
|
|
|
91
|
-
|
|
115
|
+
// Cleanup on disposal
|
|
116
|
+
onCleanup(() => {
|
|
117
|
+
if (registration?.isActive) {
|
|
118
|
+
registration.unregister()
|
|
119
|
+
registration = null
|
|
120
|
+
}
|
|
121
|
+
})
|
|
92
122
|
})
|
|
93
123
|
}
|