Implement resizable stack panel with min/max width constraints and update side menu button positioning

This commit is contained in:
Ross
2026-04-27 12:02:54 +01:00
parent d34772a3fa
commit e29bce42e2
+80 -5
View File
@@ -187,6 +187,19 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
// Right-side stack panel open state (toggled by button)
const [stackPanelOpen, setStackPanelOpen] = useState(false);
const [showOpenButtonHover, setShowOpenButtonHover] = useState(false);
const [stackPanelWidth, setStackPanelWidth] = useState(320);
const stackPanelResizeRef = useRef<{ resizing: boolean; startX: number; startWidth: number }>({
resizing: false,
startX: 0,
startWidth: 320,
});
const STACK_PANEL_MIN_WIDTH = 240;
const STACK_PANEL_MAX_WIDTH = 720;
const SIDE_MENU_WIDTH = 220;
const SIDE_MENU_BUTTON_SIZE = 40;
const SIDE_MENU_BUTTON_PEEK = 14;
const [fullscreenHoverIdx, setFullscreenHoverIdx] = useState<number | null>(null);
const [openDropdownIdx, setOpenDropdownIdx] = useState<number | null>(null);
@@ -1296,6 +1309,52 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
const [sideMenuOpen, setSideMenuOpen] = useState(false);
const [folderLoadMode, setFolderLoadMode] = useState<'add' | 'replace'>('add');
const folderInputRef = useRef<HTMLInputElement | null>(null);
const beginStackPanelResize = useCallback((event: React.MouseEvent<HTMLDivElement>) => {
if (!stackPanelOpen) return;
event.preventDefault();
event.stopPropagation();
stackPanelResizeRef.current = {
resizing: true,
startX: event.clientX,
startWidth: stackPanelWidth,
};
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
}, [stackPanelOpen, stackPanelWidth]);
useEffect(() => {
const onMouseMove = (event: MouseEvent) => {
if (!stackPanelResizeRef.current.resizing) return;
const delta = stackPanelResizeRef.current.startX - event.clientX;
const nextWidth = Math.max(
STACK_PANEL_MIN_WIDTH,
Math.min(STACK_PANEL_MAX_WIDTH, stackPanelResizeRef.current.startWidth + delta)
);
setStackPanelWidth(nextWidth);
};
const onMouseUp = () => {
if (!stackPanelResizeRef.current.resizing) return;
stackPanelResizeRef.current.resizing = false;
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
return () => {
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', onMouseUp);
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
}, []);
const sideMenuButtonLeft = sideMenuOpen
? SIDE_MENU_WIDTH - (SIDE_MENU_BUTTON_SIZE / 2)
: -SIDE_MENU_BUTTON_SIZE + SIDE_MENU_BUTTON_PEEK;
useEffect(() => {
// Some browsers don't accept non-standard props via JSX typings; set attributes directly.
try {
@@ -3947,10 +4006,10 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
position: "absolute",
top: "50%",
transform: "translateY(-50%)",
left: sideMenuOpen ? 250 : -20, // Slide right when open
left: sideMenuButtonLeft,
zIndex: 200,
width: 40,
height: 40,
width: SIDE_MENU_BUTTON_SIZE,
height: SIDE_MENU_BUTTON_SIZE,
background: "#222",
color: "#fff",
border: "none",
@@ -3974,7 +4033,7 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
position: "absolute",
top: 0,
left: 0,
width: 220,
width: SIDE_MENU_WIDTH,
height: "100%",
background: "#222",
color: "#fff",
@@ -4364,7 +4423,23 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
</div>
{/* Right-side panel occupies layout space when visible; when closed width is 0 and it doesn't catch pointer events */}
<div style={{ width: stackPanelOpen ? 320 : 0, transition: 'width 220ms ease', display: 'flex', flexDirection: 'column', pointerEvents: stackPanelOpen ? 'auto' : 'none', overflow: 'hidden' }}>
<div style={{ width: stackPanelOpen ? stackPanelWidth : 0, transition: 'width 220ms ease', display: 'flex', flexDirection: 'column', pointerEvents: stackPanelOpen ? 'auto' : 'none', overflow: 'hidden', position: 'relative' }}>
{stackPanelOpen && (
<div
onMouseDown={beginStackPanelResize}
title="Resize stacks panel"
style={{
position: 'absolute',
left: 0,
top: 0,
width: 8,
height: '100%',
cursor: 'col-resize',
zIndex: 2800,
background: 'linear-gradient(to right, rgba(100,181,246,0.25), rgba(100,181,246,0))',
}}
/>
)}
{/* Reuse the existing right-side panel markup but ensure pointerEvents are enabled inside */}
<div style={{ pointerEvents: 'auto', width: '100%', height: '100%' }}>
{/* Panel (same content as before) */}