54 lines
1.4 KiB
TypeScript
54 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect } from "react";
|
|
|
|
type ShortcutAction = () => void;
|
|
|
|
interface Shortcut {
|
|
key: string;
|
|
meta: boolean;
|
|
shift?: boolean;
|
|
action: ShortcutAction;
|
|
}
|
|
|
|
/**
|
|
* Register global keyboard shortcuts on window.
|
|
* Shortcuts are suppressed when focus is inside an input, textarea, or
|
|
* contentEditable element - except for Cmd+K which always fires.
|
|
*/
|
|
export function useGlobalShortcuts(shortcuts: Shortcut[]) {
|
|
useEffect(() => {
|
|
function handleKeyDown(event: KeyboardEvent) {
|
|
const meta = event.metaKey || event.ctrlKey;
|
|
|
|
for (const shortcut of shortcuts) {
|
|
if (
|
|
event.key.toLowerCase() === shortcut.key.toLowerCase() &&
|
|
meta === shortcut.meta &&
|
|
(shortcut.shift ?? false) === event.shiftKey
|
|
) {
|
|
// Allow Cmd+K even in inputs (standard command palette behavior)
|
|
if (shortcut.key !== "k") {
|
|
const target = event.target as HTMLElement;
|
|
const tag = target.tagName;
|
|
if (
|
|
tag === "INPUT" ||
|
|
tag === "TEXTAREA" ||
|
|
target.isContentEditable
|
|
) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
event.preventDefault();
|
|
shortcut.action();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
window.addEventListener("keydown", handleKeyDown);
|
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
}, [shortcuts]);
|
|
}
|