Implement resizable stack panel with min/max width constraints and update side menu button positioning
This commit is contained in:
@@ -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) */}
|
||||
|
||||
Reference in New Issue
Block a user