/** * CSS Grid Reference: http://grid.malven.co/ */ import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui'; import EmptyViewport from './EmptyViewport'; import classNames from 'classnames'; function ViewerViewportGrid(props) { const { servicesManager, viewportComponents, dataSource } = props; const [viewportGrid, viewportGridService] = useViewportGrid(); const { numCols, numRows, activeViewportIndex, viewports, cachedLayout, } = viewportGrid; // TODO -> Need some way of selecting which displaySets hit the viewports. const { DisplaySetService, MeasurementService, HangingProtocolService, } = servicesManager.services; // Using Hanging protocol engine to match the displaysets useEffect(() => { const { unsubscribe } = DisplaySetService.subscribe( DisplaySetService.EVENTS.DISPLAY_SETS_ADDED, eventData => { const { displaySetsAdded } = eventData; const [ matchDetails, hpAlreadyApplied, ] = HangingProtocolService.getState(); if (!matchDetails.length) return; // Match each viewport individually const numViewports = viewportGrid.numRows * viewportGrid.numCols; for (let i = 0; i < numViewports; i++) { if (hpAlreadyApplied[i] === true) { continue; } // if current viewport doesn't have a match if (matchDetails[i] === undefined) return const { SeriesInstanceUID } = matchDetails[i]; const matchingDisplaySet = displaySetsAdded.find(ds => { return ds.SeriesInstanceUID === SeriesInstanceUID; }); if (!matchingDisplaySet) { continue; } viewportGridService.setDisplaysetForViewport({ viewportIndex: i, displaySetInstanceUID: matchingDisplaySet.displaySetInstanceUID, }); HangingProtocolService.setHangingProtocolAppliedForViewport(i); } } ); return () => { unsubscribe(); }; }, [numRows, numCols]); // Layout change based on hanging protocols useEffect(() => { const { unsubscribe } = HangingProtocolService.subscribe( HangingProtocolService.EVENTS.NEW_LAYOUT, ({ numRows, numCols }) => { viewportGridService.setLayout({ numRows, numCols }); } ); return () => { unsubscribe(); }; }, [viewports]); useEffect(() => { const { unsubscribe } = MeasurementService.subscribe( MeasurementService.EVENTS.JUMP_TO_MEASUREMENT, ({ viewportIndex, measurement }) => { const referencedDisplaySetInstanceUID = measurement.displaySetInstanceUID; const viewportsDisplaySetInstanceUIDs = viewports.map( vp => vp.displaySetInstanceUID ); // if we already have the displayset in one of the viewports if ( viewportsDisplaySetInstanceUIDs.indexOf( referencedDisplaySetInstanceUID ) > -1 ) { return; } // If not in any of the viewports, hang it inside the active viewport viewportGridService.setDisplaysetForViewport({ viewportIndex, displaySetInstanceUID: referencedDisplaySetInstanceUID, }); } ); return () => { unsubscribe(); }; }, [viewports]); const onDoubleClick = viewportIndex => { // TODO -> Disabled for now. // onNewImage on a cornerstone viewport is firing setDisplaySetForViewport. // Which it really really shouldn't. We need a larger fix for jump to // measurements and all cornerstone "imageIndex" state to fix this. if (cachedLayout) { viewportGridService.set({ numCols: cachedLayout.numCols, numRows: cachedLayout.numRows, activeViewportIndex: cachedLayout.activeViewportIndex, viewports: cachedLayout.viewports, cachedLayout: null, }); return; } const cachedViewports = viewports.map(viewport => { return { displaySetInstanceUID: viewport.displaySetInstanceUID, }; }); viewportGridService.set({ numCols: 1, numRows: 1, activeViewportIndex: 0, viewports: [ { displaySetInstanceUID: viewports[viewportIndex].displaySetInstanceUID, imageIndex: undefined, }, ], cachedLayout: { numCols, numRows, viewports: cachedViewports, activeViewportIndex: viewportIndex, }, }); }; const onDropHandler = (viewportIndex, { displaySetInstanceUID }) => { viewportGridService.setDisplaysetForViewport({ viewportIndex, displaySetInstanceUID, }); }; const getViewportPanes = () => { const viewportPanes = []; const numViewportPanes = numCols * numRows; for (let i = 0; i < numViewportPanes; i++) { const viewportIndex = i; const isActive = activeViewportIndex === viewportIndex; const paneMetadata = viewports[i] || {}; const { displaySetInstanceUID } = paneMetadata; const displaySet = DisplaySetService.getDisplaySetByUID(displaySetInstanceUID) || {}; const ViewportComponent = _getViewportComponent( displaySet.SOPClassHandlerId, viewportComponents ); const onInterationHandler = event => { if (isActive) return; if (event) { event.preventDefault(); event.stopPropagation(); } viewportGridService.setActiveViewportIndex(viewportIndex); }; // TEMP -> Double click disabled for now // onDoubleClick={() => onDoubleClick(viewportIndex)} viewportPanes[i] = (
); } return viewportPanes; }; if (!numCols || !numCols) { return null; } return ( {/* {ViewportPanes} */} {getViewportPanes()} ); } ViewerViewportGrid.propTypes = { viewportComponents: PropTypes.array.isRequired, }; ViewerViewportGrid.defaultProps = { viewportComponents: [], }; function _getViewportComponent(SOPClassHandlerId, viewportComponents) { if (!SOPClassHandlerId) { return EmptyViewport; } for (let i = 0; i < viewportComponents.length; i++) { if ( viewportComponents[i].displaySetsToDisplay.includes(SOPClassHandlerId) ) { const { component } = viewportComponents[i]; return component; } } } export default ViewerViewportGrid;