Files
Pien-Studio/apps/web/hooks/use-editor-shortcuts.ts
2026-05-31 03:04:30 +07:00

85 lines
2.2 KiB
TypeScript

import React from "react";
type UseEditorShortcutsOptions = {
onSave: () => void;
onUndo: () => void;
onRedo: () => void;
onCopy: () => void;
onCut: () => void;
onPaste: (e?: ClipboardEvent) => void;
onDelete: () => void;
};
function isEditableTarget(e: Event) {
return (
e.target instanceof HTMLElement &&
/^(input|textarea|select)$/i.test(e.target.tagName)
);
}
export function useEditorShortcuts(options: UseEditorShortcutsOptions) {
const { onSave, onUndo, onRedo, onCopy, onCut, onPaste, onDelete } = options;
React.useEffect(() => {
function handleKeyDown(e: KeyboardEvent) {
if (e.ctrlKey || e.metaKey) {
if (e.key.toLowerCase() === "s") {
e.preventDefault();
onSave();
return;
}
if (e.key.toLowerCase() === "z" && !e.shiftKey) {
e.preventDefault();
onUndo();
return;
}
if (e.key.toLowerCase() === "z" && e.shiftKey) {
e.preventDefault();
onRedo();
return;
}
if (e.key.toLowerCase() === "y") {
e.preventDefault();
onRedo();
return;
}
}
if (e.key === "Delete" || e.key === "Backspace") {
if (isEditableTarget(e)) return;
e.preventDefault();
onDelete();
}
}
function handleCopy(e: ClipboardEvent) {
if (isEditableTarget(e)) return;
e.preventDefault();
onCopy();
}
function handleCut(e: ClipboardEvent) {
if (isEditableTarget(e)) return;
e.preventDefault();
onCut();
}
function handlePaste(e: ClipboardEvent) {
if (isEditableTarget(e)) return;
e.preventDefault();
onPaste(e);
}
window.addEventListener("keydown", handleKeyDown);
window.addEventListener("copy", handleCopy);
window.addEventListener("cut", handleCut);
window.addEventListener("paste", handlePaste);
return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener("copy", handleCopy);
window.removeEventListener("cut", handleCut);
window.removeEventListener("paste", handlePaste);
};
}, [onCopy, onCut, onDelete, onPaste, onRedo, onSave, onUndo]);
}