@tanstack/solid-hotkeys 0.14.4 → 0.15.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 CHANGED
@@ -51,7 +51,7 @@
51
51
  # TanStack Hotkeys
52
52
 
53
53
  > [!NOTE]
54
- > TanStack Hotkeys is alpha. We are actively developing the library and are open to feedback and contributions.
54
+ > TanStack Hotkeys is in beta. We are actively developing the library and are open to feedback and contributions.
55
55
 
56
56
  Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objects, a cross-platform `Mod` key, a singleton Hotkey Manager, and utilities for cheatsheet UIs—built to stay SSR-friendly.
57
57
 
@@ -1,6 +1,7 @@
1
1
  import { CreateHotkeyCommonOptions } from "./createHotkey.js";
2
2
  import { AppHotkeys, AppHotkeysStore, CustomHotkeys, HotkeyChordRecorderOptions, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
3
3
  import { JSX, ParentComponent } from "solid-js";
4
+ import { HotkeysProviderScopeProps } from "@tanstack/hotkeys/adapter";
4
5
  //#region src/HotkeysProvider.d.ts
5
6
  export interface HotkeysProviderOptions {
6
7
  /**
@@ -13,9 +14,12 @@ export interface HotkeysProviderOptions {
13
14
  }
14
15
  interface HotkeysContextValue {
15
16
  readonly appHotkeysStore: AppHotkeysStore | null;
17
+ /** Merged with every ancestor provider's defaults, including `hotkey.scope`. */
16
18
  readonly defaultOptions: HotkeysProviderOptions;
19
+ /** Whether this provider and every ancestor are enabled. */
20
+ readonly enabled: boolean;
17
21
  }
18
- export interface HotkeysProviderProps {
22
+ export interface HotkeysProviderProps extends HotkeysProviderScopeProps {
19
23
  children: JSX.Element;
20
24
  /**
21
25
  * Every app hotkey, from `defineAppHotkeys`. Read when the provider is
@@ -29,11 +33,16 @@ export interface HotkeysProviderProps {
29
33
  customHotkeys?: CustomHotkeys | null;
30
34
  /** Called with the custom hotkeys after every change, ready to save. */
31
35
  onCustomHotkeysChange?: (customHotkeys: CustomHotkeys) => void;
36
+ /** Merged with the parent provider's defaults. */
32
37
  defaultOptions?: HotkeysProviderOptions;
33
38
  }
34
39
  /**
35
40
  * Provides default options, app hotkeys, and the user's custom hotkeys.
36
41
  *
42
+ * Nest providers to create hotkey scopes. A nested provider inherits app
43
+ * hotkeys and merges default options. With `scope`, its hotkeys fire only
44
+ * while it is mounted and enabled; with `exclusive`, only its hotkeys fire.
45
+ *
37
46
  * @example
38
47
  * ```tsx
39
48
  * <HotkeysProvider
@@ -46,6 +55,13 @@ export interface HotkeysProviderProps {
46
55
  * <App />
47
56
  * </HotkeysProvider>
48
57
  * ```
58
+ *
59
+ * @example
60
+ * ```tsx
61
+ * <HotkeysProvider scope="edit-dialog" exclusive={{ allow: ['global'] }}>
62
+ * <DialogBody />
63
+ * </HotkeysProvider>
64
+ * ```
49
65
  */
50
66
  export declare const HotkeysProvider: ParentComponent<HotkeysProviderProps>;
51
67
  export declare function useHotkeysContext(): HotkeysContextValue | null;
@@ -1,6 +1,7 @@
1
1
  import { AppHotkeysStore, publishAppHotkeysStore } from "@tanstack/hotkeys";
2
2
  import { createComponent } from "solid-js/web";
3
- import { createContext, createEffect, onCleanup, onMount, useContext } from "solid-js";
3
+ import { createContext, createEffect, createMemo, onCleanup, onMount, useContext } from "solid-js";
4
+ import { activateHotkeysProviderScope, getHotkeysProviderScopeKey, mergeHotkeysProviderDefaults } from "@tanstack/hotkeys/adapter";
4
5
 
5
6
  //#region src/HotkeysProvider.tsx
6
7
  const HotkeysContext = createContext(null);
@@ -8,6 +9,10 @@ const DEFAULT_OPTIONS = {};
8
9
  /**
9
10
  * Provides default options, app hotkeys, and the user's custom hotkeys.
10
11
  *
12
+ * Nest providers to create hotkey scopes. A nested provider inherits app
13
+ * hotkeys and merges default options. With `scope`, its hotkeys fire only
14
+ * while it is mounted and enabled; with `exclusive`, only its hotkeys fire.
15
+ *
11
16
  * @example
12
17
  * ```tsx
13
18
  * <HotkeysProvider
@@ -20,8 +25,16 @@ const DEFAULT_OPTIONS = {};
20
25
  * <App />
21
26
  * </HotkeysProvider>
22
27
  * ```
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * <HotkeysProvider scope="edit-dialog" exclusive={{ allow: ['global'] }}>
32
+ * <DialogBody />
33
+ * </HotkeysProvider>
34
+ * ```
23
35
  */
24
36
  const HotkeysProvider = (props) => {
37
+ const parent = useContext(HotkeysContext);
25
38
  const store = props.appHotkeys ? new AppHotkeysStore(props.appHotkeys, {
26
39
  customHotkeys: props.customHotkeys,
27
40
  onCustomHotkeysChange: (custom) => props.onCustomHotkeysChange?.(custom)
@@ -32,10 +45,25 @@ const HotkeysProvider = (props) => {
32
45
  onMount(() => {
33
46
  if (store) onCleanup(publishAppHotkeysStore(store));
34
47
  });
48
+ const active = () => (parent?.enabled ?? true) && props.enabled !== false;
49
+ const defaultOptions = createMemo(() => mergeHotkeysProviderDefaults(parent?.defaultOptions, props.defaultOptions, props.scope));
50
+ const scopeKey = createMemo(() => getHotkeysProviderScopeKey({
51
+ scope: props.scope,
52
+ exclusive: props.exclusive,
53
+ active: active(),
54
+ parent: parent?.defaultOptions.hotkey?.scope
55
+ }));
56
+ createEffect(() => {
57
+ const key = scopeKey();
58
+ if (key) onCleanup(activateHotkeysProviderScope(key));
59
+ });
35
60
  const contextValue = {
36
- appHotkeysStore: store,
61
+ appHotkeysStore: store ?? parent?.appHotkeysStore ?? null,
37
62
  get defaultOptions() {
38
- return props.defaultOptions ?? DEFAULT_OPTIONS;
63
+ return defaultOptions();
64
+ },
65
+ get enabled() {
66
+ return active();
39
67
  }
40
68
  };
41
69
  return createComponent(HotkeysContext.Provider, {
@@ -6,8 +6,8 @@ import { AppHotkey, AppHotkeyListItem, CustomHotkeys, Hotkey, RegisteredAppHotke
6
6
  *
7
7
  * @example
8
8
  * ```tsx
9
- * const hotkeys = useAppHotkeys()
10
- * createHotkey(() => hotkeys()['doc.save'], () => save())
9
+ * const appHotkeys = useAppHotkeys()
10
+ * createHotkey(() => appHotkeys()['doc.save'], () => save())
11
11
  * ```
12
12
  */
13
13
  export declare function useAppHotkeys(): () => RegisteredAppHotkeys;
@@ -17,8 +17,8 @@ function useRequiredStore(name) {
17
17
  *
18
18
  * @example
19
19
  * ```tsx
20
- * const hotkeys = useAppHotkeys()
21
- * createHotkey(() => hotkeys()['doc.save'], () => save())
20
+ * const appHotkeys = useAppHotkeys()
21
+ * createHotkey(() => appHotkeys()['doc.save'], () => save())
22
22
  * ```
23
23
  */
24
24
  function useAppHotkeys() {
@@ -1,4 +1,4 @@
1
- import { AppHotkey, HotkeyCallback, RegisterableHotkey, Target } from "@tanstack/hotkeys";
1
+ import { AppHotkey, HotkeyCallback, HotkeyCallbackContextFor, RegisterableHotkey, Target } from "@tanstack/hotkeys";
2
2
  import { AdapterAppHotkeyOptions, AdapterChordOptions, AdapterCommonOptions, AdapterHotkeyOptions, AdapterOptionsFor, AdapterSequenceOptions } from "@tanstack/hotkeys/adapter";
3
3
  //#region src/createHotkey.d.ts
4
4
  /**
@@ -38,5 +38,5 @@ type MaybeAccessor<T> = T | (() => T);
38
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<T extends RegisterableHotkey>(hotkey: MaybeAccessor<T>, callback: HotkeyCallback, options?: MaybeAccessor<CreateHotkeyOptionsFor<T>>): void;
41
+ export declare function createHotkey<const T extends RegisterableHotkey>(hotkey: MaybeAccessor<T>, callback: HotkeyCallback<HotkeyCallbackContextFor<T>>, options?: MaybeAccessor<CreateHotkeyOptionsFor<T>>): void;
42
42
  //#endregion
@@ -27,7 +27,7 @@ export interface SolidHotkeyRecorder<THotkey extends Hotkey = Hotkey> {
27
27
  * @example
28
28
  * ```tsx
29
29
  * const recorder = createHotkeyRecorder({ onRecord: setShortcut })
30
- * const sequenceRecorder = createHotkeyRecorder({ maxSteps: 3, onRecord: setShortcut })
30
+ * const sequenceRecorder = createHotkeyRecorder({ maxSteps: Infinity, onRecord: setShortcut })
31
31
  * ```
32
32
  */
33
33
  export declare function createHotkeyRecorder(options: HotkeyChordRecorderOptions): SolidHotkeyRecorder<HotkeyChord>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/solid-hotkeys",
3
- "version": "0.14.4",
3
+ "version": "0.15.0",
4
4
  "description": "SolidJS adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@tanstack/hotkeys": "0.15.0",
39
+ "@tanstack/hotkeys": "0.16.0",
40
40
  "@tanstack/solid-store": "^0.11.2"
41
41
  },
42
42
  "devDependencies": {