@tanstack/react-hotkeys
Version:
React adapter for TanStack Hotkeys
91 lines (89 loc) • 2.87 kB
JavaScript
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 };