refactor(ui): unify context menus into a shared menu system
Five hand-rolled context menu implementations (image tiles, sidebar folders, sidebar albums, title bar theme switcher, plus duplicated close-listener effects in Gallery/Timeline) are replaced by shared primitives in src/components/menu/: - useDismissable: one outside-pointerdown + Escape dismissal hook - Menu.tsx: MenuPanel chrome, MenuItem (danger/disabled/checked/hint), MenuSeparator, MenuLabel, and SubMenu with viewport edge-flipping - ContextMenu: portal-rendered wrapper that measures and clamps to the viewport, fixing menus rendering off-screen and the latent fixed-inside-transform bug under framer-motion Reorder items The image right-click menu moves to ImageContextMenu.tsx, shared by Gallery and Timeline.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||
import { ImageRecord, tileSizeForZoom, useGalleryStore } from "../store";
|
||||
import { ContextMenu, ImageTile } from "./Gallery";
|
||||
import { ImageTile } from "./Gallery";
|
||||
import { ImageContextMenu } from "./ImageContextMenu";
|
||||
import { Tooltip } from "./Tooltip";
|
||||
|
||||
const GAP = 6;
|
||||
@@ -204,22 +205,6 @@ export function Timeline() {
|
||||
return () => el.removeEventListener("scroll", handleScroll);
|
||||
}, [handleScroll]);
|
||||
|
||||
useEffect(() => {
|
||||
const close = (e: PointerEvent) => {
|
||||
if ((e.target as HTMLElement | null)?.closest("[data-gallery-context-menu]")) return;
|
||||
setContextMenu(null);
|
||||
};
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") setContextMenu(null);
|
||||
};
|
||||
window.addEventListener("pointerdown", close);
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => {
|
||||
window.removeEventListener("pointerdown", close);
|
||||
window.removeEventListener("keydown", onKey);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const scrollToGroup = useCallback(
|
||||
(groupIndex: number) => {
|
||||
const row = groupFirstRowRef.current[groupIndex] ?? 0;
|
||||
@@ -353,7 +338,7 @@ export function Timeline() {
|
||||
) : null}
|
||||
|
||||
{contextMenu ? (
|
||||
<ContextMenu
|
||||
<ImageContextMenu
|
||||
x={contextMenu.x}
|
||||
y={contextMenu.y}
|
||||
image={contextMenu.image}
|
||||
|
||||
Reference in New Issue
Block a user