UNPKG

@tanstack/react-hotkeys

Version:

React adapter for TanStack Hotkeys

91 lines (89 loc) • 2.87 kB
import { AppHotkeysStore, publishAppHotkeysStore } from "@tanstack/hotkeys"; import React, { createContext, useContext, useEffect, useMemo, useState } from "react"; import { activateHotkeysProviderScope, getHotkeysProviderScopeKey, mergeHotkeysProviderDefaults } from "@tanstack/hotkeys/adapter"; //#region src/HotkeysProvider.tsx const HotkeysContext = createContext(null); const DEFAULT_OPTIONS = {}; const NO_PARENT = { appHotkeysStore: null, defaultOptions: DEFAULT_OPTIONS, enabled: true }; /** * Provides default options, app hotkeys, and the user's custom hotkeys. * * Nest providers to create hotkey scopes. A nested provider inherits app * hotkeys and merges default options. With `scope`, its hotkeys fire only * while it is mounted and enabled; with `exclusive`, only its hotkeys fire. * * @example * ```tsx * <HotkeysProvider * appHotkeys={appHotkeys} * customHotkeys={JSON.parse(localStorage.getItem('hotkeys') ?? '{}')} * onCustomHotkeysChange={(custom) => * localStorage.setItem('hotkeys', JSON.stringify(custom)) * } * > * <App /> * </HotkeysProvider> * ``` * * @example * ```tsx * function EditDialog() { * return ( * <HotkeysProvider scope="edit-dialog" exclusive={{ allow: ['global'] }}> * <DialogBody /> * </HotkeysProvider> * ) * } * ``` */ function HotkeysProvider({ children, appHotkeys, customHotkeys, onCustomHotkeysChange, defaultOptions = DEFAULT_OPTIONS, scope, exclusive = false, enabled = true }) { const parent = useContext(HotkeysContext) ?? NO_PARENT; const [store] = useState(() => appHotkeys ? new AppHotkeysStore(appHotkeys, { customHotkeys, onCustomHotkeysChange }) : null); const active = parent.enabled && enabled; const scopeKey = getHotkeysProviderScopeKey({ scope, exclusive, active, parent: parent.defaultOptions.hotkey?.scope }); useEffect(() => { return scopeKey ? activateHotkeysProviderScope(scopeKey) : void 0; }, [scopeKey]); useEffect(() => { store?.setOptions({ onCustomHotkeysChange }); }, [store, onCustomHotkeysChange]); useEffect(() => { if (appHotkeys) store?.setAppHotkeys(appHotkeys); }, [store, appHotkeys]); useEffect(() => { if (!store) return; return publishAppHotkeysStore(store); }, [store]); const contextValue = useMemo(() => ({ appHotkeysStore: store ?? parent.appHotkeysStore, defaultOptions: mergeHotkeysProviderDefaults(parent.defaultOptions, defaultOptions, scope), enabled: active }), [ store, parent, defaultOptions, scope, active ]); return /* @__PURE__ */ React.createElement(HotkeysContext.Provider, { value: contextValue }, children); } function useHotkeysContext() { return useContext(HotkeysContext); } function useDefaultHotkeysOptions() { return useContext(HotkeysContext)?.defaultOptions ?? DEFAULT_OPTIONS; } //#endregion export { HotkeysProvider, useDefaultHotkeysOptions, useHotkeysContext };