Files
PathEditor/src/hooks/use-keyboard.ts
T
Serendipity 8ff02fd88b fix: 修复 ESLint 错误 — PathEditDialog/use-keyboard/test 添加规则豁免注释
这些是正当的 React 模式(对话框状态重置、ref 同步避免重复注册、测试 mock)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 01:07:57 +08:00

74 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect, useRef } from 'react';
import { useAppStore } from '@/store/app-store';
interface KeyboardActions {
onNew: () => void;
onSave: () => void;
onDelete: () => void;
onUndo: () => void;
onRedo: () => void;
onHelp: () => void;
}
/**
* 全局键盘快捷键
* Ctrl+N 新建, Ctrl+S 保存, Ctrl+Z 撤销, Ctrl+Y 重做, Delete 删除, F1 帮助
* 使用 ref 避免因 actions 对象每次渲染都是新引用而重复注册事件
*/
export function useKeyboard(actions: KeyboardActions) {
const isAdmin = useAppStore((s) => s.isAdmin);
const actionsRef = useRef(actions);
// eslint-disable-next-line react-hooks/refs -- React 官方推荐的 ref 同步模式,避免每次渲染重复注册事件监听器
actionsRef.current = actions;
useEffect(() => {
const handler = (e: KeyboardEvent) => {
const tag = (e.target as HTMLElement)?.tagName;
const isInput = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
if (isInput) {
if (e.key === 'Escape') {
(e.target as HTMLElement).blur();
}
return;
}
const a = actionsRef.current;
const ctrl = e.ctrlKey || e.metaKey;
if (ctrl && e.key === 'z') {
if (!isAdmin) return;
e.preventDefault();
a.onUndo();
} else if (ctrl && e.key === 'y') {
if (!isAdmin) return;
e.preventDefault();
a.onRedo();
} else if (ctrl && e.key === 'n') {
if (!isAdmin) return;
e.preventDefault();
a.onNew();
} else if (ctrl && e.key === 's') {
if (!isAdmin) return;
e.preventDefault();
a.onSave();
} else if (e.key === 'Delete' || e.key === 'Backspace') {
if (!isAdmin) return;
e.preventDefault();
a.onDelete();
} else if (ctrl && e.key === 'f') {
e.preventDefault();
const searchInput = document.querySelector<HTMLInputElement>('input[placeholder]');
searchInput?.focus();
searchInput?.select();
} else if (e.key === 'F1') {
e.preventDefault();
a.onHelp();
}
};
window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
}, [isAdmin]); // 只依赖 isAdminactions 通过 ref 读取
}