Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | 4x 4x 4x 4x 64x 64x 27x 64x 4x 4x 4x 4x 4x 4x 2x 2x 2x 64x 47x 27x 20x 20x 20x 64x 64x 27x 64x 46x 30x 16x 1x 1x 1x 16x 16x 16x | import { useEffect, useCallback, useRef } from "react";
import { isSSR } from "../utils/environment";
/**
* Parse a keyboard shortcut string into its components
*
* @param shortcut - Shortcut string like "ctrl+shift+m"
* @returns Object with modifier keys and the main key
*/
function parseShortcut(shortcut: string): {
ctrl: boolean;
shift: boolean;
alt: boolean;
meta: boolean;
key: string;
} {
const parts = shortcut.toLowerCase().split("+");
const key = parts.pop() || "";
const modifiers = new Set(parts);
return {
ctrl: modifiers.has("ctrl") || modifiers.has("control"),
shift: modifiers.has("shift"),
alt: modifiers.has("alt"),
meta: modifiers.has("meta") || modifiers.has("cmd") || modifiers.has("command"),
key,
};
}
/**
* Hook to handle keyboard shortcuts
*
* @param shortcut - Keyboard shortcut string (e.g., "ctrl+shift+m")
* @param callback - Function to call when shortcut is pressed
* @param enabled - Whether the shortcut is enabled
*
* @example
* ```tsx
* useKeyboardShortcut("ctrl+shift+m", () => {
* setOpen(prev => !prev);
* });
* ```
*/
export function useKeyboardShortcut(
shortcut: string,
callback: () => void,
enabled = true
): void {
const callbackRef = useRef(callback);
// Update callback ref when callback changes
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
const parsed = parseShortcut(shortcut);
// Check if all modifiers match
const ctrlMatch = parsed.ctrl === (event.ctrlKey || event.metaKey);
const shiftMatch = parsed.shift === event.shiftKey;
const altMatch = parsed.alt === event.altKey;
const keyMatch = event.key.toLowerCase() === parsed.key;
if (ctrlMatch && shiftMatch && altMatch && keyMatch) {
event.preventDefault();
event.stopPropagation();
callbackRef.current();
}
},
[shortcut]
);
useEffect(() => {
if (isSSR() || !enabled) {
return;
}
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [handleKeyDown, enabled]);
}
/**
* Hook to handle Escape key press
*
* @param callback - Function to call when Escape is pressed
* @param enabled - Whether the handler is enabled
*/
export function useEscapeKey(callback: () => void, enabled = true): void {
const callbackRef = useRef(callback);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
useEffect(() => {
if (isSSR() || !enabled) {
return;
}
const handleKeyDown = (event: KeyboardEvent) => {
Eif (event.key === "Escape") {
event.preventDefault();
callbackRef.current();
}
};
document.addEventListener("keydown", handleKeyDown);
return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [enabled]);
}
|