@tanstack/svelte-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/HotkeysCtx.d.ts +48 -12
- package/dist/HotkeysCtx.js +34 -9
- package/dist/HotkeysProvider.svelte +19 -4
- package/dist/createHotkey.svelte.d.ts +37 -24
- package/dist/createHotkey.svelte.js +3 -106
- package/dist/createHotkeyRecorder.svelte.d.ts +35 -8
- package/dist/createHotkeyRecorder.svelte.js +12 -44
- package/dist/createHotkeys.svelte.d.ts +39 -22
- package/dist/createHotkeys.svelte.js +49 -152
- package/dist/getAppHotkeys.svelte.d.ts +49 -0
- package/dist/getAppHotkeys.svelte.js +67 -0
- package/dist/getHotkeyHint.svelte.d.ts +7 -7
- package/dist/getHotkeyHint.svelte.js +25 -11
- package/dist/getHotkeyRegistrations.svelte.d.ts +9 -12
- package/dist/getHotkeyRegistrations.svelte.js +12 -21
- package/dist/index.d.ts +2 -4
- package/dist/index.js +1 -3
- package/dist/internal.svelte.d.ts +6 -0
- package/dist/internal.svelte.js +8 -0
- package/package.json +5 -5
- package/dist/createHotkeySequence.svelte.d.ts +0 -65
- package/dist/createHotkeySequence.svelte.js +0 -149
- package/dist/createHotkeySequenceRecorder.svelte.d.ts +0 -15
- package/dist/createHotkeySequenceRecorder.svelte.js +0 -65
- package/dist/createHotkeySequences.svelte.d.ts +0 -49
- package/dist/createHotkeySequences.svelte.js +0 -189
|
@@ -1,184 +1,81 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHotkeyBindings, prepareHotkeyDefinitions, } from '@tanstack/hotkeys/adapter';
|
|
2
2
|
import { onDestroy } from 'svelte';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import { getHotkeysContext } from './HotkeysCtx';
|
|
4
|
+
import { createAppHotkeysSubscriber, resolveMaybeGetter, } from './internal.svelte';
|
|
5
|
+
/**
|
|
6
|
+
* Registers definitions in an effect. With an element, every hotkey listens on
|
|
7
|
+
* that element; otherwise each uses its own target (document by default).
|
|
8
|
+
*/
|
|
9
|
+
function bindHotkeys(hotkeys, commonOptions, element) {
|
|
10
|
+
const context = getHotkeysContext();
|
|
11
|
+
const subscribeAppHotkeys = createAppHotkeysSubscriber(context?.appHotkeysStore ?? null);
|
|
12
|
+
const bindings = createHotkeyBindings();
|
|
13
|
+
$effect(() => {
|
|
14
|
+
if (typeof document === 'undefined') {
|
|
15
|
+
return;
|
|
10
16
|
}
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
// Track app hotkeys so registrations move with custom hotkeys.
|
|
18
|
+
subscribeAppHotkeys();
|
|
19
|
+
const definitions = resolveMaybeGetter(hotkeys).map((definition) => ({
|
|
20
|
+
hotkey: resolveMaybeGetter(definition.hotkey),
|
|
21
|
+
callback: definition.callback,
|
|
22
|
+
options: definition.options
|
|
23
|
+
? resolveMaybeGetter(definition.options)
|
|
24
|
+
: undefined,
|
|
25
|
+
}));
|
|
26
|
+
bindings.update(prepareHotkeyDefinitions(definitions, {
|
|
27
|
+
appHotkeysStore: context?.appHotkeysStore,
|
|
28
|
+
defaults: context?.defaultOptions.hotkey,
|
|
29
|
+
commonOptions: element
|
|
30
|
+
? { ...resolveMaybeGetter(commonOptions), target: element }
|
|
31
|
+
: resolveMaybeGetter(commonOptions),
|
|
32
|
+
resolveTarget: element ? () => element : undefined,
|
|
33
|
+
}));
|
|
34
|
+
});
|
|
35
|
+
return bindings;
|
|
13
36
|
}
|
|
14
37
|
/**
|
|
15
|
-
*
|
|
38
|
+
* Svelte function for registering several hotkeys at once on the document:
|
|
39
|
+
* chords, sequences, and app hotkeys. Pass getters for reactive values. Use
|
|
40
|
+
* `createHotkeysAttachment` for element-scoped hotkeys.
|
|
41
|
+
*
|
|
42
|
+
* Options merge in this order: provider defaults, app hotkey declaration,
|
|
43
|
+
* commonOptions, per-hotkey options. Each option applies only to the kinds
|
|
44
|
+
* that support it. Unbound app hotkeys and empty sequences register nothing.
|
|
16
45
|
*
|
|
17
46
|
* @example
|
|
18
47
|
* ```svelte
|
|
19
48
|
* <script lang="ts">
|
|
20
|
-
* import { createHotkeys } from '@tanstack/svelte-hotkeys'
|
|
21
|
-
*
|
|
22
49
|
* createHotkeys([
|
|
23
50
|
* { hotkey: 'Mod+S', callback: () => save() },
|
|
24
|
-
* { hotkey: '
|
|
25
|
-
* { hotkey: 'Escape', callback: () => close() },
|
|
51
|
+
* { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } },
|
|
26
52
|
* ])
|
|
27
53
|
* </script>
|
|
28
54
|
* ```
|
|
29
55
|
*/
|
|
30
56
|
export function createHotkeys(hotkeys, commonOptions = {}) {
|
|
31
|
-
const
|
|
32
|
-
onDestroy(() =>
|
|
33
|
-
cleanupRegistrations(registrations);
|
|
34
|
-
});
|
|
35
|
-
$effect(() => {
|
|
36
|
-
if (typeof document === 'undefined') {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const resolvedHotkeys = resolveMaybeGetter(hotkeys);
|
|
40
|
-
const resolvedCommonOptions = resolveMaybeGetter(commonOptions);
|
|
41
|
-
const nextKeys = new SvelteSet();
|
|
42
|
-
const prepared = [];
|
|
43
|
-
for (let i = 0; i < resolvedHotkeys.length; i++) {
|
|
44
|
-
const def = resolvedHotkeys[i];
|
|
45
|
-
const resolvedHotkey = resolveMaybeGetter(def.hotkey);
|
|
46
|
-
const resolvedDefOptions = def.options
|
|
47
|
-
? resolveMaybeGetter(def.options)
|
|
48
|
-
: {};
|
|
49
|
-
const mergedOptions = {
|
|
50
|
-
...getDefaultHotkeysOptions().hotkey,
|
|
51
|
-
...resolvedCommonOptions,
|
|
52
|
-
...resolvedDefOptions,
|
|
53
|
-
};
|
|
54
|
-
const resolvedTarget = mergedOptions.target ??
|
|
55
|
-
(typeof document !== 'undefined' ? document : null);
|
|
56
|
-
if (!resolvedTarget) {
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
const hotkeyString = normalizeRegisterableHotkey(resolvedHotkey, mergedOptions.platform ?? detectPlatform());
|
|
60
|
-
const registrationKey = `${i}:${hotkeyString}`;
|
|
61
|
-
nextKeys.add(registrationKey);
|
|
62
|
-
prepared.push({
|
|
63
|
-
registrationKey,
|
|
64
|
-
def,
|
|
65
|
-
mergedOptions,
|
|
66
|
-
hotkeyString,
|
|
67
|
-
resolvedTarget,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
for (const [key, record] of [...registrations.entries()]) {
|
|
71
|
-
if (!nextKeys.has(key)) {
|
|
72
|
-
if (record.handle.isActive) {
|
|
73
|
-
record.handle.unregister();
|
|
74
|
-
}
|
|
75
|
-
registrations.delete(key);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
for (const p of prepared) {
|
|
79
|
-
const existing = registrations.get(p.registrationKey);
|
|
80
|
-
const { target: _target, ...optionsWithoutTarget } = p.mergedOptions;
|
|
81
|
-
if (existing?.handle.isActive && existing.target === p.resolvedTarget) {
|
|
82
|
-
existing.handle.callback = p.def.callback;
|
|
83
|
-
existing.handle.setOptions(optionsWithoutTarget);
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
if (existing?.handle.isActive) {
|
|
87
|
-
existing.handle.unregister();
|
|
88
|
-
}
|
|
89
|
-
if (existing) {
|
|
90
|
-
registrations.delete(p.registrationKey);
|
|
91
|
-
}
|
|
92
|
-
const handle = getHotkeyManager().register(p.hotkeyString, p.def.callback, {
|
|
93
|
-
...optionsWithoutTarget,
|
|
94
|
-
target: p.resolvedTarget,
|
|
95
|
-
});
|
|
96
|
-
registrations.set(p.registrationKey, {
|
|
97
|
-
handle,
|
|
98
|
-
target: p.resolvedTarget,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
});
|
|
57
|
+
const bindings = bindHotkeys(hotkeys, commonOptions);
|
|
58
|
+
onDestroy(() => bindings.destroy());
|
|
102
59
|
}
|
|
103
60
|
/**
|
|
104
|
-
* Create an attachment for element-scoped
|
|
61
|
+
* Create an attachment for element-scoped hotkeys: chords, sequences, and app
|
|
62
|
+
* hotkeys listen on the attached element.
|
|
105
63
|
*
|
|
106
64
|
* @example
|
|
107
65
|
* ```svelte
|
|
108
66
|
* <script lang="ts">
|
|
109
|
-
* import { createHotkeysAttachment } from '@tanstack/svelte-hotkeys'
|
|
110
|
-
*
|
|
111
67
|
* const editorKeys = createHotkeysAttachment([
|
|
112
68
|
* { hotkey: 'Mod+S', callback: () => save() },
|
|
113
|
-
* { hotkey: '
|
|
69
|
+
* { hotkey: ['G', 'G'], callback: () => scrollToTop() },
|
|
114
70
|
* ])
|
|
115
71
|
* </script>
|
|
116
72
|
*
|
|
117
|
-
* <div tabindex="0" {@attach editorKeys}>
|
|
118
|
-
* Editor content
|
|
119
|
-
* </div>
|
|
73
|
+
* <div tabindex="0" {@attach editorKeys}>Editor content</div>
|
|
120
74
|
* ```
|
|
121
75
|
*/
|
|
122
76
|
export function createHotkeysAttachment(hotkeys, commonOptions = {}) {
|
|
123
77
|
return (element) => {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
const resolvedHotkeys = resolveMaybeGetter(hotkeys);
|
|
127
|
-
const resolvedCommonOptions = resolveMaybeGetter(commonOptions);
|
|
128
|
-
const nextKeys = new SvelteSet();
|
|
129
|
-
const prepared = [];
|
|
130
|
-
for (let i = 0; i < resolvedHotkeys.length; i++) {
|
|
131
|
-
const def = resolvedHotkeys[i];
|
|
132
|
-
const resolvedHotkey = resolveMaybeGetter(def.hotkey);
|
|
133
|
-
const resolvedDefOptions = def.options
|
|
134
|
-
? resolveMaybeGetter(def.options)
|
|
135
|
-
: {};
|
|
136
|
-
const mergedOptions = {
|
|
137
|
-
...getDefaultHotkeysOptions().hotkey,
|
|
138
|
-
...resolvedCommonOptions,
|
|
139
|
-
...resolvedDefOptions,
|
|
140
|
-
};
|
|
141
|
-
const hotkeyString = normalizeRegisterableHotkey(resolvedHotkey, mergedOptions.platform ?? detectPlatform());
|
|
142
|
-
const registrationKey = `${i}:${hotkeyString}`;
|
|
143
|
-
nextKeys.add(registrationKey);
|
|
144
|
-
prepared.push({
|
|
145
|
-
registrationKey,
|
|
146
|
-
def,
|
|
147
|
-
mergedOptions,
|
|
148
|
-
hotkeyString,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
for (const [key, record] of [...registrations.entries()]) {
|
|
152
|
-
if (!nextKeys.has(key)) {
|
|
153
|
-
if (record.handle.isActive) {
|
|
154
|
-
record.handle.unregister();
|
|
155
|
-
}
|
|
156
|
-
registrations.delete(key);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
for (const p of prepared) {
|
|
160
|
-
const existing = registrations.get(p.registrationKey);
|
|
161
|
-
const { target: _target, ...optionsWithoutTarget } = p.mergedOptions;
|
|
162
|
-
if (existing?.handle.isActive) {
|
|
163
|
-
existing.handle.callback = p.def.callback;
|
|
164
|
-
existing.handle.setOptions(optionsWithoutTarget);
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (existing?.handle.isActive) {
|
|
168
|
-
existing.handle.unregister();
|
|
169
|
-
}
|
|
170
|
-
if (existing) {
|
|
171
|
-
registrations.delete(p.registrationKey);
|
|
172
|
-
}
|
|
173
|
-
const handle = getHotkeyManager().register(p.hotkeyString, p.def.callback, {
|
|
174
|
-
...optionsWithoutTarget,
|
|
175
|
-
target: element,
|
|
176
|
-
});
|
|
177
|
-
registrations.set(p.registrationKey, { handle, target: element });
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
return () => {
|
|
181
|
-
cleanupRegistrations(registrations);
|
|
182
|
-
};
|
|
78
|
+
const bindings = bindHotkeys(hotkeys, commonOptions, element);
|
|
79
|
+
return () => bindings.destroy();
|
|
183
80
|
};
|
|
184
81
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AppHotkey, AppHotkeyListItem, CustomHotkeys, Hotkey, RegisteredAppHotkeys } from '@tanstack/hotkeys';
|
|
2
|
+
export interface SvelteAppHotkeys {
|
|
3
|
+
/** Effective app hotkeys: each declaration with the user's custom hotkey applied. */
|
|
4
|
+
readonly current: RegisteredAppHotkeys;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Returns reactive access to the effective app hotkeys from `HotkeysProvider`,
|
|
8
|
+
* typed by `Register`.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```svelte
|
|
12
|
+
* <script lang="ts">
|
|
13
|
+
* const hotkeys = getAppHotkeys()
|
|
14
|
+
* createHotkey(() => hotkeys.current['doc.save'], () => save())
|
|
15
|
+
* </script>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function getAppHotkeys(): SvelteAppHotkeys;
|
|
19
|
+
export interface SvelteAppHotkeyList {
|
|
20
|
+
/** Every app hotkey with its live status, in declaration order. */
|
|
21
|
+
readonly current: Array<AppHotkeyListItem<RegisteredAppHotkeys[keyof RegisteredAppHotkeys]>>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Lists every app hotkey with its live status, for cheat sheets and settings
|
|
25
|
+
* screens. App hotkeys whose components have not mounted are `'inactive'`;
|
|
26
|
+
* those without a hotkey are `'unbound'`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getAppHotkeyList(): SvelteAppHotkeyList;
|
|
29
|
+
export interface SvelteCustomHotkeys {
|
|
30
|
+
/** The user's hotkeys that differ from the defaults. */
|
|
31
|
+
readonly current: CustomHotkeys;
|
|
32
|
+
/**
|
|
33
|
+
* Sets the user's hotkey for an app hotkey. `null` unbinds it.
|
|
34
|
+
* Returns conflicting app hotkeys; when any exist, nothing changes.
|
|
35
|
+
*/
|
|
36
|
+
setCustomHotkey: (id: keyof RegisteredAppHotkeys & string, hotkey: Hotkey | null) => Array<AppHotkey>;
|
|
37
|
+
/** Restores the default hotkey for an app hotkey. */
|
|
38
|
+
resetCustomHotkey: (id: keyof RegisteredAppHotkeys & string) => void;
|
|
39
|
+
/** Restores every default hotkey. */
|
|
40
|
+
resetCustomHotkeys: () => void;
|
|
41
|
+
/** Other app hotkeys that would collide if `id` used `hotkey`. */
|
|
42
|
+
findConflicts: (id: keyof RegisteredAppHotkeys & string, hotkey: Hotkey) => Array<AppHotkey>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Reads and changes the user's custom hotkeys. Changes apply to every
|
|
46
|
+
* registered app hotkey immediately and are passed to the provider's
|
|
47
|
+
* `onCustomHotkeysChange`, ready to save.
|
|
48
|
+
*/
|
|
49
|
+
export declare function getCustomHotkeys(): SvelteCustomHotkeys;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { getHotkeysContext } from './HotkeysCtx';
|
|
2
|
+
import { createStoreSubscriber } from './internal.svelte';
|
|
3
|
+
import { getHotkeyRegistrations } from './getHotkeyRegistrations.svelte';
|
|
4
|
+
function requireStore(name) {
|
|
5
|
+
const store = getHotkeysContext()?.appHotkeysStore;
|
|
6
|
+
if (!store) {
|
|
7
|
+
throw new Error(`${name} requires a HotkeysProvider with appHotkeys`);
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
store: store,
|
|
11
|
+
subscribe: createStoreSubscriber(store.store),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Returns reactive access to the effective app hotkeys from `HotkeysProvider`,
|
|
16
|
+
* typed by `Register`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```svelte
|
|
20
|
+
* <script lang="ts">
|
|
21
|
+
* const hotkeys = getAppHotkeys()
|
|
22
|
+
* createHotkey(() => hotkeys.current['doc.save'], () => save())
|
|
23
|
+
* </script>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function getAppHotkeys() {
|
|
27
|
+
const { store, subscribe } = requireStore('getAppHotkeys');
|
|
28
|
+
return {
|
|
29
|
+
get current() {
|
|
30
|
+
subscribe();
|
|
31
|
+
return store.appHotkeys;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Lists every app hotkey with its live status, for cheat sheets and settings
|
|
37
|
+
* screens. App hotkeys whose components have not mounted are `'inactive'`;
|
|
38
|
+
* those without a hotkey are `'unbound'`.
|
|
39
|
+
*/
|
|
40
|
+
export function getAppHotkeyList() {
|
|
41
|
+
const { store, subscribe } = requireStore('getAppHotkeyList');
|
|
42
|
+
const registrations = getHotkeyRegistrations();
|
|
43
|
+
return {
|
|
44
|
+
get current() {
|
|
45
|
+
subscribe();
|
|
46
|
+
return store.getList(registrations.hotkeys);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Reads and changes the user's custom hotkeys. Changes apply to every
|
|
52
|
+
* registered app hotkey immediately and are passed to the provider's
|
|
53
|
+
* `onCustomHotkeysChange`, ready to save.
|
|
54
|
+
*/
|
|
55
|
+
export function getCustomHotkeys() {
|
|
56
|
+
const { store, subscribe } = requireStore('getCustomHotkeys');
|
|
57
|
+
return {
|
|
58
|
+
get current() {
|
|
59
|
+
subscribe();
|
|
60
|
+
return store.customHotkeys;
|
|
61
|
+
},
|
|
62
|
+
setCustomHotkey: (id, hotkey) => store.setCustomHotkey(id, hotkey),
|
|
63
|
+
resetCustomHotkey: (id) => store.resetCustomHotkey(id),
|
|
64
|
+
resetCustomHotkeys: () => store.resetCustomHotkeys(),
|
|
65
|
+
findConflicts: (id, hotkey) => store.findConflicts(id, hotkey),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type { HeldModifierOptions, RegisterableHotkey } from '@tanstack/hotkeys';
|
|
1
|
+
import type { AppHotkey, HeldModifierOptions, RegisterableHotkey } from '@tanstack/hotkeys';
|
|
2
2
|
import type { MaybeGetter } from './internal.svelte';
|
|
3
3
|
export interface SvelteHotkeyHint {
|
|
4
4
|
readonly visible: boolean;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Returns an object with a reactive `visible` getter indicating whether held
|
|
8
|
-
* Pass getters to track changing
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Returns an object with a reactive `visible` getter indicating whether held
|
|
8
|
+
* modifiers reveal this shortcut. Pass getters to track changing hotkeys and
|
|
9
|
+
* options. A sequence reveals its hint from its first step, and an app hotkey
|
|
10
|
+
* uses its effective hotkey (false while unbound). This helper does not
|
|
11
|
+
* register a shortcut or check whether its target is focused.
|
|
12
12
|
*/
|
|
13
|
-
export declare function getHotkeyHint(hotkey: MaybeGetter<RegisterableHotkey>, options?: MaybeGetter<HeldModifierOptions>): SvelteHotkeyHint;
|
|
13
|
+
export declare function getHotkeyHint(hotkey: MaybeGetter<RegisterableHotkey | AppHotkey>, options?: MaybeGetter<HeldModifierOptions>): SvelteHotkeyHint;
|
|
@@ -1,29 +1,43 @@
|
|
|
1
1
|
import { createSubscriber } from 'svelte/reactivity';
|
|
2
|
-
import { getKeyStateTracker, matchesHeldModifiers } from '@tanstack/hotkeys';
|
|
2
|
+
import { getKeyStateTracker, isAppHotkey, matchesHeldModifiers, resolveAppHotkey, } from '@tanstack/hotkeys';
|
|
3
|
+
import { getHotkeysContext } from './HotkeysCtx';
|
|
3
4
|
import { resolveMaybeGetter } from './internal.svelte';
|
|
4
5
|
/**
|
|
5
|
-
* Returns an object with a reactive `visible` getter indicating whether held
|
|
6
|
-
* Pass getters to track changing
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Returns an object with a reactive `visible` getter indicating whether held
|
|
7
|
+
* modifiers reveal this shortcut. Pass getters to track changing hotkeys and
|
|
8
|
+
* options. A sequence reveals its hint from its first step, and an app hotkey
|
|
9
|
+
* uses its effective hotkey (false while unbound). This helper does not
|
|
10
|
+
* register a shortcut or check whether its target is focused.
|
|
10
11
|
*/
|
|
11
12
|
export function getHotkeyHint(hotkey, options = {}) {
|
|
12
13
|
const tracker = getKeyStateTracker();
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
const store = getHotkeysContext()?.appHotkeysStore ?? null;
|
|
15
|
+
// Resolve current reactive arguments without caching a stale hotkey.
|
|
16
|
+
const read = () => {
|
|
17
|
+
const value = resolveMaybeGetter(hotkey);
|
|
18
|
+
const resolved = isAppHotkey(value)
|
|
19
|
+
? resolveAppHotkey(value, store).hotkey
|
|
20
|
+
: value;
|
|
21
|
+
return (resolved !== null &&
|
|
22
|
+
matchesHeldModifiers(resolved, tracker.store.state.heldKeys, resolveMaybeGetter(options)));
|
|
23
|
+
};
|
|
15
24
|
let previous = false;
|
|
16
25
|
// Notify Svelte only when visibility changes, not for every unrelated held key.
|
|
17
26
|
const subscribe = createSubscriber((update) => {
|
|
18
27
|
previous = read();
|
|
19
|
-
const
|
|
28
|
+
const check = () => {
|
|
20
29
|
const next = read();
|
|
21
30
|
if (next !== previous) {
|
|
22
31
|
previous = next;
|
|
23
32
|
update();
|
|
24
33
|
}
|
|
25
|
-
}
|
|
26
|
-
|
|
34
|
+
};
|
|
35
|
+
const keys = tracker.store.subscribe(check);
|
|
36
|
+
const custom = store?.store.subscribe(check);
|
|
37
|
+
return () => {
|
|
38
|
+
keys.unsubscribe();
|
|
39
|
+
custom?.unsubscribe();
|
|
40
|
+
};
|
|
27
41
|
});
|
|
28
42
|
return {
|
|
29
43
|
/** Reads visibility and subscribes the current reactive consumer when needed. */
|
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
import type { HotkeyRegistrationView
|
|
1
|
+
import type { HotkeyRegistrationView } from '@tanstack/hotkeys';
|
|
2
2
|
export interface SvelteHotkeyRegistrations {
|
|
3
|
+
/** Every registration, chords and sequences. Narrow each view with `kind`. */
|
|
3
4
|
readonly hotkeys: Array<HotkeyRegistrationView>;
|
|
4
|
-
readonly sequences: Array<SequenceRegistrationView>;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Svelte function that returns reactive access to
|
|
8
|
-
*
|
|
7
|
+
* Svelte function that returns reactive access to every hotkey registration,
|
|
8
|
+
* chords and sequences, from the singleton HotkeyManager.
|
|
9
9
|
*
|
|
10
|
-
* This
|
|
11
|
-
*
|
|
12
|
-
* @returns Object with reactive `hotkeys` and `sequences` properties
|
|
10
|
+
* This function does NOT require the HotkeysProvider.
|
|
13
11
|
*
|
|
14
12
|
* @example
|
|
15
13
|
* ```svelte
|
|
16
|
-
* <script>
|
|
17
|
-
* import { getHotkeyRegistrations } from '@tanstack/svelte-hotkeys'
|
|
18
|
-
*
|
|
14
|
+
* <script lang="ts">
|
|
19
15
|
* const registrations = getHotkeyRegistrations()
|
|
20
16
|
* </script>
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
*
|
|
18
|
+
* {#each registrations.hotkeys as reg (reg.id)}
|
|
19
|
+
* <li>{reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}</li>
|
|
23
20
|
* {/each}
|
|
24
21
|
* ```
|
|
25
22
|
*/
|
|
@@ -1,36 +1,27 @@
|
|
|
1
|
-
import { getHotkeyManager,
|
|
1
|
+
import { getHotkeyManager, toHotkeyRegistrationView } from '@tanstack/hotkeys';
|
|
2
2
|
import { createStoreSubscriber } from './internal.svelte';
|
|
3
3
|
class HotkeyRegistrationsState {
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#subscribeHotkeys = createStoreSubscriber(this.#hotkeyManager.registrations);
|
|
7
|
-
#subscribeSequences = createStoreSubscriber(this.#sequenceManager.registrations);
|
|
4
|
+
#manager = getHotkeyManager();
|
|
5
|
+
#subscribe = createStoreSubscriber(this.#manager.registrations);
|
|
8
6
|
get hotkeys() {
|
|
9
|
-
this.#
|
|
10
|
-
return Array.from(this.#
|
|
11
|
-
}
|
|
12
|
-
get sequences() {
|
|
13
|
-
this.#subscribeSequences();
|
|
14
|
-
return Array.from(this.#sequenceManager.registrations.state.values());
|
|
7
|
+
this.#subscribe();
|
|
8
|
+
return Array.from(this.#manager.registrations.state.values()).map(toHotkeyRegistrationView);
|
|
15
9
|
}
|
|
16
10
|
}
|
|
17
11
|
/**
|
|
18
|
-
* Svelte function that returns reactive access to
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* This is a standalone function that does NOT require the HotkeysProvider.
|
|
12
|
+
* Svelte function that returns reactive access to every hotkey registration,
|
|
13
|
+
* chords and sequences, from the singleton HotkeyManager.
|
|
22
14
|
*
|
|
23
|
-
*
|
|
15
|
+
* This function does NOT require the HotkeysProvider.
|
|
24
16
|
*
|
|
25
17
|
* @example
|
|
26
18
|
* ```svelte
|
|
27
|
-
* <script>
|
|
28
|
-
* import { getHotkeyRegistrations } from '@tanstack/svelte-hotkeys'
|
|
29
|
-
*
|
|
19
|
+
* <script lang="ts">
|
|
30
20
|
* const registrations = getHotkeyRegistrations()
|
|
31
21
|
* </script>
|
|
32
|
-
*
|
|
33
|
-
*
|
|
22
|
+
*
|
|
23
|
+
* {#each registrations.hotkeys as reg (reg.id)}
|
|
24
|
+
* <li>{reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}</li>
|
|
34
25
|
* {/each}
|
|
35
26
|
* ```
|
|
36
27
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
export * from '@tanstack/hotkeys';
|
|
2
2
|
export * from './createHotkey.svelte';
|
|
3
3
|
export * from './createHotkeys.svelte';
|
|
4
|
-
export * from './createHotkeySequence.svelte';
|
|
5
|
-
export * from './createHotkeySequences.svelte';
|
|
6
4
|
export * from './createHotkeyRecorder.svelte';
|
|
7
|
-
export * from './createHotkeySequenceRecorder.svelte';
|
|
8
5
|
export * from './getHeldKeys.svelte';
|
|
9
6
|
export * from './getHotkeyRegistrations.svelte';
|
|
10
7
|
export * from './getHeldKeyCodesMap.svelte';
|
|
11
8
|
export * from './getIsKeyHeld.svelte';
|
|
9
|
+
export * from './getAppHotkeys.svelte';
|
|
12
10
|
export { default as HotkeysProvider } from './HotkeysProvider.svelte';
|
|
13
11
|
export { DEFAULT_OPTIONS, getDefaultHotkeysOptions, getHotkeysContext, setHotkeysContext, } from './HotkeysCtx';
|
|
14
|
-
export type { HotkeysProviderOptions, HotkeysProviderProps } from './HotkeysCtx';
|
|
12
|
+
export type { HotkeysContextOptions, HotkeysProviderOptions, HotkeysProviderProps, } from './HotkeysCtx';
|
|
15
13
|
export * from './getHotkeyHint.svelte';
|
package/dist/index.js
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
export * from '@tanstack/hotkeys';
|
|
3
3
|
export * from './createHotkey.svelte';
|
|
4
4
|
export * from './createHotkeys.svelte';
|
|
5
|
-
export * from './createHotkeySequence.svelte';
|
|
6
|
-
export * from './createHotkeySequences.svelte';
|
|
7
5
|
export * from './createHotkeyRecorder.svelte';
|
|
8
|
-
export * from './createHotkeySequenceRecorder.svelte';
|
|
9
6
|
export * from './getHeldKeys.svelte';
|
|
10
7
|
export * from './getHotkeyRegistrations.svelte';
|
|
11
8
|
export * from './getHeldKeyCodesMap.svelte';
|
|
12
9
|
export * from './getIsKeyHeld.svelte';
|
|
10
|
+
export * from './getAppHotkeys.svelte';
|
|
13
11
|
export { default as HotkeysProvider } from './HotkeysProvider.svelte';
|
|
14
12
|
export { DEFAULT_OPTIONS, getDefaultHotkeysOptions, getHotkeysContext, setHotkeysContext, } from './HotkeysCtx';
|
|
15
13
|
export * from './getHotkeyHint.svelte';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AppHotkeysStore } from '@tanstack/hotkeys';
|
|
1
2
|
export type MaybeGetter<T> = T | (() => T);
|
|
2
3
|
interface SubscribableStore<TState> {
|
|
3
4
|
state: TState;
|
|
@@ -7,4 +8,9 @@ interface SubscribableStore<TState> {
|
|
|
7
8
|
}
|
|
8
9
|
export declare function resolveMaybeGetter<T>(value: MaybeGetter<T>): T;
|
|
9
10
|
export declare function createStoreSubscriber<TState>(store: SubscribableStore<TState>): () => void;
|
|
11
|
+
/**
|
|
12
|
+
* Returns a function that subscribes the current reactive consumer to an app
|
|
13
|
+
* hotkeys store, so it re-runs when app hotkeys or custom hotkeys change.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createAppHotkeysSubscriber(store: AppHotkeysStore | null): () => void;
|
|
10
16
|
export {};
|
package/dist/internal.svelte.js
CHANGED
|
@@ -10,3 +10,11 @@ export function createStoreSubscriber(store) {
|
|
|
10
10
|
: () => subscription.unsubscribe();
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
|
+
const noop = () => { };
|
|
14
|
+
/**
|
|
15
|
+
* Returns a function that subscribes the current reactive consumer to an app
|
|
16
|
+
* hotkeys store, so it re-runs when app hotkeys or custom hotkeys change.
|
|
17
|
+
*/
|
|
18
|
+
export function createAppHotkeysSubscriber(store) {
|
|
19
|
+
return store ? createStoreSubscriber(store.store) : noop;
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/svelte-hotkeys",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Svelte adapter for TanStack Hotkeys",
|
|
5
5
|
"author": "Kunal Rao",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@tanstack/hotkeys": "0.
|
|
42
|
+
"@tanstack/hotkeys": "0.12.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@sveltejs/package": "^
|
|
46
|
-
"@sveltejs/vite-plugin-svelte": "^7.
|
|
47
|
-
"@typescript-eslint/parser": "^8.
|
|
45
|
+
"@sveltejs/package": "^3.0.0",
|
|
46
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
47
|
+
"@typescript-eslint/parser": "^8.71.0",
|
|
48
48
|
"eslint-plugin-svelte": "^3.23.0",
|
|
49
49
|
"svelte": "^5.57.1"
|
|
50
50
|
},
|