feat: extension panels
* Snapshot -- Switching branches * fix classes and hooks for panel componet * Restore button functionality * feat: support for expandableToolMenu * Shift cornerstoneTools config up a layer, and use globalToolSync * Remaining initCornerstoneTools changes * Pull in Segmentation Plugin * Shift MeasurementsPanel to an extension * Note regarding where data is coming from * Make sure measurement callback is available * Bump core version * Shift MeasurementsTable to a local extensions directory * Register appCommands as an extension * Update package dependencies and yarn lock * Support for panel width * Ability to pass props to our panel component * Add a safety check around calling our tacked on method for tool options * Left sidebar plugins + fix activeIndex prop passed to sidebar. * fix: viewer height Now in a shared container w/ top bar, so we need to factor that in when calculating height * lock file latest * Update thumbnails if studies has changes * bump cornerstone version to resolve globalToolSynch history issue * Default panel to open; studies optional * Bump minor version for ohif-cornestone-extension * Simplify button logic * Accommodate odd roundedButtonGroup value change emit * Map viewers + selectedViewport instead of pulling off the window
This commit is contained in:
1 parent
514609708d
commit
5ac9048ef0
36 files changed
+554
-463
No files matched your search
@@ -1,15 +1,18 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
//import OHIF from 'ohif-core';
|
||||
//import { CineDialog } from 'react-viewerbase';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { MODULE_TYPES } from 'ohif-core';
|
||||
import OHIF from 'ohif-core';
|
||||
import moment from 'moment';
|
||||
import WhiteLabellingContext from '../WhiteLabellingContext.js';
|
||||
import ConnectedHeader from './ConnectedHeader.js';
|
||||
import ConnectedFlexboxLayout from './ConnectedFlexboxLayout.js';
|
||||
import ConnectedToolbarRow from './ConnectedToolbarRow.js';
|
||||
import ConnectedLabellingOverlay from './ConnectedLabellingOverlay';
|
||||
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
|
||||
import ConnectedViewerMain from './ConnectedViewerMain.js';
|
||||
import SidePanel from './../components/SidePanel.js';
|
||||
import { extensionManager } from './../App.js';
|
||||
import './Viewer.css';
|
||||
/**
|
||||
* Inits OHIF Hanging Protocol's onReady.
|
||||
@@ -53,6 +56,10 @@ class Viewer extends Component {
|
||||
studyInstanceUids: PropTypes.array,
|
||||
onTimepointsUpdated: PropTypes.func,
|
||||
onMeasurementsUpdated: PropTypes.func,
|
||||
// window.store.getState().viewports.viewportSpecificData
|
||||
viewports: PropTypes.object.isRequired,
|
||||
// window.store.getState().viewports.activeViewportIndex
|
||||
activeViewportIndex: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -75,6 +82,14 @@ class Viewer extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
state = {
|
||||
isLeftSidePanelOpen: true,
|
||||
isRightSidePanelOpen: false,
|
||||
selectedRightSidePanel: '',
|
||||
selectedLeftSidePanel: 'studies', // TODO: Don't hardcode this
|
||||
thumbnails: [],
|
||||
};
|
||||
|
||||
retrieveMeasurements = (patientId, timepointIds) => {
|
||||
OHIF.log.info('retrieveMeasurements');
|
||||
// TODO: Retrieve the measurements from the latest available SR
|
||||
@@ -171,8 +186,13 @@ class Viewer extends Component {
|
||||
|
||||
if (studies) {
|
||||
const patientId = studies[0] && studies[0].patientId;
|
||||
|
||||
timepointApi.retrieveTimepoints({ patientId });
|
||||
measurementApi.retrieveMeasurements(patientId, [currentTimepointId]);
|
||||
|
||||
this.setState({
|
||||
thumbnails: _mapStudiesToThumbnails(studies),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,14 +201,33 @@ class Viewer extends Component {
|
||||
const { studies } = this.props;
|
||||
const patientId = studies[0] && studies[0].patientId;
|
||||
const currentTimepointId = this.currentTimepointId;
|
||||
|
||||
this.timepointApi.retrieveTimepoints({ patientId });
|
||||
this.measurementApi.retrieveMeasurements(patientId, [currentTimepointId]);
|
||||
|
||||
this.setState({
|
||||
thumbnails: _mapStudiesToThumbnails(studies),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let VisiblePanelLeft, VisiblePanelRight;
|
||||
const panelExtensions = extensionManager.modules[MODULE_TYPES.PANEL];
|
||||
|
||||
panelExtensions.forEach(panelExt => {
|
||||
panelExt.module.components.forEach(comp => {
|
||||
if (comp.id === this.state.selectedRightSidePanel) {
|
||||
VisiblePanelRight = comp.component;
|
||||
} else if (comp.id === this.state.selectedLeftSidePanel) {
|
||||
VisiblePanelLeft = comp.component;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* HEADER */}
|
||||
<WhiteLabellingContext.Consumer>
|
||||
{whiteLabelling => (
|
||||
<ConnectedHeader home={false}>
|
||||
@@ -196,16 +235,132 @@ class Viewer extends Component {
|
||||
</ConnectedHeader>
|
||||
)}
|
||||
</WhiteLabellingContext.Consumer>
|
||||
<div id="viewer" className="Viewer">
|
||||
<ConnectedToolbarRow />
|
||||
{/*<ConnectedStudyLoadingMonitor studies={this.props.studies} />*/}
|
||||
{/*<StudyPrefetcher studies={this.props.studies} />*/}
|
||||
<ConnectedFlexboxLayout studies={this.props.studies} />
|
||||
<ConnectedLabellingOverlay />
|
||||
|
||||
{/* TOOLBAR */}
|
||||
<ConnectedToolbarRow
|
||||
isLeftSidePanelOpen={this.state.isLeftSidePanelOpen}
|
||||
isRightSidePanelOpen={this.state.isRightSidePanelOpen}
|
||||
selectedLeftSidePanel={
|
||||
this.state.isLeftSidePanelOpen
|
||||
? this.state.selectedLeftSidePanel
|
||||
: ''
|
||||
}
|
||||
selectedRightSidePanel={
|
||||
this.state.isRightSidePanelOpen
|
||||
? this.state.selectedRightSidePanel
|
||||
: ''
|
||||
}
|
||||
handleSidePanelChange={(side, selectedPanel) => {
|
||||
const sideClicked = side && side[0].toUpperCase() + side.slice(1);
|
||||
const openKey = `is${sideClicked}SidePanelOpen`;
|
||||
const selectedKey = `selected${sideClicked}SidePanel`;
|
||||
const updatedState = Object.assign({}, this.state);
|
||||
|
||||
const isOpen = updatedState[openKey];
|
||||
const prevSelectedPanel = updatedState[selectedKey];
|
||||
// RoundedButtonGroup returns `null` if selected button is clicked
|
||||
const isSameSelectedPanel =
|
||||
prevSelectedPanel === selectedPanel || selectedPanel === null;
|
||||
|
||||
updatedState[selectedKey] = selectedPanel || prevSelectedPanel;
|
||||
|
||||
const isClosedOrShouldClose = !isOpen || isSameSelectedPanel;
|
||||
if (isClosedOrShouldClose) {
|
||||
updatedState[openKey] = !updatedState[openKey];
|
||||
}
|
||||
|
||||
this.setState(updatedState);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/*<ConnectedStudyLoadingMonitor studies={this.props.studies} />*/}
|
||||
{/*<StudyPrefetcher studies={this.props.studies} />*/}
|
||||
|
||||
{/* VIEWPORTS + SIDEPANELS */}
|
||||
<div className="FlexboxLayout">
|
||||
{/* LEFT */}
|
||||
<SidePanel from="left" isOpen={this.state.isLeftSidePanelOpen}>
|
||||
{VisiblePanelLeft ? (
|
||||
<VisiblePanelLeft
|
||||
viewports={this.props.viewports}
|
||||
activeIndex={this.props.activeViewportIndex}
|
||||
/>
|
||||
) : (
|
||||
<ConnectedStudyBrowser studies={this.state.thumbnails} />
|
||||
)}
|
||||
</SidePanel>
|
||||
|
||||
{/* MAIN */}
|
||||
<div className={classNames('main-content')}>
|
||||
<ConnectedViewerMain studies={this.props.studies} />
|
||||
</div>
|
||||
|
||||
{/* RIGHT */}
|
||||
<SidePanel from="right" isOpen={this.state.isRightSidePanelOpen}>
|
||||
{VisiblePanelRight && (
|
||||
<VisiblePanelRight
|
||||
viewports={this.props.viewports}
|
||||
activeIndex={this.props.activeViewportIndex}
|
||||
/>
|
||||
)}
|
||||
</SidePanel>
|
||||
</div>
|
||||
<ConnectedLabellingOverlay />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Viewer;
|
||||
|
||||
/**
|
||||
* What types are these? Why do we have "mapping" dropped in here instead of in
|
||||
* a mapping layer?
|
||||
*
|
||||
* TODO[react]:
|
||||
* - Add sorting of display sets
|
||||
* - Add useMiddleSeriesInstanceAsThumbnail
|
||||
* - Add showStackLoadingProgressBar option
|
||||
*
|
||||
* @param {Study[]} studies
|
||||
* @param {DisplaySet[]} studies[].displaySets
|
||||
*/
|
||||
const _mapStudiesToThumbnails = function(studies) {
|
||||
return studies.map(study => {
|
||||
const { studyInstanceUid } = study;
|
||||
|
||||
const thumbnails = study.displaySets.map(displaySet => {
|
||||
const {
|
||||
displaySetInstanceUid,
|
||||
seriesDescription,
|
||||
seriesNumber,
|
||||
instanceNumber,
|
||||
numImageFrames,
|
||||
} = displaySet;
|
||||
|
||||
let imageId;
|
||||
let altImageText = ' '; // modality
|
||||
|
||||
if (displaySet.images && displaySet.images.length) {
|
||||
imageId = displaySet.images[0].getImageId();
|
||||
} else {
|
||||
altImageText = 'SR';
|
||||
}
|
||||
|
||||
return {
|
||||
imageId,
|
||||
altImageText,
|
||||
displaySetInstanceUid,
|
||||
seriesDescription,
|
||||
seriesNumber,
|
||||
instanceNumber,
|
||||
numImageFrames,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
studyInstanceUid,
|
||||
thumbnails,
|
||||
};
|
||||
});
|
||||
};
|
||||
Reference in new issue
Block a user