diff --git a/extensions/default/src/ViewerLayout/ResizablePanelsHook.tsx b/extensions/default/src/ViewerLayout/ResizablePanelsHook.tsx index 9b7c37796..10fa5ddb8 100644 --- a/extensions/default/src/ViewerLayout/ResizablePanelsHook.tsx +++ b/extensions/default/src/ViewerLayout/ResizablePanelsHook.tsx @@ -1,22 +1,6 @@ import { useState, useCallback, useLayoutEffect, useRef } from 'react'; import { getPanelElement, getPanelGroupElement } from 'react-resizable-panels'; - -// Id needed to grab the panel group for converting pixels to percentages -const viewerLayoutResizablePanelGroupId = 'viewerLayoutResizablePanelGroup'; -const viewerLayoutResizableLeftPanelId = 'viewerLayoutResizableLeftPanel'; -const viewerLayoutResizableRightPanelId = 'viewerLayoutResizableRightPanel'; - -const sidePanelExpandedDefaultWidth = 280; -const sidePanelExpandedInsideBorderSize = 4; -const sidePanelExpandedDefaultOffsetWidth = - sidePanelExpandedDefaultWidth + sidePanelExpandedInsideBorderSize; -const sidePanelCollapsedInsideBorderSize = 4; -const sidePanelCollapsedOutsideBorderSize = 8; -const sidePanelCollapsedWidth = 25; -const sidePanelCollapsedOffsetWidth = - sidePanelCollapsedWidth + - sidePanelCollapsedInsideBorderSize + - sidePanelCollapsedOutsideBorderSize; +import { panelGroupDefinition } from './constants/panels'; /** * Set the minimum and maximum css style width attributes for the given element. @@ -43,15 +27,15 @@ const useResizablePanels = ( setRightPanelClosed ) => { const [leftPanelExpandedWidth, setLeftPanelExpandedWidth] = useState( - sidePanelExpandedDefaultWidth + panelGroupDefinition.left.initialExpandedWidth ); const [rightPanelExpandedWidth, setRightPanelExpandedWidth] = useState( - sidePanelExpandedDefaultWidth + panelGroupDefinition.right.initialExpandedWidth ); - - // Percentage sizes. - const [resizablePanelCollapsedSize, setResizablePanelCollapsedSize] = useState(0); - const [resizablePanelDefaultSize, setResizablePanelDefaultSize] = useState(0); + const [leftResizablePanelMinimumSize, setLeftResizablePanelMinimumSize] = useState(0); + const [rightResizablePanelMinimumSize, setRightResizablePanelMinimumSize] = useState(0); + const [leftResizeablePanelCollapsedSize, setLeftResizePanelCollapsedSize] = useState(0); + const [rightResizePanelCollapsedSize, setRightResizePanelCollapsedSize] = useState(0); const resizablePanelGroupElemRef = useRef(null); const resizableLeftPanelElemRef = useRef(null); @@ -65,30 +49,33 @@ const useResizablePanels = ( // converting between percentages and pixels in various callbacks. // - Expand those panels that are initially expanded. useLayoutEffect(() => { - const panelGroupElem = getPanelGroupElement(viewerLayoutResizablePanelGroupId); + const panelGroupElem = getPanelGroupElement(panelGroupDefinition.groupId); resizablePanelGroupElemRef.current = panelGroupElem; const { width: panelGroupWidth } = panelGroupElem.getBoundingClientRect(); - const leftPanelElem = getPanelElement(viewerLayoutResizableLeftPanelId); + const leftPanelElem = getPanelElement(panelGroupDefinition.left.panelId); resizableLeftPanelElemRef.current = leftPanelElem; - const rightPanelElem = getPanelElement(viewerLayoutResizableRightPanelId); + const rightPanelElem = getPanelElement(panelGroupDefinition.right.panelId); resizableRightPanelElemRef.current = rightPanelElem; - const resizablePanelExpandedSize = - (sidePanelExpandedDefaultOffsetWidth / panelGroupWidth) * 100; - // Since both resizable panels are collapsed by default (i.e. their default size is zero), // on the very first render check if either/both side panels should be expanded. + // we use the initialExpandedOffsetWidth on the first render incase the panel has min width but we want the initial state to be larger than that + if (!leftPanelClosed) { - resizableLeftPanelAPIRef?.current?.expand(resizablePanelExpandedSize); - setMinMaxWidth(leftPanelElem, sidePanelExpandedDefaultOffsetWidth); + const leftResizablePanelExpandedSize = + (panelGroupDefinition.left.initialExpandedOffsetWidth / panelGroupWidth) * 100; + resizableLeftPanelAPIRef?.current?.expand(leftResizablePanelExpandedSize); + setMinMaxWidth(leftPanelElem, panelGroupDefinition.left.initialExpandedOffsetWidth); } if (!rightPanelClosed) { - resizableRightPanelAPIRef?.current?.expand(resizablePanelExpandedSize); - setMinMaxWidth(rightPanelElem, sidePanelExpandedDefaultOffsetWidth); + const rightResizablePanelExpandedSize = + (panelGroupDefinition.right.initialExpandedOffsetWidth / panelGroupWidth) * 100; + resizableRightPanelAPIRef?.current?.expand(rightResizablePanelExpandedSize); + setMinMaxWidth(rightPanelElem, panelGroupDefinition.right.initialExpandedOffsetWidth); } }, []); // no dependencies because this useLayoutEffect is only needed on the very first render @@ -114,13 +101,17 @@ const useResizablePanels = ( // for a panel. if (!resizableLeftPanelAPIRef.current.isCollapsed()) { const leftSize = - ((leftPanelExpandedWidth + sidePanelExpandedInsideBorderSize) / panelGroupWidth) * 100; + ((leftPanelExpandedWidth + panelGroupDefinition.shared.expandedInsideBorderSize) / + panelGroupWidth) * + 100; resizableLeftPanelAPIRef.current.resize(leftSize); } if (!resizableRightPanelAPIRef.current.isCollapsed()) { const rightSize = - ((rightPanelExpandedWidth + sidePanelExpandedInsideBorderSize) / panelGroupWidth) * 100; + ((rightPanelExpandedWidth + panelGroupDefinition.shared.expandedInsideBorderSize) / + panelGroupWidth) * + 100; resizableRightPanelAPIRef.current.resize(rightSize); } @@ -128,11 +119,20 @@ const useResizablePanels = ( // component is resized. This typically occurs when the browser window resizes. const observer = new ResizeObserver(() => { const { width: panelGroupWidth } = resizablePanelGroupElemRef.current.getBoundingClientRect(); - const defaultSize = (sidePanelExpandedDefaultOffsetWidth / panelGroupWidth) * 100; + const minimumLeftSize = + (panelGroupDefinition.left.minimumExpandedOffsetWidth / panelGroupWidth) * 100; + const minimumRightSize = + (panelGroupDefinition.right.minimumExpandedOffsetWidth / panelGroupWidth) * 100; // Set the new default and collapsed resizable panel sizes. - setResizablePanelDefaultSize(Math.min(50, defaultSize)); - setResizablePanelCollapsedSize((sidePanelCollapsedOffsetWidth / panelGroupWidth) * 100); + setLeftResizablePanelMinimumSize(Math.min(50, minimumLeftSize)); + setRightResizablePanelMinimumSize(Math.min(50, minimumRightSize)); + setLeftResizePanelCollapsedSize( + (panelGroupDefinition.left.collapsedOffsetWidth / panelGroupWidth) * 100 + ); + setRightResizePanelCollapsedSize( + (panelGroupDefinition.right.collapsedOffsetWidth / panelGroupWidth) * 100 + ); if ( resizableLeftPanelAPIRef.current.isCollapsed() && @@ -146,12 +146,12 @@ const useResizablePanels = ( // Determine the current widths of the two side panels. let leftPanelOffsetWidth = resizableLeftPanelAPIRef.current.isCollapsed() - ? sidePanelCollapsedOffsetWidth - : leftPanelExpandedWidth + sidePanelExpandedInsideBorderSize; + ? panelGroupDefinition.left.collapsedOffsetWidth + : leftPanelExpandedWidth + panelGroupDefinition.shared.expandedInsideBorderSize; let rightPanelOffsetWidth = resizableRightPanelAPIRef.current.isCollapsed() - ? sidePanelCollapsedOffsetWidth - : rightPanelExpandedWidth + sidePanelExpandedInsideBorderSize; + ? panelGroupDefinition.right.collapsedOffsetWidth + : rightPanelExpandedWidth + panelGroupDefinition.shared.expandedInsideBorderSize; if ( !resizableLeftPanelAPIRef.current.isCollapsed() && @@ -162,9 +162,11 @@ const useResizablePanels = ( // Reduce the left panel width so that both panels might fit. leftPanelOffsetWidth = Math.max( panelGroupWidth - rightPanelOffsetWidth, - sidePanelExpandedDefaultOffsetWidth + panelGroupDefinition.left.minimumExpandedOffsetWidth + ); + setLeftPanelExpandedWidth( + leftPanelOffsetWidth - panelGroupDefinition.shared.expandedInsideBorderSize ); - setLeftPanelExpandedWidth(leftPanelOffsetWidth - sidePanelExpandedInsideBorderSize); setMinMaxWidth(resizableLeftPanelElemRef.current, leftPanelOffsetWidth); } @@ -177,9 +179,11 @@ const useResizablePanels = ( // Reduce the right panel width so that both panels might fit. rightPanelOffsetWidth = Math.max( panelGroupWidth - leftPanelOffsetWidth, - sidePanelExpandedDefaultOffsetWidth + panelGroupDefinition.right.minimumExpandedOffsetWidth + ); + setRightPanelExpandedWidth( + rightPanelOffsetWidth - panelGroupDefinition.shared.expandedInsideBorderSize ); - setRightPanelExpandedWidth(rightPanelOffsetWidth - sidePanelExpandedInsideBorderSize); setMinMaxWidth(resizableRightPanelElemRef.current, rightPanelOffsetWidth); } }); @@ -189,7 +193,12 @@ const useResizablePanels = ( return () => { observer.disconnect(); }; - }, [leftPanelExpandedWidth, resizablePanelDefaultSize, rightPanelExpandedWidth]); + }, [ + leftPanelExpandedWidth, + rightPanelExpandedWidth, + leftResizablePanelMinimumSize, + rightResizablePanelMinimumSize, + ]); /** * Handles dragging of either side panel resize handle. @@ -207,14 +216,14 @@ const useResizablePanels = ( if (resizableLeftPanelAPIRef?.current?.isExpanded()) { setMinMaxWidth( resizableLeftPanelElemRef.current, - leftPanelExpandedWidth + sidePanelExpandedInsideBorderSize + leftPanelExpandedWidth + panelGroupDefinition.shared.expandedInsideBorderSize ); } if (resizableRightPanelAPIRef?.current?.isExpanded()) { setMinMaxWidth( resizableRightPanelElemRef.current, - rightPanelExpandedWidth + sidePanelExpandedInsideBorderSize + rightPanelExpandedWidth + panelGroupDefinition.shared.expandedInsideBorderSize ); } } @@ -229,15 +238,12 @@ const useResizablePanels = ( }, [setLeftPanelClosed]); const onLeftPanelOpen = useCallback(() => { - resizableLeftPanelAPIRef?.current?.expand(); - if (!isResizableHandleDraggingRef.current) { - setMinMaxWidth( - resizableLeftPanelElemRef.current, - leftPanelExpandedWidth + sidePanelExpandedInsideBorderSize - ); - } + const { width: panelGroupWidth } = resizablePanelGroupElemRef.current.getBoundingClientRect(); + resizableLeftPanelAPIRef?.current?.expand( + (panelGroupDefinition.left.initialExpandedOffsetWidth / panelGroupWidth) * 100 + ); setLeftPanelClosed(false); - }, [leftPanelExpandedWidth, setLeftPanelClosed]); + }, [setLeftPanelClosed]); const onLeftPanelResize = useCallback(size => { if (resizableLeftPanelAPIRef.current.isCollapsed()) { @@ -245,7 +251,10 @@ const useResizablePanels = ( } const { width: panelGroupWidth } = resizablePanelGroupElemRef.current.getBoundingClientRect(); - setLeftPanelExpandedWidth((size / 100) * panelGroupWidth - sidePanelExpandedInsideBorderSize); + const newExpandedWidth = + (size / 100) * panelGroupWidth - panelGroupDefinition.shared.expandedInsideBorderSize; + + setLeftPanelExpandedWidth(newExpandedWidth); }, []); const onRightPanelClose = useCallback(() => { @@ -255,68 +264,69 @@ const useResizablePanels = ( }, [setRightPanelClosed]); const onRightPanelOpen = useCallback(() => { - resizableRightPanelAPIRef?.current?.expand(); - if (!isResizableHandleDraggingRef.current) { - setMinMaxWidth( - resizableRightPanelElemRef.current, - rightPanelExpandedWidth + sidePanelExpandedInsideBorderSize - ); - } + const { width: panelGroupWidth } = resizablePanelGroupElemRef.current.getBoundingClientRect(); + resizableRightPanelAPIRef?.current?.expand( + (panelGroupDefinition.right.initialExpandedOffsetWidth / panelGroupWidth) * 100 + ); setRightPanelClosed(false); - }, [rightPanelExpandedWidth, setRightPanelClosed]); + }, [setRightPanelClosed]); const onRightPanelResize = useCallback(size => { if (resizableRightPanelAPIRef?.current?.isCollapsed()) { return; } + const { width: panelGroupWidth } = resizablePanelGroupElemRef.current.getBoundingClientRect(); - setRightPanelExpandedWidth((size / 100) * panelGroupWidth - sidePanelExpandedInsideBorderSize); + const newExpandedWidth = + (size / 100) * panelGroupWidth - panelGroupDefinition.shared.expandedInsideBorderSize; + + setRightPanelExpandedWidth(newExpandedWidth); }, []); return [ { expandedWidth: leftPanelExpandedWidth, - collapsedWidth: sidePanelCollapsedWidth, - collapsedInsideBorderSize: sidePanelCollapsedInsideBorderSize, - collapsedOutsideBorderSize: sidePanelCollapsedOutsideBorderSize, - expandedInsideBorderSize: sidePanelExpandedInsideBorderSize, + collapsedWidth: panelGroupDefinition.shared.collapsedWidth, + collapsedInsideBorderSize: panelGroupDefinition.shared.collapsedInsideBorderSize, + collapsedOutsideBorderSize: panelGroupDefinition.shared.collapsedOutsideBorderSize, + expandedInsideBorderSize: panelGroupDefinition.shared.expandedInsideBorderSize, onClose: onLeftPanelClose, onOpen: onLeftPanelOpen, }, { expandedWidth: rightPanelExpandedWidth, - collapsedWidth: sidePanelCollapsedWidth, - collapsedInsideBorderSize: sidePanelCollapsedInsideBorderSize, - collapsedOutsideBorderSize: sidePanelCollapsedOutsideBorderSize, - expandedInsideBorderSize: sidePanelExpandedInsideBorderSize, + collapsedWidth: panelGroupDefinition.shared.collapsedWidth, + collapsedInsideBorderSize: panelGroupDefinition.shared.collapsedInsideBorderSize, + collapsedOutsideBorderSize: panelGroupDefinition.shared.collapsedOutsideBorderSize, + expandedInsideBorderSize: panelGroupDefinition.shared.expandedInsideBorderSize, onClose: onRightPanelClose, onOpen: onRightPanelOpen, }, - { direction: 'horizontal', id: viewerLayoutResizablePanelGroupId }, + { direction: 'horizontal', id: panelGroupDefinition.groupId }, { - defaultSize: resizablePanelDefaultSize, - minSize: resizablePanelDefaultSize, + defaultSize: leftResizablePanelMinimumSize, + minSize: leftResizablePanelMinimumSize, onResize: onLeftPanelResize, collapsible: true, - collapsedSize: resizablePanelCollapsedSize, + collapsedSize: leftResizeablePanelCollapsedSize, onCollapse: () => setLeftPanelClosed(true), onExpand: () => setLeftPanelClosed(false), ref: resizableLeftPanelAPIRef, order: 0, - id: viewerLayoutResizableLeftPanelId, + id: panelGroupDefinition.left.panelId, }, { order: 1, id: 'viewerLayoutResizableViewportGridPanel' }, { - defaultSize: resizablePanelDefaultSize, - minSize: resizablePanelDefaultSize, + defaultSize: rightResizablePanelMinimumSize, + minSize: rightResizablePanelMinimumSize, onResize: onRightPanelResize, collapsible: true, - collapsedSize: resizablePanelCollapsedSize, + collapsedSize: rightResizePanelCollapsedSize, onCollapse: () => setRightPanelClosed(true), onExpand: () => setRightPanelClosed(false), ref: resizableRightPanelAPIRef, order: 2, - id: viewerLayoutResizableRightPanelId, + id: panelGroupDefinition.right.panelId, }, onHandleDragging, ]; diff --git a/extensions/default/src/ViewerLayout/constants/panels.ts b/extensions/default/src/ViewerLayout/constants/panels.ts new file mode 100644 index 000000000..f85af3ed6 --- /dev/null +++ b/extensions/default/src/ViewerLayout/constants/panels.ts @@ -0,0 +1,38 @@ +const expandedInsideBorderSize = 4; +const collapsedInsideBorderSize = 4; +const collapsedOutsideBorderSize = 8; +const collapsedWidth = 25; + +const rightPanelInitialExpandedWidth = 280; +const leftPanelInitialExpandedWidth = 282; + +const panelGroupDefinition = { + groupId: 'viewerLayoutResizablePanelGroup', + shared: { + expandedInsideBorderSize, + collapsedInsideBorderSize, + collapsedOutsideBorderSize, + collapsedWidth, + }, + left: { + // id + panelId: 'viewerLayoutResizableLeftPanel', + // expanded width + initialExpandedWidth: leftPanelInitialExpandedWidth, + // expanded width + expanded inside border + minimumExpandedOffsetWidth: 145 + expandedInsideBorderSize, + // initial expanded width + initialExpandedOffsetWidth: leftPanelInitialExpandedWidth + expandedInsideBorderSize, + // collapsed width + collapsed inside border + collapsed outside border + collapsedOffsetWidth: collapsedWidth + collapsedInsideBorderSize + collapsedOutsideBorderSize, + }, + right: { + panelId: 'viewerLayoutResizableRightPanel', + initialExpandedWidth: rightPanelInitialExpandedWidth, + minimumExpandedOffsetWidth: rightPanelInitialExpandedWidth + expandedInsideBorderSize, + initialExpandedOffsetWidth: rightPanelInitialExpandedWidth + expandedInsideBorderSize, + collapsedOffsetWidth: collapsedWidth + collapsedInsideBorderSize + collapsedOutsideBorderSize, + }, +}; + +export { panelGroupDefinition }; diff --git a/modes/basic-dev-mode/src/index.ts b/modes/basic-dev-mode/src/index.ts index 47cdb6fd9..661a62ad2 100644 --- a/modes/basic-dev-mode/src/index.ts +++ b/modes/basic-dev-mode/src/index.ts @@ -141,7 +141,9 @@ function modeFactory({ modeConfiguration }) { props: { // TODO: Should be optional, or required to pass empty array for slots? leftPanels: [ohif.thumbnailList], + leftPanelResizable: true, rightPanels: [ohif.measurements], + rightPanelResizable: true, viewports: [ { namespace: cs3d.viewport, diff --git a/modes/basic-test-mode/src/index.ts b/modes/basic-test-mode/src/index.ts index 41d9d6341..b37a0bd3b 100644 --- a/modes/basic-test-mode/src/index.ts +++ b/modes/basic-test-mode/src/index.ts @@ -150,8 +150,10 @@ function modeFactory() { // leftPanels: [ohif.thumbnailList], // rightPanels: [dicomSeg.panel, ohif.measurements], leftPanels: [tracked.thumbnailList], + leftPanelResizable: true, // Can use cornerstone.measurements for all measurements rightPanels: [cornerstone.panel, tracked.measurements, cornerstone.measurements], + rightPanelResizable: true, // rightPanelClosed: true, // optional prop to start with collapse panels viewports: [ { diff --git a/modes/microscopy/src/index.tsx b/modes/microscopy/src/index.tsx index 7435dea96..3ca7e4b65 100644 --- a/modes/microscopy/src/index.tsx +++ b/modes/microscopy/src/index.tsx @@ -87,9 +87,11 @@ function modeFactory({ modeConfiguration }) { id: ohif.layout, props: { leftPanels: [ohif.leftPanel], + leftPanelResizable: true, leftPanelClosed: true, // we have problem with rendering thumbnails for microscopy images rightPanelClosed: true, // we do not have the save microscopy measurements yet rightPanels: ['@ohif/extension-dicom-microscopy.panelModule.measure'], + rightPanelResizable: true, viewports: [ { namespace: '@ohif/extension-dicom-microscopy.viewportModule.microscopy-dicom', diff --git a/modes/preclinical-4d/src/index.tsx b/modes/preclinical-4d/src/index.tsx index 21fc1baed..a7ad7a7ae 100644 --- a/modes/preclinical-4d/src/index.tsx +++ b/modes/preclinical-4d/src/index.tsx @@ -158,7 +158,9 @@ function modeFactory({ modeConfiguration }) { id: ohif.layout, props: { leftPanels: [[dynamicVolume.leftPanel, cornerstone.activeViewportWindowLevel]], + leftPanelResizable: true, rightPanels: [], + rightPanelResizable: true, rightPanelClosed: true, viewports: [ { diff --git a/modes/segmentation/src/index.tsx b/modes/segmentation/src/index.tsx index 34e978618..7c51c194c 100644 --- a/modes/segmentation/src/index.tsx +++ b/modes/segmentation/src/index.tsx @@ -132,7 +132,9 @@ function modeFactory({ modeConfiguration }) { id: ohif.layout, props: { leftPanels: [ohif.leftPanel], + leftPanelResizable: true, rightPanels: [cornerstone.panelTool], + rightPanelResizable: true, // leftPanelClosed: true, viewports: [ { diff --git a/modes/tmtv/src/index.ts b/modes/tmtv/src/index.ts index 9c1ba1702..e1dbfffcb 100644 --- a/modes/tmtv/src/index.ts +++ b/modes/tmtv/src/index.ts @@ -215,8 +215,10 @@ function modeFactory({ modeConfiguration }) { id: ohif.layout, props: { leftPanels: [ohif.thumbnailList], + leftPanelResizable: true, leftPanelClosed: true, rightPanels: [tmtv.tmtv, tmtv.petSUV], + rightPanelResizable: true, viewports: [ { namespace: cs3d.viewport, diff --git a/platform/app/cypress/integration/OHIFVideoDisplay.spec.js b/platform/app/cypress/integration/OHIFVideoDisplay.spec.js index ff877034a..704db2ed4 100644 --- a/platform/app/cypress/integration/OHIFVideoDisplay.spec.js +++ b/platform/app/cypress/integration/OHIFVideoDisplay.spec.js @@ -1,5 +1,6 @@ describe('OHIF Video Display', function () { beforeEach(function () { + Cypress.on('uncaught:exception', () => false); cy.openStudyInViewer('2.25.96975534054447904995905761963464388233'); }); diff --git a/platform/ui-next/src/components/StudyBrowser/StudyBrowser.tsx b/platform/ui-next/src/components/StudyBrowser/StudyBrowser.tsx index 33e68f680..41de81eca 100644 --- a/platform/ui-next/src/components/StudyBrowser/StudyBrowser.tsx +++ b/platform/ui-next/src/components/StudyBrowser/StudyBrowser.tsx @@ -63,7 +63,7 @@ const StudyBrowser = ({ className="ohif-scrollbar invisible-scrollbar bg-bkg-low flex flex-1 flex-col gap-[4px] overflow-auto" data-cy={'studyBrowser-panel'} > -