@tanstack/react-hotkeys 0.20.2 → 0.22.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.
@@ -1,9 +1,15 @@
1
1
  import { UseHotkeyCommonOptions } from "./useHotkey.js";
2
- import { AppHotkeys, AppHotkeysStore, CustomHotkeys, HotkeyChordRecorderOptions, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
2
+ import { AppHotkeys, AppHotkeysStore, CustomHotkeys, HotkeyChordRecorderOptions, HotkeyPlatform, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
3
3
  import { ReactElement, ReactNode } from "react";
4
4
  import { HotkeysProviderScopeProps } from "@tanstack/hotkeys/adapter";
5
5
  //#region src/HotkeysProvider.d.ts
6
6
  export interface HotkeysProviderOptions {
7
+ /**
8
+ * The user's platform, for labels from `useFormatForDisplay`, hotkeys, and
9
+ * recorders. Defaults to detection. When server rendering, set it from the
10
+ * request with `detectPlatform(userAgent)`.
11
+ */
12
+ platform?: HotkeyPlatform;
7
13
  /**
8
14
  * Defaults for every hotkey registered by `useHotkey` and `useHotkeys`.
9
15
  * `requireReset` applies to chords and `timeout` applies to sequences.
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+
3
+ import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
4
+ import { detectPlatform } from "@tanstack/hotkeys";
5
+ import { useSyncExternalStore } from "react";
6
+ import { getServerPlatformSnapshot } from "@tanstack/hotkeys/adapter";
7
+
8
+ //#region src/_usePlatform.ts
9
+ const subscribe = () => () => {};
10
+ /**
11
+ * The provider's platform, or the detected one. Without a provider platform,
12
+ * server rendering and hydration use 'linux', then the client re-renders
13
+ * with the detected platform.
14
+ */
15
+ function usePlatform() {
16
+ const platform = useDefaultHotkeysOptions().platform;
17
+ return useSyncExternalStore(subscribe, () => platform ?? detectPlatform(), () => platform ?? getServerPlatformSnapshot());
18
+ }
19
+
20
+ //#endregion
21
+ export { usePlatform };
package/dist/index.d.ts CHANGED
@@ -5,9 +5,10 @@ import { ReactHotkeyRecorder, useHotkeyRecorder } from "./useHotkeyRecorder.js";
5
5
  import { useHotkeyRegistrations } from "./useHotkeyRegistrations.js";
6
6
  import { useHotkeyScopesState } from "./useHotkeyScopesState.js";
7
7
  import { useHotkeyHint } from "./useHotkeyHint.js";
8
+ import { useFormatForDisplay } from "./useFormatForDisplay.js";
8
9
  import { UseCustomHotkeysResult, useAppHotkeyList, useAppHotkeys, useCustomHotkeys } from "./useAppHotkeys.js";
9
10
  import { useHeldKeys } from "./useHeldKeys.js";
10
11
  import { useHeldKeyCodes } from "./useHeldKeyCodes.js";
11
12
  import { useKeyHold } from "./useKeyHold.js";
12
13
  export * from "@tanstack/hotkeys";
13
- export { HotkeysProvider, HotkeysProviderOptions, HotkeysProviderProps, ReactHotkeyRecorder, UseAppHotkeyOptions, UseCustomHotkeysResult, UseHotkeyChordOptions, UseHotkeyCommonOptions, UseHotkeyDefinition, UseHotkeyOptions, UseHotkeyOptionsFor, UseHotkeySequenceOptions, UseHotkeyTarget, useAppHotkeyList, useAppHotkeys, useCustomHotkeys, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyHint, useHotkeyRecorder, useHotkeyRegistrations, useHotkeyScopesState, useHotkeys, useHotkeysContext, useKeyHold };
14
+ export { HotkeysProvider, HotkeysProviderOptions, HotkeysProviderProps, ReactHotkeyRecorder, UseAppHotkeyOptions, UseCustomHotkeysResult, UseHotkeyChordOptions, UseHotkeyCommonOptions, UseHotkeyDefinition, UseHotkeyOptions, UseHotkeyOptionsFor, UseHotkeySequenceOptions, UseHotkeyTarget, useAppHotkeyList, useAppHotkeys, useCustomHotkeys, useDefaultHotkeysOptions, useFormatForDisplay, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyHint, useHotkeyRecorder, useHotkeyRegistrations, useHotkeyScopesState, useHotkeys, useHotkeysContext, useKeyHold };
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { useHotkeyRecorder } from "./useHotkeyRecorder.js";
5
5
  import { useHotkeyRegistrations } from "./useHotkeyRegistrations.js";
6
6
  import { useHotkeyScopesState } from "./useHotkeyScopesState.js";
7
7
  import { useHotkeyHint } from "./useHotkeyHint.js";
8
+ import { useFormatForDisplay } from "./useFormatForDisplay.js";
8
9
  import { useAppHotkeyList, useAppHotkeys, useCustomHotkeys } from "./useAppHotkeys.js";
9
10
  import { useHeldKeys } from "./useHeldKeys.js";
10
11
  import { useHeldKeyCodes } from "./useHeldKeyCodes.js";
@@ -12,4 +13,4 @@ import { useKeyHold } from "./useKeyHold.js";
12
13
 
13
14
  export * from "@tanstack/hotkeys"
14
15
 
15
- export { HotkeysProvider, useAppHotkeyList, useAppHotkeys, useCustomHotkeys, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyHint, useHotkeyRecorder, useHotkeyRegistrations, useHotkeyScopesState, useHotkeys, useHotkeysContext, useKeyHold };
16
+ export { HotkeysProvider, useAppHotkeyList, useAppHotkeys, useCustomHotkeys, useDefaultHotkeysOptions, useFormatForDisplay, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyHint, useHotkeyRecorder, useHotkeyRegistrations, useHotkeyScopesState, useHotkeys, useHotkeysContext, useKeyHold };
@@ -0,0 +1,27 @@
1
+ import { formatForDisplay } from "@tanstack/hotkeys";
2
+ //#region src/useFormatForDisplay.d.ts
3
+ /**
4
+ * Returns `formatForDisplay` with the platform from `HotkeysProvider`, or the
5
+ * detected platform without one. A `platform` passed to the returned function
6
+ * wins.
7
+ *
8
+ * Call the hook once at the top of the component; the returned function can
9
+ * be called anywhere, such as in a loop or a condition. Server-rendered apps
10
+ * should set the provider's `defaultOptions.platform` from the request with
11
+ * `detectPlatform(userAgent)`, so the server HTML matches the user's platform.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * function SaveButton() {
16
+ * const format = useFormatForDisplay()
17
+ *
18
+ * return (
19
+ * <button>
20
+ * Save <kbd>{format('Mod+S')}</kbd>
21
+ * </button>
22
+ * )
23
+ * }
24
+ * ```
25
+ */
26
+ export declare function useFormatForDisplay(): typeof formatForDisplay;
27
+ //#endregion
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+
3
+ import { usePlatform } from "./_usePlatform.js";
4
+ import { formatForDisplay } from "@tanstack/hotkeys";
5
+ import { useCallback } from "react";
6
+
7
+ //#region src/useFormatForDisplay.ts
8
+ /**
9
+ * Returns `formatForDisplay` with the platform from `HotkeysProvider`, or the
10
+ * detected platform without one. A `platform` passed to the returned function
11
+ * wins.
12
+ *
13
+ * Call the hook once at the top of the component; the returned function can
14
+ * be called anywhere, such as in a loop or a condition. Server-rendered apps
15
+ * should set the provider's `defaultOptions.platform` from the request with
16
+ * `detectPlatform(userAgent)`, so the server HTML matches the user's platform.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * function SaveButton() {
21
+ * const format = useFormatForDisplay()
22
+ *
23
+ * return (
24
+ * <button>
25
+ * Save <kbd>{format('Mod+S')}</kbd>
26
+ * </button>
27
+ * )
28
+ * }
29
+ * ```
30
+ */
31
+ function useFormatForDisplay() {
32
+ const platform = usePlatform();
33
+ return useCallback((hotkey, options) => formatForDisplay(hotkey, {
34
+ ...options,
35
+ platform: options?.platform ?? platform
36
+ }), [platform]);
37
+ }
38
+
39
+ //#endregion
40
+ export { useFormatForDisplay };
@@ -65,7 +65,7 @@ function useHotkeys(hotkeys, commonOptions = {}) {
65
65
  };
66
66
  const bindingsRef = useRef(null);
67
67
  useEffect(() => {
68
- bindingsRef.current ??= createHotkeyBindings();
68
+ bindingsRef.current ??= createHotkeyBindings({ afterDelegatedHandlers: true });
69
69
  const { hotkeys, ...context } = latestRef.current;
70
70
  bindingsRef.current.update(prepareHotkeyDefinitions(hotkeys, {
71
71
  ...context,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-hotkeys",
3
- "version": "0.20.2",
3
+ "version": "0.22.0",
4
4
  "description": "React adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -35,13 +35,14 @@
35
35
  "skills"
36
36
  ],
37
37
  "dependencies": {
38
- "@tanstack/hotkeys": "0.22.1"
38
+ "@tanstack/hotkeys": "0.24.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@eslint-react/eslint-plugin": "^4.2.3",
42
42
  "@tanstack/react-store": "^0.11.2",
43
43
  "@testing-library/react": "^16.3.3",
44
44
  "@types/react": "^19.3.0",
45
+ "@types/react-dom": "^19.3.0",
45
46
  "@vitejs/plugin-react": "^6.1.1",
46
47
  "eslint-plugin-react-compiler": "19.1.0-rc.2",
47
48
  "eslint-plugin-react-hooks": "^7.1.1",
@@ -55,6 +55,7 @@ export function SaveButton({ onSave }: { onSave: () => void }) {
55
55
  | `<HotkeysProvider>` | Set default options, a scope (`exclusive` for dialogs), `enabled`, and app hotkeys for the components inside it. |
56
56
  | `useHotkeysContext()` | Read the nearest provider's merged defaults and enabled state, or `null` outside a provider. |
57
57
  | `useDefaultHotkeysOptions()` | Read the default options merged from every provider above. |
58
+ | `useFormatForDisplay()` | Get `formatForDisplay` with the provider's `platform` filled in. Use it for labels in server-rendered apps; call it at the top of the component. |
58
59
  | `useAppHotkeys()` | Get the declared app hotkeys with the user's custom hotkeys applied, to pass to the register API. |
59
60
  | `useAppHotkeyList()` | List every app hotkey with its status, including ones whose components aren't mounted, for a cheat sheet or settings screen. |
60
61
  | `useCustomHotkeys()` | Read, change, and reset the user's custom hotkeys, and check a new one for conflicts. |
@@ -107,6 +108,7 @@ A hook uses the nearest `HotkeysProvider` above its component. Registering `Esca
107
108
  - **Call the hooks directly.** A `useShortcut(keys: string)` wrapper has to cast strings to hotkeys, which turns off type checking and drops sequences, options, app hotkeys, and the typed callback context.
108
109
  - **`matchesKeyboardEvent` takes the DOM event.** In a widget's `onKeyDown`, pass `event.nativeEvent`.
109
110
  - **In tests,** set `<HotkeysProvider defaultOptions={{ hotkey: { platform: 'mac' } }}>`.
111
+ - **Server-rendered labels need the user's platform.** On the server, `formatForDisplay` can't see the user's OS, so it renders Linux labels that differ after hydration for Mac and Windows users. Set the provider's `defaultOptions.platform` to `detectPlatform(userAgent)` from the request's `User-Agent` header, and format labels with `const format = useFormatForDisplay()` at the top of the component, then `format(hotkey)`. Keep plain `formatForDisplay` for labels that render only in the browser.
110
112
 
111
113
  When declaring app hotkeys, building a cheat sheet, or letting users rebind shortcuts, load `@tanstack/hotkeys#app-hotkeys` and read [app hotkeys in React](references/app-hotkeys.md).
112
114