@tanstack/solid-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/solid-hotkeys",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "SolidJS 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/solid-store": "^0.11.2"
41
43
  },
42
44
  "devDependencies": {
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: solid-hotkeys
3
+ description: >-
4
+ Use when adding or fixing keyboard shortcuts in a SolidJS app with
5
+ @tanstack/solid-hotkeys: createHotkey and createHotkeys, accessor options and
6
+ signal targets, HotkeysProvider defaults and dialog scopes, app hotkey
7
+ primitives, recorders, held-key and hint primitives, or replacing onMount
8
+ keydown listeners.
9
+ metadata:
10
+ purpose: >-
11
+ Solid bindings for TanStack Hotkeys: primitives, accessors, providers, and
12
+ the Solid-specific reactivity mistakes to avoid.
13
+ type: framework
14
+ framework: solid
15
+ library: '@tanstack/solid-hotkeys'
16
+ library_version: '0.15.0'
17
+ sources:
18
+ - src/createHotkey.ts
19
+ - src/createHotkeys.ts
20
+ - src/HotkeysProvider.tsx
21
+ - src/appHotkeys.ts
22
+ - TanStack/hotkeys:docs/framework/solid/quick-start.md
23
+ - TanStack/hotkeys:docs/framework/solid/guides/hotkeys-provider-and-scopes.md
24
+ requires:
25
+ - '@tanstack/hotkeys#hotkeys-core'
26
+ ---
27
+
28
+ # TanStack Hotkeys in Solid
29
+
30
+ Before starting, load the `@tanstack/hotkeys#hotkeys-core` skill (`npx @tanstack/intent@latest load @tanstack/hotkeys#hotkeys-core`).
31
+
32
+ ```tsx
33
+ import { createSignal } from 'solid-js'
34
+ import { createHotkey } from '@tanstack/solid-hotkeys'
35
+
36
+ export function Editor() {
37
+ const [isDirty, setIsDirty] = createSignal(false)
38
+ const [panel, setPanel] = createSignal<HTMLDivElement | null>(null)
39
+
40
+ createHotkey(
41
+ 'Mod+S',
42
+ () => save(),
43
+ () => ({ enabled: isDirty() }),
44
+ )
45
+ createHotkey(
46
+ 'Escape',
47
+ () => closePanel(),
48
+ () => ({ target: panel() }),
49
+ )
50
+
51
+ return <div ref={setPanel} tabIndex={0} onInput={() => setIsDirty(true)} />
52
+ }
53
+ ```
54
+
55
+ ## API
56
+
57
+ | API | What it's for |
58
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
59
+ | `createHotkey(hotkey, callback, options?)` | Register one chord, sequence, or app hotkey for the owning component's lifetime; the hotkey and options can be accessors. |
60
+ | `createHotkeys(definitions, commonOptions?)` | Register several hotkeys, or an accessor of a list built from data, in one call. |
61
+ | `<HotkeysProvider>` | Set default options, a scope (`exclusive` for dialogs), `enabled`, and app hotkeys for the components inside it. |
62
+ | `useHotkeysContext()` | Read the nearest provider's merged defaults and enabled state, or `null` outside a provider. |
63
+ | `useDefaultHotkeysOptions()` | Read the default options merged from every provider above. |
64
+ | `useAppHotkeys()` | Get an accessor of the declared app hotkeys with the user's custom hotkeys applied. |
65
+ | `createAppHotkeyList()` | Get an accessor listing every app hotkey with its status, for a cheat sheet or settings screen. |
66
+ | `useCustomHotkeys()` | Read, change, and reset the user's custom hotkeys, and check a new one for conflicts. |
67
+ | `createHotkeyRecorder(options)` | Record a chord or sequence the user presses; its state fields are accessors. |
68
+ | `createHotkeyRegistrations()` | Get an accessor listing every registered hotkey with its options and `meta`. |
69
+ | `createHotkeyHint(hotkey, options?)` | Get an accessor of whether held modifiers should reveal a shortcut's hint. |
70
+ | `createKeyHold(key)` | Get an accessor of whether one key is held. |
71
+ | `createHeldKeys()` | Get an accessor of every key that's held right now. |
72
+ | `createHeldKeyCodes()` | Get an accessor mapping each held key to its physical `event.code`. |
73
+
74
+ The package also re-exports `@tanstack/hotkeys`, such as `formatForDisplay`, `matchesKeyboardEvent`, and `defineAppHotkeys`.
75
+
76
+ The state these return is accessors, including the recorder's fields. No provider is required.
77
+
78
+ ## Pitfalls
79
+
80
+ - **Pass options as an accessor.** `{ enabled: isDirty() }` is read once, during setup, so the hotkey never follows the signal. Use `() => ({ enabled: isDirty() })`. The hotkey and `createHotkeys`' definitions also accept accessors.
81
+ - **Keep element targets in a signal.** A `let el` variable is still `undefined` when the options are read, and an undefined target means `document`. Use `ref={setEl}` and `() => ({ target: el() })`; a `null` target waits for the element.
82
+ - **The callback isn't tracked.** Read props inside it, as `() => props.onClose()`, rather than passing `props.onClose` itself.
83
+ - **Register a dialog's hotkeys in a child of its provider.** A primitive uses the nearest `HotkeysProvider` above its component, so a dialog that renders `<HotkeysProvider scope={...} exclusive>` and creates `Escape` itself puts it in the suspended outer scope. Give each open dialog its own scope name, such as `` `dialog-${createUniqueId()}` ``.
84
+ - **Call the primitives directly.** A `createShortcut(keys: string)` wrapper has to cast strings to hotkeys, which turns off type checking and drops sequences, accessor options, and app hotkeys.
85
+
86
+ When declaring app hotkeys, building a cheat sheet, or letting users rebind shortcuts, load `@tanstack/hotkeys#app-hotkeys` and read [app hotkeys in Solid](references/app-hotkeys.md).
87
+
88
+ ## API discovery and docs
89
+
90
+ Signatures: `node_modules/@tanstack/solid-hotkeys/dist/*.d.ts`. Guides: `https://tanstack.com/hotkeys/latest/docs/framework/solid/guides/<page>.md`, such as `hotkeys`, `hotkeys-provider-and-scopes`, or `app-hotkeys`. Devtools: `hotkeysDevtoolsPlugin()` from `@tanstack/solid-hotkeys-devtools` in `@tanstack/solid-devtools`.
@@ -0,0 +1,83 @@
1
+ # App hotkeys in Solid
2
+
3
+ The `@tanstack/hotkeys#app-hotkeys` skill owns the rules. This is the Solid wiring.
4
+
5
+ ```ts
6
+ // app-hotkeys.ts
7
+ import { defineAppHotkeys } from '@tanstack/solid-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/solid-hotkeys' {
15
+ interface Register {
16
+ appHotkeys: typeof appHotkeys
17
+ }
18
+ }
19
+ ```
20
+
21
+ ```tsx no-check
22
+ import type { JSX } from 'solid-js'
23
+ import { HotkeysProvider } from '@tanstack/solid-hotkeys'
24
+ import type { CustomHotkeys } from '@tanstack/solid-hotkeys'
25
+ import { appHotkeys } from './app-hotkeys'
26
+
27
+ const saved: CustomHotkeys = JSON.parse(localStorage.getItem('hotkeys') ?? '{}')
28
+
29
+ export function Root(props: { children: JSX.Element }) {
30
+ return (
31
+ <HotkeysProvider
32
+ appHotkeys={appHotkeys}
33
+ customHotkeys={saved}
34
+ onCustomHotkeysChange={(custom) =>
35
+ localStorage.setItem('hotkeys', JSON.stringify(custom))
36
+ }
37
+ >
38
+ {props.children}
39
+ </HotkeysProvider>
40
+ )
41
+ }
42
+ ```
43
+
44
+ `useAppHotkeys()` returns an accessor, so register inside one: `createHotkey(() => appHotkeys()['doc.save'], save)`. Build a cheat sheet from `createAppHotkeyList()`.
45
+
46
+ A settings row records with `createHotkeyRecorder` and saves through `useCustomHotkeys`:
47
+
48
+ ```tsx
49
+ import {
50
+ createHotkeyRecorder,
51
+ formatForDisplay,
52
+ useAppHotkeys,
53
+ useCustomHotkeys,
54
+ } from '@tanstack/solid-hotkeys'
55
+ import type { AppHotkeyId } from '@tanstack/solid-hotkeys'
56
+
57
+ export function ShortcutRow(props: { id: AppHotkeyId }) {
58
+ const appHotkeys = useAppHotkeys()
59
+ const appHotkey = () => appHotkeys()[props.id]
60
+ const { findConflicts, setCustomHotkey, resetCustomHotkey } =
61
+ useCustomHotkeys()
62
+ const recorder = createHotkeyRecorder({
63
+ maxSteps: Infinity,
64
+ validate: (hotkey) => {
65
+ const [conflict] = findConflicts(props.id, hotkey)
66
+ return conflict ? `Used by ${conflict.meta?.name ?? conflict.id}` : true
67
+ },
68
+ onRecord: (hotkey) => setCustomHotkey(props.id, hotkey),
69
+ onClear: () => setCustomHotkey(props.id, null),
70
+ })
71
+
72
+ return (
73
+ <div>
74
+ {appHotkey().meta?.name}:{' '}
75
+ {appHotkey().hotkey
76
+ ? formatForDisplay(appHotkey().hotkey!)
77
+ : 'Unassigned'}
78
+ <button onClick={recorder.startRecording}>Change</button>
79
+ <button onClick={() => resetCustomHotkey(props.id)}>Reset</button>
80
+ </div>
81
+ )
82
+ }
83
+ ```