WIP DisplaySetService

This commit is contained in:
James A. Petts committed 2020-05-18 17:21:57 +02:00
1 parent 649216fde4
commit 7a8418f64b
29 files changed
+460 -541

No files matched your search

+40 -22
View File
@@ -10,32 +10,48 @@ import { ViewportGrid, ViewportPane } from '@ohif/ui';
function ViewerViewportGrid(props) {
const {
activeViewportIndex,
viewportData,
servicesManager,
viewportComponents,
dataSource,
} = props;
// From ViewportGridService and/or ContextProvider
const [viewportGrid, setViewportGrid] = useState({
numRows: 2,
numCols: 2,
activeViewportIndex: 0,
viewports: [
{
displaySetUid: undefined,
},
{
displaySetUid: undefined,
},
{
displaySetUid: undefined,
},
{
displaySetUid: undefined,
},
],
debugger;
// TODO -> Need some way of selecting which displaySets hit the viewports.
const { DisplaySetService } = servicesManager.services;
// TODO -> Make a HangingProtocolService
const HangingProtocolService = displaySets => {
return {
numRows: 1,
numCols: 1,
activeViewportIndex: 0,
viewports: [
{
displaySetInstanceUid: Object.keys(displaySets)[0][0]
.displaySetInstanceUid,
},
],
};
};
const handleDisplaySetSubscription = useCallback(displaySets => {
setViewportGrid(HangingProtocolService(displaySets));
});
useEffect(() => {
debugger;
const { unsubscribe } = DisplaySetService.subscribe(
DisplaySetService.EVENTS.DISPLAY_SET_ADDED,
handleDisplaySetSubscription
);
return unsubscribe;
}, []);
// From ViewportGridService and/or ContextProvider
const [viewportGrid, setViewportGrid] = useState({});
// viewportData --> displaySets
const getViewportPanes = () =>
@@ -47,8 +63,10 @@ function ViewerViewportGrid(props) {
<ViewportPane
key={viewportIndex}
className="m-1"
onDrop={() => {
/* setDisplaySet for Viewport */
// Pass in as prop?
acceptDropsFor="displayset"
onDrop={droppedItem => {
console.warn('DROPPED ITEM:', droppedItem);
}}
isActive={activeViewportIndex === viewportIndex}
></ViewportPane>
@@ -1,42 +0,0 @@
import ViewportGrid from './ViewportGrid.js';
import { MODULE_TYPES } from '@ohif/core';
import { connect } from 'react-redux';
import { extensionManager } from './../../App.jsx';
import memoize from 'lodash/memoize';
const getAvailableViewportModules = memoize(viewportModules => {
const availableViewportModules = {};
viewportModules.forEach(moduleDefinition => {
availableViewportModules[moduleDefinition.extensionId] =
moduleDefinition.module;
});
return availableViewportModules;
});
const mapStateToProps = state => {
const viewportModules = extensionManager.modules[MODULE_TYPES.VIEWPORT];
const availableViewportModules = getAvailableViewportModules(viewportModules);
// TODO: Use something like state.plugins.defaultPlugin[MODULE_TYPES.VIEWPORT]
let defaultPlugin;
if (viewportModules.length) {
defaultPlugin = viewportModules[0].extensionId;
}
const { numRows, numColumns, layout, activeViewportIndex } = state.viewports;
return {
numRows,
numColumns,
layout,
activeViewportIndex,
// TODO: rename `availableViewportModules`
availablePlugins: availableViewportModules,
// TODO: rename `defaultViewportModule`
defaultPlugin,
};
};
const ConnectedViewportGrid = connect(mapStateToProps, null)(ViewportGrid);
export default ConnectedViewportGrid;
@@ -1,207 +0,0 @@
import './ViewportGrid.css';
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { utils } from '@ohif/core';
import { useSnackbarContext } from '@ohif/ui';
//
import ViewportPane from './ViewportPane.js';
import DefaultViewport from './DefaultViewport.js';
import EmptyViewport from './EmptyViewport.js';
const { loadAndCacheDerivedDisplaySets } = utils;
const ViewportGrid = function(props) {
const {
activeViewportIndex,
availablePlugins,
defaultPlugin: defaultPluginName,
layout,
numRows,
numColumns,
setViewportData,
studies,
viewportData,
children,
isStudyLoaded,
} = props;
const rowSize = 100 / numRows;
const colSize = 100 / numColumns;
// http://grid.malven.co/
if (!viewportData || !viewportData.length) {
return null;
}
const snackbar = useSnackbarContext();
useEffect(() => {
if (isStudyLoaded) {
viewportData.forEach(displaySet => {
const promises = loadAndCacheDerivedDisplaySets(displaySet, studies);
promises.forEach(promise => {
promise.catch(error => {
snackbar.show({
title: 'Error loading derived display set:',
message: error.message,
type: 'error',
autoClose: false,
});
});
});
});
}
}, [studies, viewportData, isStudyLoaded]);
const getViewportPanes = () =>
layout.viewports.map((layout, viewportIndex) => {
const displaySet = viewportData[viewportIndex];
if (!displaySet) {
return null;
}
const data = {
displaySet,
studies,
};
// JAMES TODO:
// Use whichever plugin is currently in use in the panel
// unless nothing is specified. If nothing is specified
// and the display set has a plugin specified, use that.
//
// TODO: Change this logic to:
// - Plugins define how capable they are of displaying a SopClass
// - When updating a panel, ensure that the currently enabled plugin
// in the viewport is capable of rendering this display set. If not
// then use the most capable available plugin
const pluginName =
!layout.plugin && displaySet && displaySet.plugin
? displaySet.plugin
: layout.plugin;
const ViewportComponent = _getViewportComponent(
data, // Why do we pass this as `ViewportData`, when that's not really what it is?
viewportIndex,
children,
availablePlugins,
pluginName,
defaultPluginName
);
return (
<ViewportPane
onDrop={setViewportData}
viewportIndex={viewportIndex} // Needed by `setViewportData`
className={classNames('viewport-container', {
active: activeViewportIndex === viewportIndex,
})}
key={viewportIndex}
>
{ViewportComponent}
</ViewportPane>
);
});
const ViewportPanes = React.useMemo(getViewportPanes, [
layout,
viewportData,
studies,
children,
availablePlugins,
defaultPluginName,
setViewportData,
activeViewportIndex,
]);
return (
<div
data-cy="viewprt-grid"
style={{
display: 'grid',
gridTemplateRows: `repeat(${numRows}, ${rowSize}%)`,
gridTemplateColumns: `repeat(${numColumns}, ${colSize}%)`,
height: '100%',
width: '100%',
}}
>
{ViewportPanes}
</div>
);
};
ViewportGrid.propTypes = {
viewportData: PropTypes.array.isRequired,
supportsDrop: PropTypes.bool.isRequired,
activeViewportIndex: PropTypes.number.isRequired,
layout: PropTypes.object.isRequired,
availablePlugins: PropTypes.object.isRequired,
setViewportData: PropTypes.func.isRequired,
studies: PropTypes.array,
children: PropTypes.node,
defaultPlugin: PropTypes.string,
numRows: PropTypes.number.isRequired,
numColumns: PropTypes.number.isRequired,
};
ViewportGrid.defaultProps = {
viewportData: [],
numRows: 1,
numColumns: 1,
layout: {
viewports: [{}],
},
activeViewportIndex: 0,
supportsDrop: true,
availablePlugins: {
DefaultViewport,
},
defaultPlugin: 'defaultViewportPlugin',
};
/**
*
*
* @param {*} plugin
* @param {*} viewportData
* @param {*} viewportIndex
* @param {*} children
* @returns
*/
function _getViewportComponent(
viewportData,
viewportIndex,
children,
availablePlugins,
pluginName,
defaultPluginName
) {
if (viewportData.displaySet) {
pluginName = pluginName || defaultPluginName;
const ViewportComponent = availablePlugins[pluginName];
if (!ViewportComponent) {
throw new Error(
`No Viewport Component available for name ${pluginName}.
Available plugins: ${JSON.stringify(availablePlugins)}`
);
}
return (
<ViewportComponent
viewportData={viewportData}
viewportIndex={viewportIndex}
children={[children]}
/>
);
}
return <EmptyViewport />;
}
export default ViewportGrid;
@@ -1,52 +0,0 @@
import React from 'react';
import { useDrop } from 'react-dnd';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import './ViewportPane.css';
const ViewportPane = function(props) {
const { children, onDrop, viewportIndex, className: propClassName } = props;
const [{ hovered, highlighted }, drop] = useDrop({
accept: 'thumbnail',
drop: (droppedItem, monitor) => {
const canDrop = monitor.canDrop();
const isOver = monitor.isOver();
if (canDrop && isOver && onDrop) {
const { StudyInstanceUID, displaySetInstanceUID } = droppedItem;
onDrop({ viewportIndex, StudyInstanceUID, displaySetInstanceUID });
}
},
// Monitor, and collect props.
// Returned as values by `useDrop`
collect: monitor => ({
highlighted: monitor.canDrop(),
hovered: monitor.isOver(),
}),
});
return (
<div
className={classNames(
'viewport-drop-target',
{ hovered: hovered },
{ highlighted: highlighted },
propClassName
)}
ref={drop}
data-cy={`viewport-container-${viewportIndex}`}
>
{children}
</div>
);
};
ViewportPane.propTypes = {
children: PropTypes.node.isRequired,
viewportIndex: PropTypes.number.isRequired,
onDrop: PropTypes.func.isRequired,
className: PropTypes.string,
};
export default ViewportPane;