@intility/bifrost-react
Version:
React library for Intility's design system, Bifrost.
217 lines (213 loc) • 5.69 kB
JavaScript
"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;