@tanstack/svelte-hotkeys 0.15.0 → 0.16.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/svelte-hotkeys",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Svelte adapter for TanStack Hotkeys",
|
|
5
5
|
"author": "Kunal Rao",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"keys",
|
|
21
21
|
"hotkeys",
|
|
22
22
|
"keyboard",
|
|
23
|
-
"shortcuts"
|
|
23
|
+
"shortcuts",
|
|
24
|
+
"tanstack-intent"
|
|
24
25
|
],
|
|
25
26
|
"type": "module",
|
|
26
27
|
"types": "./dist/index.d.ts",
|
|
@@ -36,10 +37,11 @@
|
|
|
36
37
|
},
|
|
37
38
|
"sideEffects": false,
|
|
38
39
|
"files": [
|
|
39
|
-
"dist"
|
|
40
|
+
"dist",
|
|
41
|
+
"skills"
|
|
40
42
|
],
|
|
41
43
|
"dependencies": {
|
|
42
|
-
"@tanstack/hotkeys": "0.
|
|
44
|
+
"@tanstack/hotkeys": "0.17.0"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@sveltejs/package": "^3.0.0",
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: svelte-hotkeys
|
|
3
|
+
description: >-
|
|
4
|
+
Use when adding or fixing keyboard shortcuts in a Svelte 5 app with
|
|
5
|
+
@tanstack/svelte-hotkeys: createHotkey and createHotkeys, getter options,
|
|
6
|
+
createHotkeyAttachment for element hotkeys, HotkeysProvider and
|
|
7
|
+
setHotkeysContext scopes, app hotkey getters, recorders, held-key and hint
|
|
8
|
+
getters, or replacing svelte:window keydown handlers.
|
|
9
|
+
metadata:
|
|
10
|
+
purpose: >-
|
|
11
|
+
Svelte 5 bindings for TanStack Hotkeys: getters, attachments, providers,
|
|
12
|
+
and the Svelte-specific reactivity mistakes to avoid.
|
|
13
|
+
type: framework
|
|
14
|
+
framework: svelte
|
|
15
|
+
library: '@tanstack/svelte-hotkeys'
|
|
16
|
+
library_version: '0.15.0'
|
|
17
|
+
sources:
|
|
18
|
+
- src/createHotkey.svelte.ts
|
|
19
|
+
- src/createHotkeys.svelte.ts
|
|
20
|
+
- src/HotkeysCtx.ts
|
|
21
|
+
- src/HotkeysProvider.svelte
|
|
22
|
+
- src/getAppHotkeys.svelte.ts
|
|
23
|
+
- src/createHotkeyRecorder.svelte.ts
|
|
24
|
+
- TanStack/hotkeys:docs/framework/svelte/quick-start.md
|
|
25
|
+
- TanStack/hotkeys:docs/framework/svelte/guides/hotkeys-provider-and-scopes.md
|
|
26
|
+
requires:
|
|
27
|
+
- '@tanstack/hotkeys#hotkeys-core'
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
# TanStack Hotkeys in Svelte
|
|
31
|
+
|
|
32
|
+
Before starting, load the `@tanstack/hotkeys#hotkeys-core` skill (`npx @tanstack/intent@latest load @tanstack/hotkeys#hotkeys-core`).
|
|
33
|
+
|
|
34
|
+
```svelte
|
|
35
|
+
<script lang="ts">
|
|
36
|
+
import {
|
|
37
|
+
createHotkey,
|
|
38
|
+
createHotkeyAttachment,
|
|
39
|
+
} from '@tanstack/svelte-hotkeys'
|
|
40
|
+
|
|
41
|
+
let isDirty = $state(false)
|
|
42
|
+
|
|
43
|
+
createHotkey(
|
|
44
|
+
'Mod+S',
|
|
45
|
+
() => save(),
|
|
46
|
+
() => ({ enabled: isDirty }),
|
|
47
|
+
)
|
|
48
|
+
const closeOnEscape = createHotkeyAttachment('Escape', () => closePanel())
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<div tabindex="0" {@attach closeOnEscape} oninput={() => (isDirty = true)}>
|
|
52
|
+
Panel
|
|
53
|
+
</div>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## API
|
|
57
|
+
|
|
58
|
+
| API | What it's for |
|
|
59
|
+
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
60
|
+
| `createHotkey(hotkey, callback, options?)` | Register one chord, sequence, or app hotkey on the document while the component is mounted; pass getters for values that change. |
|
|
61
|
+
| `createHotkeys(definitions, commonOptions?)` | Register several hotkeys on the document, or a getter of a list built from data. |
|
|
62
|
+
| `createHotkeyAttachment(hotkey, callback, options?)` | Create an attachment for `{@attach}` that listens on one element. |
|
|
63
|
+
| `createHotkeysAttachment(definitions, commonOptions?)` | Create an attachment that registers several hotkeys on one element. |
|
|
64
|
+
| `<HotkeysProvider>` | Set default options, a scope (`exclusive` for dialogs), `enabled`, and app hotkeys for the components inside it. |
|
|
65
|
+
| `setHotkeysContext(options)` | Set the same options from a component's script instead of rendering `HotkeysProvider`; they apply to that component too. |
|
|
66
|
+
| `getHotkeysContext()` | Read the nearest context's merged defaults and enabled state, or `null` outside one. |
|
|
67
|
+
| `getDefaultHotkeysOptions()` | Read the default options merged from every context above. |
|
|
68
|
+
| `getAppHotkeys()` | Read `.current`, the declared app hotkeys with the user's custom hotkeys applied. |
|
|
69
|
+
| `getAppHotkeyList()` | Read `.current`, every app hotkey with its status, for a cheat sheet or settings screen. |
|
|
70
|
+
| `getCustomHotkeys()` | Read the user's custom hotkeys from `.current`, change or reset them, and check a new one for conflicts. |
|
|
71
|
+
| `createHotkeyRecorder(options)` | Record a chord or sequence the user presses, for a settings screen. |
|
|
72
|
+
| `getHotkeyRegistrations()` | Read `.hotkeys`, every registered hotkey with its options and `meta`. |
|
|
73
|
+
| `getHotkeyHint(hotkey, options?)` | Read `.visible`, whether held modifiers should reveal a shortcut's hint. |
|
|
74
|
+
| `getIsKeyHeld(key)` | Read `.held`, whether one key is held. |
|
|
75
|
+
| `getHeldKeys()` | Read `.keys`, every key that's held right now. |
|
|
76
|
+
| `getHeldKeyCodesMap()` | Read `.codes`, which maps each held key to its physical `event.code`. |
|
|
77
|
+
|
|
78
|
+
The package also re-exports `@tanstack/hotkeys`, such as `formatForDisplay`, `matchesKeyboardEvent`, and `defineAppHotkeys`.
|
|
79
|
+
|
|
80
|
+
No provider is required. Call these at the top level of a component's `<script>`.
|
|
81
|
+
|
|
82
|
+
## Pitfalls
|
|
83
|
+
|
|
84
|
+
- **Pass options as a getter.** `{ enabled: isDirty }` is read once, during initialization, so the hotkey never follows the state. Use `() => ({ enabled: isDirty })`. The hotkey and `createHotkeys`' definitions also accept getters.
|
|
85
|
+
- **Don't destructure the returned objects.** `const { held } = getIsKeyHeld('Shift')` copies the value once. Keep the object and read `shift.held`, `recorder.isRecording`, or `appHotkeys.current` where you use it.
|
|
86
|
+
- **Use attachments for element hotkeys.** The `target` option takes only `document` or `window`.
|
|
87
|
+
- **Call `setHotkeysContext` before creating the component's hotkeys.** It applies to the component itself, so a hotkey created first lands in the outer scope, which an exclusive context then suspends. It reads its options once; for `enabled` or `exclusive` that change, wrap the component in `<HotkeysProvider>`. Give each open dialog its own scope name.
|
|
88
|
+
- **The callback isn't tracked.** Read state and props inside it.
|
|
89
|
+
- **Call the functions directly.** A `createShortcut(keys: string)` wrapper has to cast strings to hotkeys, which turns off type checking and drops sequences, getter options, and app hotkeys.
|
|
90
|
+
|
|
91
|
+
When declaring app hotkeys, building a cheat sheet, or letting users rebind shortcuts, load `@tanstack/hotkeys#app-hotkeys` and read [app hotkeys in Svelte](references/app-hotkeys.md).
|
|
92
|
+
|
|
93
|
+
## API discovery and docs
|
|
94
|
+
|
|
95
|
+
Signatures: `node_modules/@tanstack/svelte-hotkeys/dist/*.d.ts`. Guides: `https://tanstack.com/hotkeys/latest/docs/framework/svelte/guides/<page>.md`, such as `hotkeys`, `hotkeys-provider-and-scopes`, or `app-hotkeys`. Devtools: `hotkeysDevtoolsPlugin()` from `@tanstack/svelte-hotkeys-devtools` in `@tanstack/svelte-devtools`.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# App hotkeys in Svelte
|
|
2
|
+
|
|
3
|
+
The `@tanstack/hotkeys#app-hotkeys` skill owns the rules. This is the Svelte wiring.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
// app-hotkeys.ts
|
|
7
|
+
import { defineAppHotkeys } from '@tanstack/svelte-hotkeys'
|
|
8
|
+
|
|
9
|
+
export const appHotkeys = defineAppHotkeys({
|
|
10
|
+
'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save' } },
|
|
11
|
+
'help.shortcuts': { hotkey: 'Mod+/', meta: { name: 'Keyboard shortcuts' } },
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
declare module '@tanstack/svelte-hotkeys' {
|
|
15
|
+
interface Register {
|
|
16
|
+
appHotkeys: typeof appHotkeys
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```svelte
|
|
22
|
+
<!-- App.svelte -->
|
|
23
|
+
<script lang="ts">
|
|
24
|
+
import { HotkeysProvider } from '@tanstack/svelte-hotkeys'
|
|
25
|
+
import type { CustomHotkeys } from '@tanstack/svelte-hotkeys'
|
|
26
|
+
import { appHotkeys } from './app-hotkeys'
|
|
27
|
+
import AppContent from './AppContent.svelte'
|
|
28
|
+
|
|
29
|
+
const saved: CustomHotkeys = JSON.parse(
|
|
30
|
+
localStorage.getItem('hotkeys') ?? '{}',
|
|
31
|
+
)
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<HotkeysProvider
|
|
35
|
+
{appHotkeys}
|
|
36
|
+
customHotkeys={saved}
|
|
37
|
+
onCustomHotkeysChange={(custom) =>
|
|
38
|
+
localStorage.setItem('hotkeys', JSON.stringify(custom))}
|
|
39
|
+
>
|
|
40
|
+
<AppContent />
|
|
41
|
+
</HotkeysProvider>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Read app hotkeys inside a getter so registrations follow rebinding: `createHotkey(() => appHotkeys.current['doc.save'], save)`, with `const appHotkeys = getAppHotkeys()`. Build a cheat sheet from `getAppHotkeyList().current`.
|
|
45
|
+
|
|
46
|
+
A settings row records with `createHotkeyRecorder` and saves through `getCustomHotkeys`:
|
|
47
|
+
|
|
48
|
+
```svelte
|
|
49
|
+
<script lang="ts">
|
|
50
|
+
import {
|
|
51
|
+
createHotkeyRecorder,
|
|
52
|
+
formatForDisplay,
|
|
53
|
+
getAppHotkeys,
|
|
54
|
+
getCustomHotkeys,
|
|
55
|
+
} from '@tanstack/svelte-hotkeys'
|
|
56
|
+
import type { AppHotkeyId } from '@tanstack/svelte-hotkeys'
|
|
57
|
+
|
|
58
|
+
let { id }: { id: AppHotkeyId } = $props()
|
|
59
|
+
const appHotkeys = getAppHotkeys()
|
|
60
|
+
const custom = getCustomHotkeys()
|
|
61
|
+
const appHotkey = $derived(appHotkeys.current[id])
|
|
62
|
+
|
|
63
|
+
const recorder = createHotkeyRecorder(() => ({
|
|
64
|
+
maxSteps: Infinity,
|
|
65
|
+
validate: (hotkey) => {
|
|
66
|
+
const [conflict] = custom.findConflicts(id, hotkey)
|
|
67
|
+
return conflict ? `Used by ${conflict.meta?.name ?? conflict.id}` : true
|
|
68
|
+
},
|
|
69
|
+
onRecord: (hotkey) => custom.setCustomHotkey(id, hotkey),
|
|
70
|
+
onClear: () => custom.setCustomHotkey(id, null),
|
|
71
|
+
}))
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
{appHotkey.meta?.name}: {appHotkey.hotkey
|
|
75
|
+
? formatForDisplay(appHotkey.hotkey)
|
|
76
|
+
: 'Unassigned'}
|
|
77
|
+
<button onclick={recorder.startRecording}>Change</button>
|
|
78
|
+
<button onclick={() => custom.resetCustomHotkey(id)}>Reset</button>
|
|
79
|
+
```
|