@tanstack/svelte-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,5 +1,6 @@
1
1
  import { AppHotkeysStore } from '@tanstack/hotkeys';
2
2
  import type { AppHotkeys, CustomHotkeys, HotkeyChordRecorderOptions, HotkeySequenceRecorderOptions } from '@tanstack/hotkeys';
3
+ import type { HotkeysProviderScopeProps } from '@tanstack/hotkeys/adapter';
3
4
  import type { CreateHotkeyCommonOptions } from './createHotkey.svelte';
4
5
  import type { Snippet } from 'svelte';
5
6
  export interface HotkeysProviderOptions {
@@ -12,7 +13,8 @@ export interface HotkeysProviderOptions {
12
13
  hotkeyRecorder?: Partial<Omit<HotkeyChordRecorderOptions & HotkeySequenceRecorderOptions, 'maxSteps' | 'onRecord' | 'validate'>>;
13
14
  }
14
15
  /** Options for `setHotkeysContext` and props for `HotkeysProvider`. */
15
- export interface HotkeysContextOptions {
16
+ export interface HotkeysContextOptions extends HotkeysProviderScopeProps {
17
+ /** Merged with the parent context's defaults. */
16
18
  defaultOptions?: HotkeysProviderOptions;
17
19
  /**
18
20
  * Every app hotkey, from `defineAppHotkeys`. Read when the context is set;
@@ -33,11 +35,19 @@ export interface HotkeysProviderProps extends HotkeysContextOptions {
33
35
  export declare const DEFAULT_OPTIONS: HotkeysProviderOptions;
34
36
  interface HotkeysContextValue {
35
37
  readonly appHotkeysStore: AppHotkeysStore | null;
38
+ /** Merged with every ancestor's defaults, including `hotkey.scope`. */
36
39
  readonly defaultOptions: HotkeysProviderOptions;
40
+ /** Whether this context and every ancestor are enabled. */
41
+ readonly enabled: boolean;
37
42
  }
38
43
  /**
39
44
  * Sets the hotkeys context for descendants. `HotkeysProvider` calls this; call
40
- * it directly in a root component's script instead of rendering the provider.
45
+ * it directly in a component's script instead of rendering the provider.
46
+ *
47
+ * Nest contexts to create hotkey scopes. A nested context inherits app
48
+ * hotkeys and merges default options. With `scope`, its hotkeys fire only
49
+ * while the component is mounted and enabled; with `exclusive`, only its
50
+ * hotkeys fire.
41
51
  *
42
52
  * @example
43
53
  * ```svelte
@@ -1,10 +1,17 @@
1
1
  import { createContext, onMount } from 'svelte';
2
2
  import { AppHotkeysStore, publishAppHotkeysStore } from '@tanstack/hotkeys';
3
+ import { getHotkeysProviderScopeKey, mergeHotkeysProviderDefaults, } from '@tanstack/hotkeys/adapter';
4
+ import { activateScopeWhileMounted } from './internal.svelte';
3
5
  export const DEFAULT_OPTIONS = {};
4
6
  const [useHotkeysContext, setHotkeysContextValue] = createContext();
5
7
  /**
6
8
  * Sets the hotkeys context for descendants. `HotkeysProvider` calls this; call
7
- * it directly in a root component's script instead of rendering the provider.
9
+ * it directly in a component's script instead of rendering the provider.
10
+ *
11
+ * Nest contexts to create hotkey scopes. A nested context inherits app
12
+ * hotkeys and merges default options. With `scope`, its hotkeys fire only
13
+ * while the component is mounted and enabled; with `exclusive`, only its
14
+ * hotkeys fire.
8
15
  *
9
16
  * @example
10
17
  * ```svelte
@@ -23,6 +30,7 @@ export function setHotkeysContext(options = {}) {
23
30
  }
24
31
  /** Sets the context from a getter, so default options stay reactive. */
25
32
  export function setHotkeysContextSource(source) {
33
+ const parent = getHotkeysContext();
26
34
  const { appHotkeys, customHotkeys } = source();
27
35
  const store = appHotkeys
28
36
  ? new AppHotkeysStore(appHotkeys, {
@@ -33,12 +41,28 @@ export function setHotkeysContextSource(source) {
33
41
  // Publish the store so devtools can list declared hotkeys.
34
42
  if (store)
35
43
  onMount(() => publishAppHotkeysStore(store));
36
- return setHotkeysContextValue({
37
- appHotkeysStore: store,
44
+ const context = setHotkeysContextValue({
45
+ appHotkeysStore: store ?? parent?.appHotkeysStore ?? null,
38
46
  get defaultOptions() {
39
- return source().defaultOptions ?? DEFAULT_OPTIONS;
47
+ const { defaultOptions, scope } = source();
48
+ return mergeHotkeysProviderDefaults(parent?.defaultOptions, defaultOptions, scope);
49
+ },
50
+ get enabled() {
51
+ return (parent?.enabled ?? true) && source().enabled !== false;
40
52
  },
41
53
  });
54
+ // Compared by value, so a new `exclusive` object does not reactivate the
55
+ // scope and lose its precedence.
56
+ activateScopeWhileMounted(() => {
57
+ const { scope, exclusive } = source();
58
+ return getHotkeysProviderScopeKey({
59
+ scope,
60
+ exclusive,
61
+ active: context.enabled,
62
+ parent: parent?.defaultOptions.hotkey?.scope,
63
+ });
64
+ });
65
+ return context;
42
66
  }
43
67
  export function getHotkeysContext() {
44
68
  try {
@@ -9,6 +9,9 @@
9
9
  appHotkeys,
10
10
  customHotkeys,
11
11
  onCustomHotkeysChange,
12
+ scope,
13
+ exclusive,
14
+ enabled,
12
15
  }: HotkeysProviderProps = $props()
13
16
 
14
17
  const context = setHotkeysContextSource(() => ({
@@ -16,6 +19,9 @@
16
19
  appHotkeys,
17
20
  customHotkeys,
18
21
  onCustomHotkeysChange,
22
+ scope,
23
+ exclusive,
24
+ enabled,
19
25
  }))
20
26
 
21
27
  // Keep declarations current (for example after hot module replacement).
@@ -1,5 +1,5 @@
1
1
  import type { Attachment } from 'svelte/attachments';
2
- import type { AppHotkey, HotkeyCallback, RegisterableHotkey } from '@tanstack/hotkeys';
2
+ import type { AppHotkey, HotkeyCallback, HotkeyCallbackContextFor, RegisterableHotkey } from '@tanstack/hotkeys';
3
3
  import type { AdapterAppHotkeyOptions, AdapterChordOptions, AdapterCommonOptions, AdapterHotkeyOptions, AdapterOptionsFor, AdapterSequenceOptions } from '@tanstack/hotkeys/adapter';
4
4
  import type { MaybeGetter } from './internal.svelte';
5
5
  /** The document or window to listen on. Use an attachment for elements. */
@@ -37,7 +37,7 @@ export type CreateAppHotkeyOptions<T extends AppHotkey = AppHotkey> = AdapterApp
37
37
  * ```
38
38
  */
39
39
  export declare function createHotkey<T extends AppHotkey>(hotkey: MaybeGetter<T>, callback: HotkeyCallback, options?: MaybeGetter<CreateAppHotkeyOptions<T>>): void;
40
- export declare function createHotkey<T extends RegisterableHotkey>(hotkey: MaybeGetter<T>, callback: HotkeyCallback, options?: MaybeGetter<CreateHotkeyOptionsFor<T>>): void;
40
+ export declare function createHotkey<const T extends RegisterableHotkey>(hotkey: MaybeGetter<T>, callback: HotkeyCallback<HotkeyCallbackContextFor<T>>, options?: MaybeGetter<CreateHotkeyOptionsFor<T>>): void;
41
41
  /**
42
42
  * Create an attachment for an element-scoped hotkey: a chord, a sequence, or
43
43
  * an app hotkey that listens on the attached element.
@@ -52,4 +52,4 @@ export declare function createHotkey<T extends RegisterableHotkey>(hotkey: Maybe
52
52
  * ```
53
53
  */
54
54
  export declare function createHotkeyAttachment<T extends AppHotkey>(hotkey: MaybeGetter<T>, callback: HotkeyCallback, options?: MaybeGetter<CreateAppHotkeyOptions<T>>): Attachment<HTMLElement>;
55
- export declare function createHotkeyAttachment<T extends RegisterableHotkey>(hotkey: MaybeGetter<T>, callback: HotkeyCallback, options?: MaybeGetter<CreateHotkeyOptionsFor<T>>): Attachment<HTMLElement>;
55
+ export declare function createHotkeyAttachment<const T extends RegisterableHotkey>(hotkey: MaybeGetter<T>, callback: HotkeyCallback<HotkeyCallbackContextFor<T>>, options?: MaybeGetter<CreateHotkeyOptionsFor<T>>): Attachment<HTMLElement>;
@@ -27,7 +27,7 @@ export interface SvelteHotkeyRecorder<THotkey extends Hotkey = Hotkey> {
27
27
  * ```svelte
28
28
  * <script lang="ts">
29
29
  * const recorder = createHotkeyRecorder({ onRecord: (hotkey) => (shortcut = hotkey) })
30
- * const sequenceRecorder = createHotkeyRecorder({ maxSteps: 3, onRecord: save })
30
+ * const sequenceRecorder = createHotkeyRecorder({ maxSteps: Infinity, onRecord: save })
31
31
  * </script>
32
32
  *
33
33
  * <button onclick={recorder.startRecording}>
@@ -1,5 +1,5 @@
1
1
  import type { Attachment } from 'svelte/attachments';
2
- import type { AppHotkey, HotkeyCallback, HotkeySequence, RegisterableHotkey, RegisterableHotkeyChord } from '@tanstack/hotkeys';
2
+ import type { AppHotkey, HotkeyCallback, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence } from '@tanstack/hotkeys';
3
3
  import type { MaybeGetter } from './internal.svelte';
4
4
  import type { CreateAppHotkeyOptions, CreateHotkeyChordOptions, CreateHotkeyCommonOptions, CreateHotkeyOptions, CreateHotkeySequenceOptions } from './createHotkey.svelte';
5
5
  /**
@@ -8,15 +8,15 @@ import type { CreateAppHotkeyOptions, CreateHotkeyChordOptions, CreateHotkeyComm
8
8
  * kind accept only shared options.
9
9
  */
10
10
  export type CreateHotkeyDefinition = {
11
- /** A chord string such as `'Mod+S'`, or a RawHotkey object */
11
+ /** A chord string such as `'Mod+S'`, or a chord object */
12
12
  hotkey: MaybeGetter<RegisterableHotkeyChord>;
13
13
  /** The function to call when the hotkey is pressed */
14
14
  callback: HotkeyCallback;
15
15
  /** Per-hotkey options (merged on top of commonOptions) */
16
16
  options?: MaybeGetter<CreateHotkeyChordOptions>;
17
17
  } | {
18
- /** A sequence such as `['G', 'G']` */
19
- hotkey: MaybeGetter<HotkeySequence>;
18
+ /** A sequence such as `['G', 'G']`, with each step a string or an object */
19
+ hotkey: MaybeGetter<RegisterableHotkeySequence>;
20
20
  callback: HotkeyCallback;
21
21
  options?: MaybeGetter<CreateHotkeySequenceOptions>;
22
22
  } | {
@@ -10,8 +10,8 @@ export interface SvelteAppHotkeys {
10
10
  * @example
11
11
  * ```svelte
12
12
  * <script lang="ts">
13
- * const hotkeys = getAppHotkeys()
14
- * createHotkey(() => hotkeys.current['doc.save'], () => save())
13
+ * const appHotkeys = getAppHotkeys()
14
+ * createHotkey(() => appHotkeys.current['doc.save'], () => save())
15
15
  * </script>
16
16
  * ```
17
17
  */
@@ -18,8 +18,8 @@ function requireStore(name) {
18
18
  * @example
19
19
  * ```svelte
20
20
  * <script lang="ts">
21
- * const hotkeys = getAppHotkeys()
22
- * createHotkey(() => hotkeys.current['doc.save'], () => save())
21
+ * const appHotkeys = getAppHotkeys()
22
+ * createHotkey(() => appHotkeys.current['doc.save'], () => save())
23
23
  * </script>
24
24
  * ```
25
25
  */
@@ -13,4 +13,9 @@ export declare function createStoreSubscriber<TState>(store: SubscribableStore<T
13
13
  * hotkeys store, so it re-runs when app hotkeys or custom hotkeys change.
14
14
  */
15
15
  export declare function createAppHotkeysSubscriber(store: AppHotkeysStore | null): () => void;
16
+ /**
17
+ * Activates a provider's scope while the component is mounted, and again
18
+ * whenever the key from `getHotkeysProviderScopeKey` changes.
19
+ */
20
+ export declare function activateScopeWhileMounted(getKey: () => string | null): void;
16
21
  export {};
@@ -1,4 +1,5 @@
1
1
  import { createSubscriber } from 'svelte/reactivity';
2
+ import { activateHotkeysProviderScope } from '@tanstack/hotkeys/adapter';
2
3
  export function resolveMaybeGetter(value) {
3
4
  return typeof value === 'function' ? value() : value;
4
5
  }
@@ -18,3 +19,11 @@ const noop = () => { };
18
19
  export function createAppHotkeysSubscriber(store) {
19
20
  return store ? createStoreSubscriber(store.store) : noop;
20
21
  }
22
+ /**
23
+ * Activates a provider's scope while the component is mounted, and again
24
+ * whenever the key from `getHotkeysProviderScopeKey` changes.
25
+ */
26
+ export function activateScopeWhileMounted(getKey) {
27
+ const key = $derived(getKey());
28
+ $effect(() => (key ? activateHotkeysProviderScope(key) : undefined));
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/svelte-hotkeys",
3
- "version": "0.14.4",
3
+ "version": "0.15.0",
4
4
  "description": "Svelte adapter for TanStack Hotkeys",
5
5
  "author": "Kunal Rao",
6
6
  "license": "MIT",
@@ -39,7 +39,7 @@
39
39
  "dist"
40
40
  ],
41
41
  "dependencies": {
42
- "@tanstack/hotkeys": "0.15.0"
42
+ "@tanstack/hotkeys": "0.16.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@sveltejs/package": "^3.0.0",