@tanstack/solid-hotkeys 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/HotkeysProvider.d.ts +39 -8
- package/dist/HotkeysProvider.js +35 -3
- package/dist/appHotkeys.d.ts +42 -0
- package/dist/appHotkeys.js +64 -0
- package/dist/createHotkey.d.ts +33 -60
- package/dist/createHotkey.js +6 -113
- package/dist/createHotkeyHint.d.ts +6 -7
- package/dist/createHotkeyHint.js +13 -7
- package/dist/createHotkeyRecorder.d.ts +22 -41
- package/dist/createHotkeyRecorder.js +12 -55
- package/dist/createHotkeyRegistrations.d.ts +14 -17
- package/dist/createHotkeyRegistrations.js +14 -13
- package/dist/createHotkeys.d.ts +16 -43
- package/dist/createHotkeys.js +23 -93
- package/dist/defaultOptions.js +24 -0
- package/dist/index.d.ts +6 -8
- package/dist/index.js +6 -8
- package/package.json +3 -3
- package/dist/createHotkeySequence.d.ts +0 -53
- package/dist/createHotkeySequence.js +0 -106
- package/dist/createHotkeySequenceRecorder.d.ts +0 -16
- package/dist/createHotkeySequenceRecorder.js +0 -43
- package/dist/createHotkeySequences.d.ts +0 -54
- package/dist/createHotkeySequences.js +0 -111
|
@@ -1,76 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useDefaultHotkeysOptionsSource } from "./defaultOptions.js";
|
|
2
2
|
import { HotkeyRecorder } from "@tanstack/hotkeys";
|
|
3
3
|
import { createEffect, onCleanup } from "solid-js";
|
|
4
4
|
import { useSelector } from "@tanstack/solid-store";
|
|
5
5
|
|
|
6
6
|
//#region src/createHotkeyRecorder.ts
|
|
7
|
-
/**
|
|
8
|
-
* SolidJS primitive for recording keyboard shortcuts.
|
|
9
|
-
*
|
|
10
|
-
* This primitive provides a thin wrapper around the framework-agnostic `HotkeyRecorder`
|
|
11
|
-
* class, managing all the complexity of capturing keyboard events, converting them
|
|
12
|
-
* to hotkey strings, and handling edge cases like Escape to cancel or Backspace/Delete
|
|
13
|
-
* to clear.
|
|
14
|
-
*
|
|
15
|
-
* This primitive uses `useSelector` from `@tanstack/solid-store` to subscribe
|
|
16
|
-
* to the recorder's store state (same pattern as useHotkeyRecorder in React).
|
|
17
|
-
*
|
|
18
|
-
* @param options - Configuration options for the recorder (or accessor function)
|
|
19
|
-
* @returns An object with recording state signals and control functions
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```tsx
|
|
23
|
-
* function ShortcutSettings() {
|
|
24
|
-
* const [shortcut, setShortcut] = createSignal<Hotkey>('Mod+S')
|
|
25
|
-
*
|
|
26
|
-
* const recorder = createHotkeyRecorder({
|
|
27
|
-
* onRecord: (hotkey) => {
|
|
28
|
-
* setShortcut(hotkey)
|
|
29
|
-
* },
|
|
30
|
-
* onCancel: () => {
|
|
31
|
-
* console.log('Recording cancelled')
|
|
32
|
-
* },
|
|
33
|
-
* })
|
|
34
|
-
*
|
|
35
|
-
* return (
|
|
36
|
-
* <div>
|
|
37
|
-
* <button onClick={recorder.startRecording}>
|
|
38
|
-
* {recorder.isRecording() ? 'Recording...' : 'Edit Shortcut'}
|
|
39
|
-
* </button>
|
|
40
|
-
* <Show when={recorder.recordedHotkey()}>
|
|
41
|
-
* <div>Recording: {recorder.recordedHotkey()}</div>
|
|
42
|
-
* </Show>
|
|
43
|
-
* </div>
|
|
44
|
-
* )
|
|
45
|
-
* }
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
7
|
function createHotkeyRecorder(options) {
|
|
49
|
-
const defaultOptions =
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
...
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const recorder = new HotkeyRecorder(mergedOptions);
|
|
8
|
+
const defaultOptions = useDefaultHotkeysOptionsSource();
|
|
9
|
+
const resolve = () => ({
|
|
10
|
+
...defaultOptions().hotkeyRecorder,
|
|
11
|
+
...typeof options === "function" ? options() : options
|
|
12
|
+
});
|
|
13
|
+
const recorder = new HotkeyRecorder(resolve());
|
|
56
14
|
const isRecording = useSelector(recorder.store, (state) => state.isRecording);
|
|
15
|
+
const steps = useSelector(recorder.store, (state) => state.steps);
|
|
57
16
|
const recordedHotkey = useSelector(recorder.store, (state) => state.recordedHotkey);
|
|
58
17
|
createEffect(() => {
|
|
59
|
-
|
|
60
|
-
recorder.setOptions({
|
|
61
|
-
...defaultOptions.hotkeyRecorder,
|
|
62
|
-
...resolved
|
|
63
|
-
});
|
|
18
|
+
recorder.setOptions(resolve());
|
|
64
19
|
});
|
|
65
20
|
onCleanup(() => {
|
|
66
21
|
recorder.destroy();
|
|
67
22
|
});
|
|
68
23
|
return {
|
|
69
24
|
isRecording,
|
|
25
|
+
steps,
|
|
70
26
|
recordedHotkey,
|
|
71
27
|
startRecording: () => recorder.start(),
|
|
72
28
|
stopRecording: () => recorder.stop(),
|
|
73
|
-
cancelRecording: () => recorder.cancel()
|
|
29
|
+
cancelRecording: () => recorder.cancel(),
|
|
30
|
+
commitRecording: () => recorder.commit()
|
|
74
31
|
};
|
|
75
32
|
}
|
|
76
33
|
|
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
import { HotkeyRegistrationView
|
|
1
|
+
import { HotkeyRegistrationView } from "@tanstack/hotkeys";
|
|
2
2
|
//#region src/createHotkeyRegistrations.d.ts
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
hotkeys: () => Array<HotkeyRegistrationView>;
|
|
9
|
-
/** Accessor for all registered sequences */
|
|
10
|
-
sequences: () => Array<SequenceRegistrationView>;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Solid primitive that reactively reads all hotkey and sequence registrations
|
|
14
|
-
* from the singleton managers.
|
|
4
|
+
* Solid primitive that reactively reads every hotkey registration, chords and
|
|
5
|
+
* sequences, from the singleton HotkeyManager. Narrow each view with `kind`.
|
|
6
|
+
*
|
|
7
|
+
* This primitive does NOT require the HotkeysProvider.
|
|
15
8
|
*
|
|
16
|
-
*
|
|
17
|
-
* It subscribes to both HotkeyManager and SequenceManager stores and
|
|
18
|
-
* triggers reactivity when registrations change.
|
|
9
|
+
* @returns An accessor for all registrations, without callbacks
|
|
19
10
|
*
|
|
20
|
-
* @
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const registrations = createHotkeyRegistrations()
|
|
14
|
+
* <For each={registrations()}>
|
|
15
|
+
* {(reg) => <li>{reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}</li>}
|
|
16
|
+
* </For>
|
|
17
|
+
* ```
|
|
21
18
|
*/
|
|
22
|
-
export declare function createHotkeyRegistrations():
|
|
19
|
+
export declare function createHotkeyRegistrations(): () => Array<HotkeyRegistrationView>;
|
|
23
20
|
//#endregion
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
import { getHotkeyManager,
|
|
1
|
+
import { getHotkeyManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
|
|
2
2
|
import { useSelector } from "@tanstack/solid-store";
|
|
3
3
|
|
|
4
4
|
//#region src/createHotkeyRegistrations.ts
|
|
5
5
|
/**
|
|
6
|
-
* Solid primitive that reactively reads
|
|
7
|
-
* from the singleton
|
|
6
|
+
* Solid primitive that reactively reads every hotkey registration, chords and
|
|
7
|
+
* sequences, from the singleton HotkeyManager. Narrow each view with `kind`.
|
|
8
8
|
*
|
|
9
|
-
* This
|
|
10
|
-
* It subscribes to both HotkeyManager and SequenceManager stores and
|
|
11
|
-
* triggers reactivity when registrations change.
|
|
9
|
+
* This primitive does NOT require the HotkeysProvider.
|
|
12
10
|
*
|
|
13
|
-
* @returns
|
|
11
|
+
* @returns An accessor for all registrations, without callbacks
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const registrations = createHotkeyRegistrations()
|
|
16
|
+
* <For each={registrations()}>
|
|
17
|
+
* {(reg) => <li>{reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}</li>}
|
|
18
|
+
* </For>
|
|
19
|
+
* ```
|
|
14
20
|
*/
|
|
15
21
|
function createHotkeyRegistrations() {
|
|
16
|
-
|
|
17
|
-
const sequenceManager = getSequenceManager();
|
|
18
|
-
return {
|
|
19
|
-
hotkeys: useSelector(hotkeyManager.registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView)),
|
|
20
|
-
sequences: useSelector(sequenceManager.registrations, (state) => Array.from(state.values()))
|
|
21
|
-
};
|
|
22
|
+
return useSelector(getHotkeyManager().registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView));
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
//#endregion
|
package/dist/createHotkeys.d.ts
CHANGED
|
@@ -1,54 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CreateHotkeyCommonOptions, CreateHotkeyTarget } from "./createHotkey.js";
|
|
2
|
+
import { AdapterHotkeyDefinition } from "@tanstack/hotkeys/adapter";
|
|
3
3
|
//#region src/createHotkeys.d.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* One hotkey for `createHotkeys`. Chords accept `requireReset`; sequences
|
|
6
|
+
* accept `timeout`; hotkeys of unknown kind accept only shared options.
|
|
6
7
|
*/
|
|
7
|
-
export
|
|
8
|
-
/** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */
|
|
9
|
-
hotkey: RegisterableHotkey;
|
|
10
|
-
/** The function to call when the hotkey is pressed */
|
|
11
|
-
callback: HotkeyCallback;
|
|
12
|
-
/** Per-hotkey options (merged on top of commonOptions) */
|
|
13
|
-
options?: CreateHotkeyOptions;
|
|
14
|
-
}
|
|
8
|
+
export type CreateHotkeyDefinition = AdapterHotkeyDefinition<CreateHotkeyTarget>;
|
|
15
9
|
/**
|
|
16
|
-
*
|
|
10
|
+
* Solid primitive for registering several hotkeys at once: chords, sequences,
|
|
11
|
+
* and app hotkeys. Pass an accessor for a reactive list.
|
|
17
12
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* Options are merged in this order:
|
|
23
|
-
* HotkeysProvider defaults < commonOptions < per-definition options
|
|
24
|
-
*
|
|
25
|
-
* @param hotkeys - Array of hotkey definitions, or accessor returning them
|
|
26
|
-
* @param commonOptions - Shared options applied to all hotkeys, or accessor
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```tsx
|
|
30
|
-
* function Editor() {
|
|
31
|
-
* createHotkeys([
|
|
32
|
-
* { hotkey: 'Mod+S', callback: () => save() },
|
|
33
|
-
* { hotkey: 'Mod+Z', callback: () => undo() },
|
|
34
|
-
* { hotkey: 'Escape', callback: () => close() },
|
|
35
|
-
* ])
|
|
36
|
-
* }
|
|
37
|
-
* ```
|
|
13
|
+
* Options merge in this order: provider defaults, app hotkey declaration,
|
|
14
|
+
* commonOptions, per-hotkey options. Each option applies only to the kinds
|
|
15
|
+
* that support it. Unbound app hotkeys and empty sequences register nothing.
|
|
16
|
+
* A `null` target skips registration until it is set.
|
|
38
17
|
*
|
|
39
18
|
* @example
|
|
40
19
|
* ```tsx
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* callback: item.action,
|
|
46
|
-
* options: { enabled: item.enabled },
|
|
47
|
-
* })),
|
|
48
|
-
* { preventDefault: true },
|
|
49
|
-
* )
|
|
50
|
-
* }
|
|
20
|
+
* createHotkeys([
|
|
21
|
+
* { hotkey: 'Mod+S', callback: () => save() },
|
|
22
|
+
* { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } },
|
|
23
|
+
* ])
|
|
51
24
|
* ```
|
|
52
25
|
*/
|
|
53
|
-
export declare function createHotkeys(hotkeys:
|
|
26
|
+
export declare function createHotkeys(hotkeys: ReadonlyArray<CreateHotkeyDefinition> | (() => ReadonlyArray<CreateHotkeyDefinition>), commonOptions?: CreateHotkeyCommonOptions | (() => CreateHotkeyCommonOptions)): void;
|
|
54
27
|
//#endregion
|
package/dist/createHotkeys.js
CHANGED
|
@@ -1,109 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { detectPlatform, getHotkeyManager, normalizeRegisterableHotkey } from "@tanstack/hotkeys";
|
|
1
|
+
import { useAppHotkeysStoreSource, useDefaultHotkeysOptionsSource } from "./defaultOptions.js";
|
|
3
2
|
import { createEffect, onCleanup, untrack } from "solid-js";
|
|
3
|
+
import { createHotkeyBindings, prepareHotkeyDefinitions } from "@tanstack/hotkeys/adapter";
|
|
4
4
|
|
|
5
5
|
//#region src/createHotkeys.ts
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Solid primitive for registering several hotkeys at once: chords, sequences,
|
|
8
|
+
* and app hotkeys. Pass an accessor for a reactive list.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Options are merged in this order:
|
|
14
|
-
* HotkeysProvider defaults < commonOptions < per-definition options
|
|
15
|
-
*
|
|
16
|
-
* @param hotkeys - Array of hotkey definitions, or accessor returning them
|
|
17
|
-
* @param commonOptions - Shared options applied to all hotkeys, or accessor
|
|
10
|
+
* Options merge in this order: provider defaults, app hotkey declaration,
|
|
11
|
+
* commonOptions, per-hotkey options. Each option applies only to the kinds
|
|
12
|
+
* that support it. Unbound app hotkeys and empty sequences register nothing.
|
|
13
|
+
* A `null` target skips registration until it is set.
|
|
18
14
|
*
|
|
19
15
|
* @example
|
|
20
16
|
* ```tsx
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* { hotkey: 'Escape', callback: () => close() },
|
|
26
|
-
* ])
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```tsx
|
|
32
|
-
* function MenuShortcuts(props) {
|
|
33
|
-
* createHotkeys(
|
|
34
|
-
* () => props.items.map((item) => ({
|
|
35
|
-
* hotkey: item.shortcut,
|
|
36
|
-
* callback: item.action,
|
|
37
|
-
* options: { enabled: item.enabled },
|
|
38
|
-
* })),
|
|
39
|
-
* { preventDefault: true },
|
|
40
|
-
* )
|
|
41
|
-
* }
|
|
17
|
+
* createHotkeys([
|
|
18
|
+
* { hotkey: 'Mod+S', callback: () => save() },
|
|
19
|
+
* { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } },
|
|
20
|
+
* ])
|
|
42
21
|
* ```
|
|
43
22
|
*/
|
|
44
23
|
function createHotkeys(hotkeys, commonOptions = {}) {
|
|
45
|
-
const defaultOptions =
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
onCleanup(() =>
|
|
49
|
-
for (const { handle } of registrations.values()) if (handle.isActive) handle.unregister();
|
|
50
|
-
registrations.clear();
|
|
51
|
-
});
|
|
24
|
+
const defaultOptions = useDefaultHotkeysOptionsSource();
|
|
25
|
+
const appHotkeys = useAppHotkeysStoreSource();
|
|
26
|
+
const bindings = createHotkeyBindings();
|
|
27
|
+
onCleanup(() => bindings.destroy());
|
|
52
28
|
createEffect(() => {
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const resolvedDefOptions = def.options ?? {};
|
|
60
|
-
const mergedOptions = {
|
|
61
|
-
...defaultOptions.hotkey,
|
|
62
|
-
...resolvedCommonOptions,
|
|
63
|
-
...resolvedDefOptions
|
|
64
|
-
};
|
|
65
|
-
const platform = mergedOptions.platform ?? detectPlatform();
|
|
66
|
-
const hotkeyString = normalizeRegisterableHotkey(def.hotkey, platform);
|
|
67
|
-
const resolvedTarget = "target" in mergedOptions ? mergedOptions.target ?? null : typeof document !== "undefined" ? document : null;
|
|
68
|
-
if (!resolvedTarget) continue;
|
|
69
|
-
const registrationKey = `${i}:${hotkeyString}`;
|
|
70
|
-
nextKeys.add(registrationKey);
|
|
71
|
-
prepared.push({
|
|
72
|
-
registrationKey,
|
|
73
|
-
def,
|
|
74
|
-
mergedOptions,
|
|
75
|
-
hotkeyString,
|
|
76
|
-
resolvedTarget
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
untrack(() => {
|
|
80
|
-
for (const [key, record] of [...registrations.entries()]) if (!nextKeys.has(key)) {
|
|
81
|
-
if (record.handle.isActive) record.handle.unregister();
|
|
82
|
-
registrations.delete(key);
|
|
83
|
-
}
|
|
84
|
-
for (const p of prepared) {
|
|
85
|
-
const existing = registrations.get(p.registrationKey);
|
|
86
|
-
if (existing?.handle.isActive && existing.target === p.resolvedTarget) {
|
|
87
|
-
existing.handle.callback = p.def.callback;
|
|
88
|
-
const { target: _target, ...optionsWithoutTarget } = p.mergedOptions;
|
|
89
|
-
existing.handle.setOptions(optionsWithoutTarget);
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (existing) {
|
|
93
|
-
if (existing.handle.isActive) existing.handle.unregister();
|
|
94
|
-
registrations.delete(p.registrationKey);
|
|
95
|
-
}
|
|
96
|
-
const { target: _target, ...optionsWithoutTarget } = p.mergedOptions;
|
|
97
|
-
const handle = manager.register(p.hotkeyString, p.def.callback, {
|
|
98
|
-
...optionsWithoutTarget,
|
|
99
|
-
target: p.resolvedTarget
|
|
100
|
-
});
|
|
101
|
-
registrations.set(p.registrationKey, {
|
|
102
|
-
handle,
|
|
103
|
-
target: p.resolvedTarget
|
|
104
|
-
});
|
|
105
|
-
}
|
|
29
|
+
appHotkeys.state();
|
|
30
|
+
const definitions = prepareHotkeyDefinitions(typeof hotkeys === "function" ? hotkeys() : hotkeys, {
|
|
31
|
+
appHotkeysStore: appHotkeys.store,
|
|
32
|
+
defaults: defaultOptions().hotkey,
|
|
33
|
+
commonOptions: typeof commonOptions === "function" ? commonOptions() : commonOptions,
|
|
34
|
+
resolveTarget: (target) => target
|
|
106
35
|
});
|
|
36
|
+
untrack(() => bindings.update(definitions));
|
|
107
37
|
});
|
|
108
38
|
}
|
|
109
39
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useHotkeysContext } from "./HotkeysProvider.js";
|
|
2
|
+
import { Store, useSelector } from "@tanstack/solid-store";
|
|
3
|
+
|
|
4
|
+
//#region src/defaultOptions.ts
|
|
5
|
+
/** Captures the context once and reads current defaults inside reactive computations. */
|
|
6
|
+
function useDefaultHotkeysOptionsSource() {
|
|
7
|
+
const context = useHotkeysContext();
|
|
8
|
+
return () => context?.defaultOptions ?? {};
|
|
9
|
+
}
|
|
10
|
+
const NO_APP_HOTKEYS = new Store(null);
|
|
11
|
+
/**
|
|
12
|
+
* The provider's app hotkeys store and an accessor that tracks its state, so
|
|
13
|
+
* computations re-run when app hotkeys or custom hotkeys change.
|
|
14
|
+
*/
|
|
15
|
+
function useAppHotkeysStoreSource() {
|
|
16
|
+
const store = useHotkeysContext()?.appHotkeysStore ?? null;
|
|
17
|
+
return {
|
|
18
|
+
store,
|
|
19
|
+
state: useSelector(store?.store ?? NO_APP_HOTKEYS, (value) => value)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
export { useAppHotkeysStoreSource, useDefaultHotkeysOptionsSource };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import { CreateHotkeyOptions, createHotkey } from "./createHotkey.js";
|
|
2
|
-
import { CreateHotkeySequenceOptions, createHotkeySequence } from "./createHotkeySequence.js";
|
|
1
|
+
import { CreateAppHotkeyOptions, CreateHotkeyChordOptions, CreateHotkeyCommonOptions, CreateHotkeyOptions, CreateHotkeyOptionsFor, CreateHotkeySequenceOptions, CreateHotkeyTarget, createHotkey } from "./createHotkey.js";
|
|
3
2
|
import { HotkeysProvider, HotkeysProviderOptions, HotkeysProviderProps, useDefaultHotkeysOptions, useHotkeysContext } from "./HotkeysProvider.js";
|
|
4
3
|
import { CreateHotkeyDefinition, createHotkeys } from "./createHotkeys.js";
|
|
4
|
+
import { SolidHotkeyRecorder, createHotkeyRecorder } from "./createHotkeyRecorder.js";
|
|
5
|
+
import { createHotkeyRegistrations } from "./createHotkeyRegistrations.js";
|
|
6
|
+
import { createHotkeyHint } from "./createHotkeyHint.js";
|
|
7
|
+
import { UseCustomHotkeysResult, createAppHotkeyList, useAppHotkeys, useCustomHotkeys } from "./appHotkeys.js";
|
|
5
8
|
import { createHeldKeys } from "./createHeldKeys.js";
|
|
6
9
|
import { createHeldKeyCodes } from "./createHeldKeyCodes.js";
|
|
7
10
|
import { createKeyHold } from "./createKeyHold.js";
|
|
8
|
-
import { CreateHotkeySequenceDefinition, createHotkeySequences } from "./createHotkeySequences.js";
|
|
9
|
-
import { SolidHotkeyRecorder, createHotkeyRecorder } from "./createHotkeyRecorder.js";
|
|
10
|
-
import { HotkeyRegistrationsResult, createHotkeyRegistrations } from "./createHotkeyRegistrations.js";
|
|
11
|
-
import { SolidHotkeySequenceRecorder, createHotkeySequenceRecorder } from "./createHotkeySequenceRecorder.js";
|
|
12
|
-
import { createHotkeyHint } from "./createHotkeyHint.js";
|
|
13
11
|
export * from "@tanstack/hotkeys";
|
|
14
|
-
export { CreateHotkeyDefinition, CreateHotkeyOptions,
|
|
12
|
+
export { CreateAppHotkeyOptions, CreateHotkeyChordOptions, CreateHotkeyCommonOptions, CreateHotkeyDefinition, CreateHotkeyOptions, CreateHotkeyOptionsFor, CreateHotkeySequenceOptions, CreateHotkeyTarget, HotkeysProvider, HotkeysProviderOptions, HotkeysProviderProps, SolidHotkeyRecorder, UseCustomHotkeysResult, createAppHotkeyList, createHeldKeyCodes, createHeldKeys, createHotkey, createHotkeyHint, createHotkeyRecorder, createHotkeyRegistrations, createHotkeys, createKeyHold, useAppHotkeys, useCustomHotkeys, useDefaultHotkeysOptions, useHotkeysContext };
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { HotkeysProvider, useDefaultHotkeysOptions, useHotkeysContext } from "./HotkeysProvider.js";
|
|
2
|
-
import { createHotkey } from "./createHotkey.js";
|
|
3
2
|
import { createHotkeys } from "./createHotkeys.js";
|
|
4
|
-
import {
|
|
5
|
-
import { createHeldKeyCodes } from "./createHeldKeyCodes.js";
|
|
6
|
-
import { createKeyHold } from "./createKeyHold.js";
|
|
7
|
-
import { createHotkeySequence } from "./createHotkeySequence.js";
|
|
8
|
-
import { createHotkeySequences } from "./createHotkeySequences.js";
|
|
3
|
+
import { createHotkey } from "./createHotkey.js";
|
|
9
4
|
import { createHotkeyRecorder } from "./createHotkeyRecorder.js";
|
|
10
5
|
import { createHotkeyRegistrations } from "./createHotkeyRegistrations.js";
|
|
11
|
-
import { createHotkeySequenceRecorder } from "./createHotkeySequenceRecorder.js";
|
|
12
6
|
import { createHotkeyHint } from "./createHotkeyHint.js";
|
|
7
|
+
import { createAppHotkeyList, useAppHotkeys, useCustomHotkeys } from "./appHotkeys.js";
|
|
8
|
+
import { createHeldKeys } from "./createHeldKeys.js";
|
|
9
|
+
import { createHeldKeyCodes } from "./createHeldKeyCodes.js";
|
|
10
|
+
import { createKeyHold } from "./createKeyHold.js";
|
|
13
11
|
|
|
14
12
|
export * from "@tanstack/hotkeys"
|
|
15
13
|
|
|
16
|
-
export { HotkeysProvider, createHeldKeyCodes, createHeldKeys, createHotkey, createHotkeyHint, createHotkeyRecorder, createHotkeyRegistrations,
|
|
14
|
+
export { HotkeysProvider, createAppHotkeyList, createHeldKeyCodes, createHeldKeys, createHotkey, createHotkeyHint, createHotkeyRecorder, createHotkeyRegistrations, createHotkeys, createKeyHold, useAppHotkeys, useCustomHotkeys, useDefaultHotkeysOptions, useHotkeysContext };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/solid-hotkeys",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "SolidJS adapter for TanStack Hotkeys",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@tanstack/hotkeys": "0.
|
|
40
|
-
"@tanstack/solid-store": "^0.11.
|
|
39
|
+
"@tanstack/hotkeys": "0.12.0",
|
|
40
|
+
"@tanstack/solid-store": "^0.11.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@solidjs/testing-library": "^0.8.10",
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
|
|
2
|
-
//#region src/createHotkeySequence.d.ts
|
|
3
|
-
export interface CreateHotkeySequenceOptions extends Omit<SequenceOptions, 'target'> {
|
|
4
|
-
/**
|
|
5
|
-
* The DOM element to attach the event listener to.
|
|
6
|
-
* Can be a direct DOM element, an accessor, or null. Defaults to document.
|
|
7
|
-
*/
|
|
8
|
-
target?: HTMLElement | Document | Window | null;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* SolidJS primitive for registering a keyboard shortcut sequence (Vim-style).
|
|
12
|
-
*
|
|
13
|
-
* This primitive allows you to register multi-key sequences like 'g g' or 'd d'
|
|
14
|
-
* that trigger when the full sequence is pressed within a timeout.
|
|
15
|
-
*
|
|
16
|
-
* Each step may include modifiers. You can chain the same modifier across
|
|
17
|
-
* steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
|
|
18
|
-
* Control, Alt, or Meta pressed alone) are ignored while matching—they do not
|
|
19
|
-
* advance the sequence or reset progress.
|
|
20
|
-
*
|
|
21
|
-
* @param sequence - Array of hotkey strings that form the sequence (or accessor function)
|
|
22
|
-
* @param callback - Function to call when the sequence is completed
|
|
23
|
-
* @param options - Options for the sequence behavior (or accessor function)
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```tsx
|
|
27
|
-
* function VimEditor() {
|
|
28
|
-
* // 'g g' to go to top
|
|
29
|
-
* createHotkeySequence(['G', 'G'], () => {
|
|
30
|
-
* scrollToTop()
|
|
31
|
-
* })
|
|
32
|
-
*
|
|
33
|
-
* // 'd d' to delete line
|
|
34
|
-
* createHotkeySequence(['D', 'D'], () => {
|
|
35
|
-
* deleteLine()
|
|
36
|
-
* })
|
|
37
|
-
*
|
|
38
|
-
* // 'd i w' to delete inner word
|
|
39
|
-
* createHotkeySequence(['D', 'I', 'W'], () => {
|
|
40
|
-
* deleteInnerWord()
|
|
41
|
-
* }, { timeout: 500 })
|
|
42
|
-
*
|
|
43
|
-
* // Same modifier on consecutive steps (bare Shift between chords is ignored)
|
|
44
|
-
* createHotkeySequence(['Shift+R', 'Shift+T'], () => {
|
|
45
|
-
* nextAction()
|
|
46
|
-
* })
|
|
47
|
-
*
|
|
48
|
-
* return <div>...</div>
|
|
49
|
-
* }
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export declare function createHotkeySequence(sequence: HotkeySequence | (() => HotkeySequence), callback: HotkeyCallback, options?: CreateHotkeySequenceOptions | (() => CreateHotkeySequenceOptions)): void;
|
|
53
|
-
//#endregion
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
|
|
2
|
-
import { formatHotkeySequence, getSequenceManager } from "@tanstack/hotkeys";
|
|
3
|
-
import { createEffect, onCleanup, untrack } from "solid-js";
|
|
4
|
-
|
|
5
|
-
//#region src/createHotkeySequence.ts
|
|
6
|
-
/**
|
|
7
|
-
* SolidJS primitive for registering a keyboard shortcut sequence (Vim-style).
|
|
8
|
-
*
|
|
9
|
-
* This primitive allows you to register multi-key sequences like 'g g' or 'd d'
|
|
10
|
-
* that trigger when the full sequence is pressed within a timeout.
|
|
11
|
-
*
|
|
12
|
-
* Each step may include modifiers. You can chain the same modifier across
|
|
13
|
-
* steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
|
|
14
|
-
* Control, Alt, or Meta pressed alone) are ignored while matching—they do not
|
|
15
|
-
* advance the sequence or reset progress.
|
|
16
|
-
*
|
|
17
|
-
* @param sequence - Array of hotkey strings that form the sequence (or accessor function)
|
|
18
|
-
* @param callback - Function to call when the sequence is completed
|
|
19
|
-
* @param options - Options for the sequence behavior (or accessor function)
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```tsx
|
|
23
|
-
* function VimEditor() {
|
|
24
|
-
* // 'g g' to go to top
|
|
25
|
-
* createHotkeySequence(['G', 'G'], () => {
|
|
26
|
-
* scrollToTop()
|
|
27
|
-
* })
|
|
28
|
-
*
|
|
29
|
-
* // 'd d' to delete line
|
|
30
|
-
* createHotkeySequence(['D', 'D'], () => {
|
|
31
|
-
* deleteLine()
|
|
32
|
-
* })
|
|
33
|
-
*
|
|
34
|
-
* // 'd i w' to delete inner word
|
|
35
|
-
* createHotkeySequence(['D', 'I', 'W'], () => {
|
|
36
|
-
* deleteInnerWord()
|
|
37
|
-
* }, { timeout: 500 })
|
|
38
|
-
*
|
|
39
|
-
* // Same modifier on consecutive steps (bare Shift between chords is ignored)
|
|
40
|
-
* createHotkeySequence(['Shift+R', 'Shift+T'], () => {
|
|
41
|
-
* nextAction()
|
|
42
|
-
* })
|
|
43
|
-
*
|
|
44
|
-
* return <div>...</div>
|
|
45
|
-
* }
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
function createHotkeySequence(sequence, callback, options = {}) {
|
|
49
|
-
const defaultOptions = useDefaultHotkeysOptions();
|
|
50
|
-
const manager = getSequenceManager();
|
|
51
|
-
let registration = null;
|
|
52
|
-
let lastSequenceKey = null;
|
|
53
|
-
let lastTarget = null;
|
|
54
|
-
onCleanup(() => {
|
|
55
|
-
if (registration?.isActive) {
|
|
56
|
-
registration.unregister();
|
|
57
|
-
registration = null;
|
|
58
|
-
}
|
|
59
|
-
lastSequenceKey = null;
|
|
60
|
-
lastTarget = null;
|
|
61
|
-
});
|
|
62
|
-
createEffect(() => {
|
|
63
|
-
const resolvedSequence = typeof sequence === "function" ? sequence() : sequence;
|
|
64
|
-
const resolvedOptions = typeof options === "function" ? options() : options;
|
|
65
|
-
const mergedOptions = {
|
|
66
|
-
...defaultOptions.hotkeySequence,
|
|
67
|
-
...resolvedOptions
|
|
68
|
-
};
|
|
69
|
-
const { target: _target, ...optionsWithoutTarget } = mergedOptions;
|
|
70
|
-
const resolvedTarget = "target" in mergedOptions ? mergedOptions.target ?? null : typeof document !== "undefined" ? document : null;
|
|
71
|
-
if (resolvedSequence.length === 0 || !resolvedTarget) {
|
|
72
|
-
if (registration?.isActive) {
|
|
73
|
-
registration.unregister();
|
|
74
|
-
registration = null;
|
|
75
|
-
}
|
|
76
|
-
lastSequenceKey = null;
|
|
77
|
-
lastTarget = null;
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const sequenceKey = formatHotkeySequence(resolvedSequence);
|
|
81
|
-
untrack(() => {
|
|
82
|
-
if (registration?.isActive && lastSequenceKey === sequenceKey && lastTarget === resolvedTarget) {
|
|
83
|
-
registration.callback = callback;
|
|
84
|
-
registration.setOptions(optionsWithoutTarget);
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
if (registration?.isActive) {
|
|
88
|
-
registration.unregister();
|
|
89
|
-
registration = null;
|
|
90
|
-
}
|
|
91
|
-
registration = manager.register(resolvedSequence, callback, {
|
|
92
|
-
...mergedOptions,
|
|
93
|
-
target: resolvedTarget
|
|
94
|
-
});
|
|
95
|
-
if (registration.isActive) {
|
|
96
|
-
registration.callback = callback;
|
|
97
|
-
registration.setOptions(optionsWithoutTarget);
|
|
98
|
-
}
|
|
99
|
-
lastSequenceKey = sequenceKey;
|
|
100
|
-
lastTarget = resolvedTarget;
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
//#endregion
|
|
106
|
-
export { createHotkeySequence };
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { HotkeySequence, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
|
|
2
|
-
//#region src/createHotkeySequenceRecorder.d.ts
|
|
3
|
-
export interface SolidHotkeySequenceRecorder {
|
|
4
|
-
isRecording: () => boolean;
|
|
5
|
-
steps: () => HotkeySequence;
|
|
6
|
-
recordedSequence: () => HotkeySequence | null;
|
|
7
|
-
startRecording: () => void;
|
|
8
|
-
stopRecording: () => void;
|
|
9
|
-
cancelRecording: () => void;
|
|
10
|
-
commitRecording: () => void;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* SolidJS primitive for recording multi-chord sequences (Vim-style shortcuts).
|
|
14
|
-
*/
|
|
15
|
-
export declare function createHotkeySequenceRecorder(options: HotkeySequenceRecorderOptions | (() => HotkeySequenceRecorderOptions)): SolidHotkeySequenceRecorder;
|
|
16
|
-
//#endregion
|