@tanstack/vue-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/vue-hotkeys",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Vue adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -20,7 +20,8 @@
20
20
  "tanstack",
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",
@@ -33,10 +34,11 @@
33
34
  "node": ">=20"
34
35
  },
35
36
  "files": [
36
- "dist"
37
+ "dist",
38
+ "skills"
37
39
  ],
38
40
  "dependencies": {
39
- "@tanstack/hotkeys": "0.16.0",
41
+ "@tanstack/hotkeys": "0.17.0",
40
42
  "@tanstack/vue-store": "^0.11.2"
41
43
  },
42
44
  "devDependencies": {
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: vue-hotkeys
3
+ description: >-
4
+ Use when adding or fixing keyboard shortcuts in a Vue 3 app with
5
+ @tanstack/vue-hotkeys: useHotkey and useHotkeys composables, refs and getters
6
+ as options, template-ref targets, HotkeysProvider and provideHotkeysContext
7
+ scopes, app hotkey composables, recorders, held-key and hint composables, or
8
+ replacing @keydown handlers and window keydown listeners.
9
+ metadata:
10
+ purpose: >-
11
+ Vue bindings for TanStack Hotkeys: composables, reactive options,
12
+ providers, and the Vue-specific reactivity mistakes to avoid.
13
+ type: framework
14
+ framework: vue
15
+ library: '@tanstack/vue-hotkeys'
16
+ library_version: '0.15.0'
17
+ sources:
18
+ - src/useHotkey.ts
19
+ - src/useHotkeys.ts
20
+ - src/HotkeysProvider.tsx
21
+ - src/HotkeysProviderContext.ts
22
+ - src/useAppHotkeys.ts
23
+ - src/useHotkeyRecorder.ts
24
+ - TanStack/hotkeys:docs/framework/vue/quick-start.md
25
+ - TanStack/hotkeys:docs/framework/vue/guides/hotkeys-provider-and-scopes.md
26
+ requires:
27
+ - '@tanstack/hotkeys#hotkeys-core'
28
+ ---
29
+
30
+ # TanStack Hotkeys in Vue
31
+
32
+ Before starting, load the `@tanstack/hotkeys#hotkeys-core` skill (`npx @tanstack/intent@latest load @tanstack/hotkeys#hotkeys-core`).
33
+
34
+ ```vue
35
+ <script setup lang="ts">
36
+ import { ref } from 'vue'
37
+ import { formatForDisplay, useHotkey } from '@tanstack/vue-hotkeys'
38
+
39
+ const isDirty = ref(false)
40
+ const panel = ref<HTMLDivElement | null>(null)
41
+
42
+ useHotkey('Mod+S', () => save(), { enabled: isDirty })
43
+ useHotkey('Escape', () => closePanel(), { target: panel })
44
+ </script>
45
+
46
+ <template>
47
+ <div ref="panel" tabindex="0" @input="isDirty = true">
48
+ Save <kbd>{{ formatForDisplay('Mod+S') }}</kbd>
49
+ </div>
50
+ </template>
51
+ ```
52
+
53
+ ## API
54
+
55
+ | API | What it's for |
56
+ | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
57
+ | `useHotkey(hotkey, callback, options?)` | Register one chord, sequence, or app hotkey while the component is mounted; the hotkey and options can be refs or getters. |
58
+ | `useHotkeys(definitions, commonOptions?)` | Register several hotkeys, or a ref or getter of a list built from data, in one call. |
59
+ | `<HotkeysProvider>` | Set default options, a scope (`exclusive` for dialogs), `enabled`, and app hotkeys for its children, and emit `custom-hotkeys-change`. |
60
+ | `provideHotkeysContext(options)` | Provide the same options from a component's setup instead of rendering `HotkeysProvider`; they apply to its children. |
61
+ | `useHotkeysContext()` | Read the nearest provider's merged defaults and enabled state, or `null` outside a provider. |
62
+ | `useDefaultHotkeysOptions()` | Read the default options merged from every provider above. |
63
+ | `useAppHotkeys()` | Get a computed ref of the declared app hotkeys with the user's custom hotkeys applied. |
64
+ | `useAppHotkeyList()` | Get a computed ref listing every app hotkey with its status, for a cheat sheet or settings screen. |
65
+ | `useCustomHotkeys()` | Read, change, and reset the user's custom hotkeys, and check a new one for conflicts. |
66
+ | `useHotkeyRecorder(options)` | Record a chord or sequence the user presses; destructure the refs it returns. |
67
+ | `useHotkeyRegistrations()` | Get a ref listing every registered hotkey with its options and `meta`. |
68
+ | `useHotkeyHint(hotkey, options?)` | Get a computed ref of whether held modifiers should reveal a shortcut's hint. |
69
+ | `useKeyHold(key)` | Get a ref of whether one key is held. |
70
+ | `useHeldKeys()` | Get a ref of every key that's held right now. |
71
+ | `useHeldKeyCodes()` | Get a ref mapping each held key to its physical `event.code`. |
72
+
73
+ The package also re-exports `@tanstack/hotkeys`, such as `formatForDisplay`, `matchesKeyboardEvent`, and `defineAppHotkeys`.
74
+
75
+ No provider is required. Call the composables synchronously in setup.
76
+
77
+ ## Pitfalls
78
+
79
+ - **Pass the ref, not `.value`.** `{ enabled: isDirty.value }` is read once, so the hotkey never turns on, and `{ target: panel.value }` is `null` in setup, so the hotkey never registers. Pass `isDirty` and `panel`. `enabled` and `target` accept refs and getters; for other options that depend on state, pass a getter for the whole options object.
80
+ - **Put the provider around the dialog component.** A `HotkeysProvider` in a component's own template, or `provideHotkeysContext` in its own setup, applies only to its children. Register the dialog's hotkeys in a child component, and give each open dialog its own scope name, such as `` `dialog-${useId()}` ``.
81
+ - **Destructure the recorder.** Templates unwrap only top-level refs, so `v-if="recorder.isRecording"` is always true. Use `const { isRecording, startRecording } = useHotkeyRecorder(...)`.
82
+ - **The callback isn't tracked.** Read refs and props inside it.
83
+ - **Call the composables directly.** A `useShortcut(keys: string)` wrapper has to cast strings to hotkeys, which turns off type checking and drops sequences, reactive options, and app hotkeys.
84
+
85
+ When declaring app hotkeys, building a cheat sheet, or letting users rebind shortcuts, load `@tanstack/hotkeys#app-hotkeys` and read [app hotkeys in Vue](references/app-hotkeys.md).
86
+
87
+ ## API discovery and docs
88
+
89
+ Signatures: `node_modules/@tanstack/vue-hotkeys/dist/*.d.ts`. Guides: `https://tanstack.com/hotkeys/latest/docs/framework/vue/guides/<page>.md`, such as `hotkeys`, `hotkeys-provider-and-scopes`, or `app-hotkeys`. Devtools: `HotkeysDevtoolsPanel` from `@tanstack/vue-hotkeys-devtools`.
@@ -0,0 +1,82 @@
1
+ # App hotkeys in Vue
2
+
3
+ The `@tanstack/hotkeys#app-hotkeys` skill owns the rules. This is the Vue wiring.
4
+
5
+ ```ts
6
+ // app-hotkeys.ts
7
+ import { defineAppHotkeys } from '@tanstack/vue-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/vue-hotkeys' {
15
+ interface Register {
16
+ appHotkeys: typeof appHotkeys
17
+ }
18
+ }
19
+ ```
20
+
21
+ ```vue
22
+ <!-- App.vue -->
23
+ <script setup lang="ts">
24
+ import { HotkeysProvider } from '@tanstack/vue-hotkeys'
25
+ import type { CustomHotkeys } from '@tanstack/vue-hotkeys'
26
+ import { appHotkeys } from './app-hotkeys'
27
+ import AppContent from './AppContent.vue'
28
+
29
+ const saved: CustomHotkeys = JSON.parse(localStorage.getItem('hotkeys') ?? '{}')
30
+ const saveHotkeys = (custom: CustomHotkeys) =>
31
+ localStorage.setItem('hotkeys', JSON.stringify(custom))
32
+ </script>
33
+
34
+ <template>
35
+ <HotkeysProvider
36
+ :app-hotkeys="appHotkeys"
37
+ :custom-hotkeys="saved"
38
+ @custom-hotkeys-change="saveHotkeys"
39
+ >
40
+ <AppContent />
41
+ </HotkeysProvider>
42
+ </template>
43
+ ```
44
+
45
+ `useAppHotkeys()` returns a computed ref, so register inside a getter: `useHotkey(() => appHotkeys.value['doc.save'], save)`. Build a cheat sheet from `useAppHotkeyList()`.
46
+
47
+ A settings row records with `useHotkeyRecorder` and saves through `useCustomHotkeys`:
48
+
49
+ ```vue
50
+ <script setup lang="ts">
51
+ import { computed } from 'vue'
52
+ import {
53
+ formatForDisplay,
54
+ useAppHotkeys,
55
+ useCustomHotkeys,
56
+ useHotkeyRecorder,
57
+ } from '@tanstack/vue-hotkeys'
58
+ import type { AppHotkeyId } from '@tanstack/vue-hotkeys'
59
+
60
+ const props = defineProps<{ id: AppHotkeyId }>()
61
+ const appHotkeys = useAppHotkeys()
62
+ const appHotkey = computed(() => appHotkeys.value[props.id])
63
+ const { findConflicts, setCustomHotkey, resetCustomHotkey } = useCustomHotkeys()
64
+
65
+ const { startRecording } = useHotkeyRecorder({
66
+ maxSteps: Infinity,
67
+ validate: (hotkey) => {
68
+ const [conflict] = findConflicts(props.id, hotkey)
69
+ return conflict ? `Used by ${conflict.meta?.name ?? conflict.id}` : true
70
+ },
71
+ onRecord: (hotkey) => setCustomHotkey(props.id, hotkey),
72
+ onClear: () => setCustomHotkey(props.id, null),
73
+ })
74
+ </script>
75
+
76
+ <template>
77
+ {{ appHotkey.meta?.name }}:
78
+ {{ appHotkey.hotkey ? formatForDisplay(appHotkey.hotkey) : 'Unassigned' }}
79
+ <button @click="startRecording">Change</button>
80
+ <button @click="resetCustomHotkey(id)">Reset</button>
81
+ </template>
82
+ ```