refactor context menu and keyboard navigation handling for improved clarity and maintainability
This commit is contained in:
+134
-129
@@ -1174,17 +1174,17 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const el = appRootRef.current;
|
const el = appRootRef.current;
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
const handleContextMenu = (e: MouseEvent) => {
|
const handleContextMenu = (e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
};
|
};
|
||||||
el.addEventListener("contextmenu", handleContextMenu);
|
el.addEventListener("contextmenu", handleContextMenu);
|
||||||
return () => {
|
return () => {
|
||||||
el.removeEventListener("contextmenu", handleContextMenu);
|
el.removeEventListener("contextmenu", handleContextMenu);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!referenceLinesEnabled) return;
|
if (!referenceLinesEnabled) return;
|
||||||
@@ -1926,6 +1926,8 @@ useEffect(() => {
|
|||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const el = appRootRef.current;
|
||||||
|
if (!el) return;
|
||||||
function handleArrowKeyNavigation(e: KeyboardEvent) {
|
function handleArrowKeyNavigation(e: KeyboardEvent) {
|
||||||
console.log("Handling arrow key navigation", e.key);
|
console.log("Handling arrow key navigation", e.key);
|
||||||
console.log("activeViewport", activeViewport);
|
console.log("activeViewport", activeViewport);
|
||||||
@@ -1975,8 +1977,11 @@ useEffect(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("keydown", handleArrowKeyNavigation);
|
el.addEventListener("keydown", handleArrowKeyNavigation);
|
||||||
return () => window.removeEventListener("keydown", handleArrowKeyNavigation);
|
|
||||||
|
return () => {
|
||||||
|
el.removeEventListener("keydown", handleArrowKeyNavigation);
|
||||||
|
};
|
||||||
}, [activeViewport, viewportModes, viewportImageIds, updateOverlay]);
|
}, [activeViewport, viewportModes, viewportImageIds, updateOverlay]);
|
||||||
|
|
||||||
|
|
||||||
@@ -2401,98 +2406,98 @@ useEffect(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Preset menu (bottom right of viewport) */}
|
{/* Preset menu (bottom right of viewport) */}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
right: 8,
|
right: 8,
|
||||||
bottom: 8,
|
bottom: 8,
|
||||||
zIndex: 10,
|
zIndex: 10,
|
||||||
pointerEvents: "auto", // <-- ensure pointer events are enabled
|
pointerEvents: "auto", // <-- ensure pointer events are enabled
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="preset-menu"
|
className="preset-menu"
|
||||||
style={{
|
|
||||||
background: "rgba(0,0,0,0.7)",
|
|
||||||
color: "#fff",
|
|
||||||
borderRadius: "4px",
|
|
||||||
fontSize: "14px",
|
|
||||||
minWidth: "120px",
|
|
||||||
overflow: "hidden",
|
|
||||||
cursor: "pointer",
|
|
||||||
position: "relative",
|
|
||||||
boxShadow: presetsOpenArr[i] ? "0 2px 8px rgba(0,0,0,0.3)" : undefined,
|
|
||||||
pointerEvents: "auto", // <-- ensure pointer events are enabled
|
|
||||||
}}
|
|
||||||
onClick={e => e.stopPropagation()} // <-- prevent bubbling to viewport
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
padding: "6px 12px",
|
|
||||||
fontWeight: "bold",
|
|
||||||
userSelect: "none",
|
|
||||||
}}
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation(); // <-- prevent bubbling to viewport
|
|
||||||
setPresetsOpenArr(prev => {
|
|
||||||
const next = [...prev];
|
|
||||||
next[i] = !next[i];
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Presets
|
|
||||||
<span style={{ float: "right", fontWeight: "normal" }}>
|
|
||||||
{presetsOpenArr[i] ? "▲" : "▼"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{presetsOpenArr[i] && (
|
|
||||||
<div
|
|
||||||
className="preset-menu-content"
|
|
||||||
style={{
|
|
||||||
background: "rgba(0,0,0,0.95)",
|
|
||||||
borderRadius: "0 0 4px 4px",
|
|
||||||
padding: "6px 0 6px 0",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
zIndex: 11,
|
|
||||||
pointerEvents: "auto", // <-- ensure pointer events are enabled
|
|
||||||
}}
|
|
||||||
onClick={e => e.stopPropagation()} // <-- prevent bubbling to viewport
|
|
||||||
>
|
|
||||||
{WINDOW_LEVEL_PRESETS.map((preset) => (
|
|
||||||
<button
|
|
||||||
key={preset.name}
|
|
||||||
style={{
|
style={{
|
||||||
display: "block",
|
background: "rgba(0,0,0,0.7)",
|
||||||
width: "100%",
|
|
||||||
margin: "2px 0",
|
|
||||||
background: "#333",
|
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
border: "none",
|
borderRadius: "4px",
|
||||||
borderRadius: "3px",
|
fontSize: "14px",
|
||||||
|
minWidth: "120px",
|
||||||
|
overflow: "hidden",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
padding: "4px 0",
|
position: "relative",
|
||||||
fontSize: "13px",
|
boxShadow: presetsOpenArr[i] ? "0 2px 8px rgba(0,0,0,0.3)" : undefined,
|
||||||
opacity: 0.9,
|
pointerEvents: "auto", // <-- ensure pointer events are enabled
|
||||||
}}
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation(); // <-- prevent bubbling to viewport
|
|
||||||
handleApplyPreset(i, preset.center, preset.width);
|
|
||||||
setPresetsOpenArr(prev => {
|
|
||||||
const next = [...prev];
|
|
||||||
next[i] = false;
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
|
onClick={e => e.stopPropagation()} // <-- prevent bubbling to viewport
|
||||||
>
|
>
|
||||||
{preset.name}
|
<div
|
||||||
</button>
|
style={{
|
||||||
))}
|
padding: "6px 12px",
|
||||||
</div>
|
fontWeight: "bold",
|
||||||
)}
|
userSelect: "none",
|
||||||
</div>
|
}}
|
||||||
</div>
|
onClick={e => {
|
||||||
|
e.stopPropagation(); // <-- prevent bubbling to viewport
|
||||||
|
setPresetsOpenArr(prev => {
|
||||||
|
const next = [...prev];
|
||||||
|
next[i] = !next[i];
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Presets
|
||||||
|
<span style={{ float: "right", fontWeight: "normal" }}>
|
||||||
|
{presetsOpenArr[i] ? "▲" : "▼"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{presetsOpenArr[i] && (
|
||||||
|
<div
|
||||||
|
className="preset-menu-content"
|
||||||
|
style={{
|
||||||
|
background: "rgba(0,0,0,0.95)",
|
||||||
|
borderRadius: "0 0 4px 4px",
|
||||||
|
padding: "6px 0 6px 0",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
zIndex: 11,
|
||||||
|
pointerEvents: "auto", // <-- ensure pointer events are enabled
|
||||||
|
}}
|
||||||
|
onClick={e => e.stopPropagation()} // <-- prevent bubbling to viewport
|
||||||
|
>
|
||||||
|
{WINDOW_LEVEL_PRESETS.map((preset) => (
|
||||||
|
<button
|
||||||
|
key={preset.name}
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
width: "100%",
|
||||||
|
margin: "2px 0",
|
||||||
|
background: "#333",
|
||||||
|
color: "#fff",
|
||||||
|
border: "none",
|
||||||
|
borderRadius: "3px",
|
||||||
|
cursor: "pointer",
|
||||||
|
padding: "4px 0",
|
||||||
|
fontSize: "13px",
|
||||||
|
opacity: 0.9,
|
||||||
|
}}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation(); // <-- prevent bubbling to viewport
|
||||||
|
handleApplyPreset(i, preset.center, preset.width);
|
||||||
|
setPresetsOpenArr(prev => {
|
||||||
|
const next = [...prev];
|
||||||
|
next[i] = false;
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{preset.name}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/* Position bar inside each viewport */}
|
{/* Position bar inside each viewport */}
|
||||||
{viewportModes[i] === "stack" && viewportImageIds[i] && (
|
{viewportModes[i] === "stack" && viewportImageIds[i] && (
|
||||||
<div
|
<div
|
||||||
@@ -2856,18 +2861,18 @@ useEffect(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
// App root
|
// App root
|
||||||
<div
|
<div
|
||||||
ref={appRootRef}
|
ref={appRootRef}
|
||||||
style={{
|
style={{
|
||||||
position: "relative",
|
position: "relative",
|
||||||
inset: 0,
|
inset: 0,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
background: "#222",
|
background: "#222",
|
||||||
padding: "12px",
|
padding: "12px",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
}}>
|
}}>
|
||||||
{/* Loading overlay */}
|
{/* Loading overlay */}
|
||||||
{loadingState && (
|
{loadingState && (
|
||||||
<div
|
<div
|
||||||
@@ -3086,23 +3091,23 @@ useEffect(() => {
|
|||||||
? "Disable Alt Key for Reference Cursors"
|
? "Disable Alt Key for Reference Cursors"
|
||||||
: "Enable Alt Key for Reference Cursors"}
|
: "Enable Alt Key for Reference Cursors"}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
style={{
|
style={{
|
||||||
padding: "10px 18px",
|
padding: "10px 18px",
|
||||||
background: annotationNavEnabled ? "#1976d2" : "#333",
|
background: annotationNavEnabled ? "#1976d2" : "#333",
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
border: "none",
|
border: "none",
|
||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
fontSize: "15px",
|
fontSize: "15px",
|
||||||
marginBottom: "18px",
|
marginBottom: "18px",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
onClick={() => setAnnotationNavEnabled((prev) => !prev)}
|
onClick={() => setAnnotationNavEnabled((prev) => !prev)}
|
||||||
>
|
>
|
||||||
{annotationNavEnabled ? "Hide" : "Show"} Annotation Navigation
|
{annotationNavEnabled ? "Hide" : "Show"} Annotation Navigation
|
||||||
</button>
|
</button>
|
||||||
{/* Add more menu items here if needed */}
|
{/* Add more menu items here if needed */}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user