UNPKG

@intility/bifrost-react

Version:

React library for Intility's design system, Bifrost.

217 lines (213 loc) 5.69 kB
"use client"; import { c as _c } from "react-compiler-runtime"; import { useEffect, useSyncExternalStore } from "react"; import useLocale from "./useLocale.js"; import useEffectEvent from "./useEffectEvent.js"; /** * Configuration object for defining keyboard shortcuts with explicit modifiers. */ const defaultConfig = { ctrlOrCmd: true, shift: false, alt: false, preventDefault: true }; /** * React hook for handling keyboard shortcuts (hotkeys). * * Supports two APIs: * 1. useHotkey("k", callback) - Listens to "Ctrl/⌘ + key" * 2. useHotkey("k", callback, options) - "Ctrl/⌘ + key" is still default, but * provides options to add `shift` or `alt`, or turn off `ctrlOrCmd` and * `preventDefault` * * @param key - The key to listen for (e.g., "k", "Escape", "ArrowUp", "F1", " " for Space) * @param callback - Function called when hotkey is triggered * @param config - Optional configuration object provides options to add `shift` * or `alt`, or turn off `ctrlOrCmd` and `preventDefault` * @returns Object with hotkeyText for display purposes * * @see https://bifrost.intility.com/react/useHotkey * * @example * // Focus search input on Ctrl/⌘ + K * const { hotkeyText } = useHotkey("k", (e) => { * searchInputRef.current?.focus(); * }); * // `hotkeyText` will be "Ctrl + K" on win/linux, and "⌘ + K" on macos * * @example * // Save on Ctrl/⌘ + S * const { hotkeyText } = useHotkey("s", (e) => { * save(); * }); * * @example * // Save all with Ctrl/⌘ + Shift/⇧ + S * const { hotkeyText } = useHotkey("s", (e) => { * saveAll(); * }, { shift: true }); * // `hotkeyText` will be "Ctrl + Shift + S" on win/linux, and "⌘ + ⇧ + S" on macos * * @example * // Escape (no modifiers) * const { hotkeyText } = useHotkey("Escape", (e) => { * cancel(); * }, { ctrlOrCmd: false }); * * @example * // Special keys - Escape (no modifiers) * const { hotkeyText } = useHotkey("Escape", (e) => { * cancel(); * }, { ctrlOrCmd: false }); */ function useHotkey(key, callback, config) { const $ = _c(31); const isMac = useSyncExternalStore(_temp2, _temp3, _temp4); const locale = useLocale(); let t0; if ($[0] !== config) { t0 = { ...defaultConfig, ...config }; $[0] = config; $[1] = t0; } else { t0 = $[1]; } const cfg = t0; const callbackEvent = useEffectEvent(callback); let t1; if ($[2] !== callbackEvent || $[3] !== cfg.alt || $[4] !== cfg.ctrlOrCmd || $[5] !== cfg.preventDefault || $[6] !== cfg.shift || $[7] !== isMac || $[8] !== key) { t1 = () => { const onKeyDown = event => { const keyMatches = event.key.toLowerCase() === key.toLowerCase(); if (!keyMatches) { return; } if (event.shiftKey !== cfg.shift) { return; } if (event.altKey !== cfg.alt) { return; } if (isMac) { if (event.metaKey !== cfg.ctrlOrCmd || event.ctrlKey) { return; } } else { if (event.ctrlKey !== cfg.ctrlOrCmd || event.metaKey) { return; } } if (cfg.preventDefault) { event.preventDefault(); } callbackEvent(event); }; document.addEventListener("keydown", onKeyDown); return () => { document.removeEventListener("keydown", onKeyDown); }; }; $[2] = callbackEvent; $[3] = cfg.alt; $[4] = cfg.ctrlOrCmd; $[5] = cfg.preventDefault; $[6] = cfg.shift; $[7] = isMac; $[8] = key; $[9] = t1; } else { t1 = $[9]; } let t2; if ($[10] !== cfg.alt || $[11] !== cfg.ctrlOrCmd || $[12] !== cfg.preventDefault || $[13] !== cfg.shift || $[14] !== isMac || $[15] !== key) { t2 = [key, cfg.ctrlOrCmd, cfg.shift, cfg.alt, cfg.preventDefault, isMac]; $[10] = cfg.alt; $[11] = cfg.ctrlOrCmd; $[12] = cfg.preventDefault; $[13] = cfg.shift; $[14] = isMac; $[15] = key; $[16] = t2; } else { t2 = $[16]; } useEffect(t1, t2); const t3 = isMac ? "\u2326" : "Del"; let t4; if ($[17] !== locale.space || $[18] !== t3) { t4 = { ArrowUp: "\u2191", ArrowDown: "\u2193", ArrowLeft: "\u2190", ArrowRight: "\u2192", Escape: "Esc", Delete: t3, " ": locale.space }; $[17] = locale.space; $[18] = t3; $[19] = t4; } else { t4 = $[19]; } const keyMap = t4; let t5; if ($[20] !== key || $[21] !== keyMap) { t5 = keyMap[key] ?? (key.length === 1 ? key.toUpperCase() : key); $[20] = key; $[21] = keyMap; $[22] = t5; } else { t5 = $[22]; } const keyDisplay = t5; let modifiers; if ($[23] !== cfg.alt || $[24] !== cfg.ctrlOrCmd || $[25] !== cfg.shift || $[26] !== isMac || $[27] !== keyDisplay) { modifiers = []; if (cfg.ctrlOrCmd) { modifiers.push(isMac ? "\u2318" : "Ctrl"); } if (cfg.alt) { modifiers.push(isMac ? "\u2325" : "Alt"); } if (cfg.shift) { modifiers.push(isMac ? "\u21E7" : "Shift"); } modifiers.push(keyDisplay); $[23] = cfg.alt; $[24] = cfg.ctrlOrCmd; $[25] = cfg.shift; $[26] = isMac; $[27] = keyDisplay; $[28] = modifiers; } else { modifiers = $[28]; } const hotkeyText = modifiers.join(" + "); let t6; if ($[29] !== hotkeyText) { t6 = { hotkeyText }; $[29] = hotkeyText; $[30] = t6; } else { t6 = $[30]; } return t6; } function _temp4() { return false; } function _temp3() { return globalThis.navigator?.userAgent?.includes("Mac") ?? false; } function _temp2() { return _temp; } function _temp() {} export default useHotkey;