@tanstack/solid-hotkeys 0.17.1 → 0.19.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.
@@ -27,8 +27,10 @@ export interface HotkeysProviderProps extends HotkeysProviderScopeProps {
27
27
  */
28
28
  appHotkeys?: AppHotkeys;
29
29
  /**
30
- * Starting custom hotkeys, such as saved data. Read once; the provider owns
31
- * the state afterwards. Invalid entries are ignored with a warning.
30
+ * The user's saved custom hotkeys. When the value changes, such as after
31
+ * saved data loads or the user signs in, it replaces the current custom
32
+ * hotkeys. `undefined` leaves them unchanged; `null` clears them. Invalid
33
+ * entries are ignored with a warning.
32
34
  */
33
35
  customHotkeys?: CustomHotkeys | null;
34
36
  /** Called with the custom hotkeys after every change, ready to save. */
@@ -1,7 +1,7 @@
1
- import { AppHotkeysStore, publishAppHotkeysStore } from "@tanstack/hotkeys";
1
+ import { publishAppHotkeysStore } from "@tanstack/hotkeys";
2
2
  import { createComponent } from "solid-js/web";
3
3
  import { createContext, createEffect, createMemo, onCleanup, onMount, useContext } from "solid-js";
4
- import { activateHotkeysProviderScope, getHotkeysProviderScopeKey, mergeHotkeysProviderDefaults } from "@tanstack/hotkeys/adapter";
4
+ import { activateHotkeysProviderScope, createAppHotkeysStore, getHotkeysProviderScopeKey, mergeHotkeysProviderDefaults } from "@tanstack/hotkeys/adapter";
5
5
 
6
6
  //#region src/HotkeysProvider.tsx
7
7
  const HotkeysContext = createContext(null);
@@ -35,13 +35,17 @@ const DEFAULT_OPTIONS = {};
35
35
  */
36
36
  const HotkeysProvider = (props) => {
37
37
  const parent = useContext(HotkeysContext);
38
- const store = props.appHotkeys ? new AppHotkeysStore(props.appHotkeys, {
38
+ const store = props.appHotkeys ? createAppHotkeysStore(props.appHotkeys, {
39
39
  customHotkeys: props.customHotkeys,
40
40
  onCustomHotkeysChange: (custom) => props.onCustomHotkeysChange?.(custom)
41
41
  }) : null;
42
42
  createEffect(() => {
43
43
  if (props.appHotkeys) store?.setAppHotkeys(props.appHotkeys);
44
44
  });
45
+ createEffect(() => {
46
+ const customHotkeys = props.customHotkeys;
47
+ store?.setOptions({ customHotkeys });
48
+ });
45
49
  onMount(() => {
46
50
  if (store) onCleanup(publishAppHotkeysStore(store));
47
51
  });
@@ -0,0 +1,18 @@
1
+ import { createSignal, onCleanup } from "solid-js";
2
+
3
+ //#region src/_useSelector.ts
4
+ /**
5
+ * Subscribes to a store until the owner is cleaned up and returns an accessor
6
+ * for the selected value, which updates when it changes (by `===`).
7
+ */
8
+ function useSelector(store, selector) {
9
+ const [selected, setSelected] = createSignal(selector(store.get()));
10
+ const { unsubscribe } = store.subscribe((state) => {
11
+ setSelected(() => selector(state));
12
+ });
13
+ onCleanup(unsubscribe);
14
+ return selected;
15
+ }
16
+
17
+ //#endregion
18
+ export { useSelector };
@@ -5,8 +5,7 @@
5
5
  * This is useful for debugging which physical key was pressed (e.g. distinguishing
6
6
  * left vs right Shift via "ShiftLeft" / "ShiftRight").
7
7
  *
8
- * This primitive uses `useSelector` from `@tanstack/solid-store` to subscribe
9
- * to the global KeyStateTracker.
8
+ * This primitive subscribes to the global KeyStateTracker.
10
9
  *
11
10
  * @returns Signal accessor for record mapping normalized key names to their `event.code` values
12
11
  *
@@ -1,5 +1,5 @@
1
+ import { useSelector } from "./_useSelector.js";
1
2
  import { getKeyStateTracker } from "@tanstack/hotkeys";
2
- import { useSelector } from "@tanstack/solid-store";
3
3
 
4
4
  //#region src/createHeldKeyCodes.ts
5
5
  /**
@@ -8,8 +8,7 @@ import { useSelector } from "@tanstack/solid-store";
8
8
  * This is useful for debugging which physical key was pressed (e.g. distinguishing
9
9
  * left vs right Shift via "ShiftLeft" / "ShiftRight").
10
10
  *
11
- * This primitive uses `useSelector` from `@tanstack/solid-store` to subscribe
12
- * to the global KeyStateTracker.
11
+ * This primitive subscribes to the global KeyStateTracker.
13
12
  *
14
13
  * @returns Signal accessor for record mapping normalized key names to their `event.code` values
15
14
  *
@@ -2,8 +2,7 @@
2
2
  /**
3
3
  * SolidJS primitive that returns a signal of currently held keyboard keys.
4
4
  *
5
- * This primitive uses `useSelector` from `@tanstack/solid-store` to subscribe
6
- * to the global KeyStateTracker and updates whenever keys are pressed
5
+ * This primitive subscribes to the global KeyStateTracker and updates whenever keys are pressed
7
6
  * or released.
8
7
  *
9
8
  * @returns Signal accessor for array of currently held key names
@@ -1,12 +1,11 @@
1
+ import { useSelector } from "./_useSelector.js";
1
2
  import { getKeyStateTracker } from "@tanstack/hotkeys";
2
- import { useSelector } from "@tanstack/solid-store";
3
3
 
4
4
  //#region src/createHeldKeys.ts
5
5
  /**
6
6
  * SolidJS primitive that returns a signal of currently held keyboard keys.
7
7
  *
8
- * This primitive uses `useSelector` from `@tanstack/solid-store` to subscribe
9
- * to the global KeyStateTracker and updates whenever keys are pressed
8
+ * This primitive subscribes to the global KeyStateTracker and updates whenever keys are pressed
10
9
  * or released.
11
10
  *
12
11
  * @returns Signal accessor for array of currently held key names
@@ -29,6 +29,7 @@ type MaybeAccessor<T> = T | (() => T);
29
29
  * sequence registers as its chord. An app hotkey uses its effective hotkey
30
30
  * from `HotkeysProvider` (including the user's custom hotkey) and its
31
31
  * declared meta and options, and registers nothing while unbound.
32
+ * `undefined`, `null`, or `''` registers nothing, for an optional shortcut.
32
33
  *
33
34
  * @example
34
35
  * ```tsx
@@ -37,6 +38,6 @@ type MaybeAccessor<T> = T | (() => T);
37
38
  * createHotkey('Escape', () => close(), () => ({ enabled: isOpen() }))
38
39
  * ```
39
40
  */
40
- export declare function createHotkey<T extends AppHotkey>(hotkey: MaybeAccessor<T>, callback: HotkeyCallback, options?: MaybeAccessor<CreateAppHotkeyOptions<T>>): void;
41
- export declare function createHotkey<const T extends RegisterableHotkey>(hotkey: MaybeAccessor<T>, callback: HotkeyCallback<HotkeyCallbackContextFor<T>>, options?: MaybeAccessor<CreateHotkeyOptionsFor<T>>): void;
41
+ export declare function createHotkey<T extends AppHotkey>(hotkey: MaybeAccessor<T | null | undefined>, callback: HotkeyCallback, options?: MaybeAccessor<CreateAppHotkeyOptions<T>>): void;
42
+ export declare function createHotkey<const T extends RegisterableHotkey>(hotkey: MaybeAccessor<T | null | undefined>, callback: HotkeyCallback<HotkeyCallbackContextFor<T>>, options?: MaybeAccessor<CreateHotkeyOptionsFor<T>>): void;
42
43
  //#endregion
@@ -5,7 +5,8 @@ import { AppHotkey, HeldModifierOptions, RegisterableHotkey } from "@tanstack/ho
5
5
  * The hint shows only while the hotkey can fire: some registration of it is
6
6
  * enabled and in an active scope. A sequence reveals its hint from its first
7
7
  * step, and an app hotkey matches its registrations by id and uses its
8
- * effective hotkey (false while unbound). This helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
8
+ * effective hotkey (false while unbound). No hotkey (`undefined`, `null`, or
9
+ * `''`) returns false. This helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
9
10
  */
10
- export declare function createHotkeyHint(hotkey: RegisterableHotkey | AppHotkey | (() => RegisterableHotkey | AppHotkey), options?: HeldModifierOptions | (() => HeldModifierOptions)): () => boolean;
11
+ export declare function createHotkeyHint(hotkey: RegisterableHotkey | AppHotkey | null | undefined | (() => RegisterableHotkey | AppHotkey | null | undefined), options?: HeldModifierOptions | (() => HeldModifierOptions)): () => boolean;
11
12
  //#endregion
@@ -1,7 +1,7 @@
1
+ import { useSelector } from "./_useSelector.js";
1
2
  import { useAppHotkeysStoreSource } from "./defaultOptions.js";
2
3
  import { getHotkeyManager, getKeyStateTracker, isAppHotkey, matchesHeldModifiers, resolveAppHotkey } from "@tanstack/hotkeys";
3
4
  import { createMemo } from "solid-js";
4
- import { useSelector } from "@tanstack/solid-store";
5
5
 
6
6
  //#region src/createHotkeyHint.ts
7
7
  /**
@@ -9,7 +9,8 @@ import { useSelector } from "@tanstack/solid-store";
9
9
  * The hint shows only while the hotkey can fire: some registration of it is
10
10
  * enabled and in an active scope. A sequence reveals its hint from its first
11
11
  * step, and an app hotkey matches its registrations by id and uses its
12
- * effective hotkey (false while unbound). This helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
12
+ * effective hotkey (false while unbound). No hotkey (`undefined`, `null`, or
13
+ * `''`) returns false. This helper does not register a shortcut. A hotkey on an element counts only while focus is inside it.
13
14
  */
14
15
  function createHotkeyHint(hotkey, options = {}) {
15
16
  const appHotkeys = useAppHotkeysStoreSource();
@@ -1,15 +1,13 @@
1
+ import { useSelector } from "./_useSelector.js";
1
2
  import { useDefaultHotkeysOptionsSource } from "./defaultOptions.js";
2
3
  import { HotkeyRecorder } from "@tanstack/hotkeys";
3
4
  import { createEffect, onCleanup } from "solid-js";
4
- import { useSelector } from "@tanstack/solid-store";
5
+ import { withDefaults } from "@tanstack/hotkeys/adapter";
5
6
 
6
7
  //#region src/createHotkeyRecorder.ts
7
8
  function createHotkeyRecorder(options) {
8
9
  const defaultOptions = useDefaultHotkeysOptionsSource();
9
- const resolve = () => ({
10
- ...defaultOptions().hotkeyRecorder,
11
- ...typeof options === "function" ? options() : options
12
- });
10
+ const resolve = () => withDefaults(defaultOptions().hotkeyRecorder, typeof options === "function" ? options() : options);
13
11
  const recorder = new HotkeyRecorder(resolve());
14
12
  const isRecording = useSelector(recorder.store, (state) => state.isRecording);
15
13
  const steps = useSelector(recorder.store, (state) => state.steps);
@@ -1,5 +1,5 @@
1
+ import { useSelector } from "./_useSelector.js";
1
2
  import { getHotkeyManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
2
- import { useSelector } from "@tanstack/solid-store";
3
3
 
4
4
  //#region src/createHotkeyRegistrations.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
+ import { useSelector } from "./_useSelector.js";
1
2
  import { getHotkeyManager } from "@tanstack/hotkeys";
2
- import { useSelector } from "@tanstack/solid-store";
3
3
 
4
4
  //#region src/createHotkeyScopesState.ts
5
5
  /**
@@ -12,7 +12,8 @@ export type CreateHotkeyDefinition = AdapterHotkeyDefinition<CreateHotkeyTarget>
12
12
  *
13
13
  * Options merge in this order: provider defaults, app hotkey declaration,
14
14
  * commonOptions, per-hotkey options. Each option applies only to the kinds
15
- * that support it. Unbound app hotkeys and empty sequences register nothing.
15
+ * that support it. Unbound app hotkeys, empty sequences, and `undefined`,
16
+ * `null`, or `''` hotkeys register nothing.
16
17
  * A `null` target skips registration until it is set.
17
18
  *
18
19
  * @example
@@ -9,7 +9,8 @@ import { createHotkeyBindings, prepareHotkeyDefinitions } from "@tanstack/hotkey
9
9
  *
10
10
  * Options merge in this order: provider defaults, app hotkey declaration,
11
11
  * commonOptions, per-hotkey options. Each option applies only to the kinds
12
- * that support it. Unbound app hotkeys and empty sequences register nothing.
12
+ * that support it. Unbound app hotkeys, empty sequences, and `undefined`,
13
+ * `null`, or `''` hotkeys register nothing.
13
14
  * A `null` target skips registration until it is set.
14
15
  *
15
16
  * @example
@@ -3,8 +3,7 @@ import { IndividualKey } from "@tanstack/hotkeys";
3
3
  /**
4
4
  * SolidJS primitive that returns whether a specific key is currently being held.
5
5
  *
6
- * This primitive uses `useSelector` from `@tanstack/solid-store` to subscribe
7
- * to the global KeyStateTracker and uses a selector to determine if the
6
+ * This primitive subscribes to the global KeyStateTracker and uses a selector to determine if the
8
7
  * specified key is held.
9
8
  *
10
9
  * @param key - The key to check (e.g., 'Shift', 'Control', 'A') - can be an accessor function
@@ -1,13 +1,12 @@
1
+ import { useSelector } from "./_useSelector.js";
1
2
  import { getKeyStateTracker } from "@tanstack/hotkeys";
2
3
  import { createMemo } from "solid-js";
3
- import { useSelector } from "@tanstack/solid-store";
4
4
 
5
5
  //#region src/createKeyHold.ts
6
6
  /**
7
7
  * SolidJS primitive that returns whether a specific key is currently being held.
8
8
  *
9
- * This primitive uses `useSelector` from `@tanstack/solid-store` to subscribe
10
- * to the global KeyStateTracker and uses a selector to determine if the
9
+ * This primitive subscribes to the global KeyStateTracker and uses a selector to determine if the
11
10
  * specified key is held.
12
11
  *
13
12
  * @param key - The key to check (e.g., 'Shift', 'Control', 'A') - can be an accessor function
@@ -1,5 +1,6 @@
1
1
  import { useHotkeysContext } from "./HotkeysProvider.js";
2
- import { Store, useSelector } from "@tanstack/solid-store";
2
+ import { useSelector } from "./_useSelector.js";
3
+ import { createStore } from "@tanstack/hotkeys/adapter";
3
4
 
4
5
  //#region src/defaultOptions.ts
5
6
  /** Captures the context once and reads current defaults inside reactive computations. */
@@ -7,7 +8,7 @@ function useDefaultHotkeysOptionsSource() {
7
8
  const context = useHotkeysContext();
8
9
  return () => context?.defaultOptions ?? {};
9
10
  }
10
- const NO_APP_HOTKEYS = new Store(null);
11
+ const NO_APP_HOTKEYS = createStore(null);
11
12
  /**
12
13
  * The provider's app hotkeys store and an accessor that tracks its state, so
13
14
  * computations re-run when app hotkeys or custom hotkeys change.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/solid-hotkeys",
3
- "version": "0.17.1",
3
+ "version": "0.19.0",
4
4
  "description": "SolidJS adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -38,8 +38,7 @@
38
38
  "skills"
39
39
  ],
40
40
  "dependencies": {
41
- "@tanstack/hotkeys": "0.19.0",
42
- "@tanstack/solid-store": "^0.11.2"
41
+ "@tanstack/hotkeys": "0.21.0"
43
42
  },
44
43
  "devDependencies": {
45
44
  "@solidjs/testing-library": "^0.8.10",