UNPKG

@tanstack/react-hotkeys

Version:

React adapter for TanStack Hotkeys

82 lines (80 loc) • 2.43 kB
import { useHotkeysContext } from "./HotkeysProvider.js"; import { useAppHotkeysStoreState } from "./_appHotkeysStore.js"; import { resolveTarget } from "./utils.js"; import { useEffect, useRef } from "react"; import { createHotkeyBindings, prepareHotkeyDefinitions } from "@tanstack/hotkeys/adapter"; //#region src/useHotkeys.ts /** * React hook for registering several hotkeys at once: chords, sequences, and * app hotkeys. Accepts a dynamic array, so it is safe to use with * variable-length lists without violating the rules of hooks. * * Options merge in this order: provider defaults, app hotkey declaration, * commonOptions, per-hotkey options. Each option applies only to the kinds * that support it. Callbacks and options sync on every render. * * Per-row `enabled: false` keeps that registration (visible in devtools) and * only suppresses firing. Unbound app hotkeys and empty sequences register nothing. * * @param hotkeys - Hotkeys to register * @param commonOptions - Options applied to every hotkey (overridden by per-hotkey options) * * @example * ```tsx * function Editor() { * useHotkeys([ * { hotkey: 'Mod+S', callback: () => save() }, * { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } }, * { hotkey: 'Escape', callback: () => close() }, * ]) * } * ``` * * @example * ```tsx * function MenuShortcuts({ items }) { * // Dynamic hotkeys from data -- safe because it's a single hook call * useHotkeys( * items.map((item) => ({ * hotkey: item.shortcut, * callback: item.action, * options: { enabled: item.enabled }, * })), * { preventDefault: true }, * ) * } * ``` */ function useHotkeys(hotkeys, commonOptions = {}) { const defaults = useHotkeysContext()?.defaultOptions.hotkey; const appHotkeysStore = useAppHotkeysStoreState(); const latestRef = useRef({ hotkeys, commonOptions, defaults, appHotkeysStore }); latestRef.current = { hotkeys, commonOptions, defaults, appHotkeysStore }; const bindingsRef = useRef(null); useEffect(() => { bindingsRef.current ??= createHotkeyBindings(); const { hotkeys, ...context } = latestRef.current; bindingsRef.current.update(prepareHotkeyDefinitions(hotkeys, { ...context, resolveTarget })); }); useEffect(() => { return () => { bindingsRef.current?.destroy(); bindingsRef.current = null; }; }, []); } //#endregion export { useHotkeys };