mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
refactor(layout): share popup menu dismiss handling
The language and theme menus carried two identical outside-click/Escape effects; fold them into one useMenuDismiss hook.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
ReactNode,
|
||||
RefObject,
|
||||
SVGProps,
|
||||
useCallback,
|
||||
useEffect,
|
||||
@@ -87,6 +88,39 @@ interface SidebarNavGroup {
|
||||
const flattenNavItems = (items: SidebarNavItem[]): SidebarNavLinkItem[] =>
|
||||
items.flatMap((item) => item.kind === 'drawer' ? item.children : [item]);
|
||||
|
||||
/** 点击菜单外或按下 Escape 时关闭弹出菜单 */
|
||||
function useMenuDismiss(
|
||||
open: boolean,
|
||||
menuRef: RefObject<HTMLDivElement | null>,
|
||||
onClose: () => void
|
||||
) {
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlePointerDown = (event: MouseEvent) => {
|
||||
if (!menuRef.current?.contains(event.target as Node)) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const handleEscape = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handlePointerDown);
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handlePointerDown);
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
};
|
||||
}, [open, menuRef, onClose]);
|
||||
}
|
||||
|
||||
function PluginSidebarIcon({ src }: { src: string }) {
|
||||
const [failed, setFailed] = useState(false);
|
||||
const showImage = Boolean(src) && !failed;
|
||||
@@ -355,57 +389,10 @@ export function MainLayout() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!languageMenuOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlePointerDown = (event: MouseEvent) => {
|
||||
if (!languageMenuRef.current?.contains(event.target as Node)) {
|
||||
setLanguageMenuOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEscape = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
setLanguageMenuOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handlePointerDown);
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handlePointerDown);
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
};
|
||||
}, [languageMenuOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!themeMenuOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handlePointerDown = (event: MouseEvent) => {
|
||||
if (!themeMenuRef.current?.contains(event.target as Node)) {
|
||||
setThemeMenuOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEscape = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
setThemeMenuOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handlePointerDown);
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handlePointerDown);
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
};
|
||||
}, [themeMenuOpen]);
|
||||
const closeLanguageMenu = useCallback(() => setLanguageMenuOpen(false), []);
|
||||
const closeThemeMenu = useCallback(() => setThemeMenuOpen(false), []);
|
||||
useMenuDismiss(languageMenuOpen, languageMenuRef, closeLanguageMenu);
|
||||
useMenuDismiss(themeMenuOpen, themeMenuRef, closeThemeMenu);
|
||||
|
||||
const toggleLanguageMenu = useCallback(() => {
|
||||
setLanguageMenuOpen((prev) => !prev);
|
||||
|
||||
Reference in New Issue
Block a user