diff --git a/docs/latest/deployment/recipes/static-assets.md b/docs/latest/deployment/recipes/static-assets.md
index 01ea90eaa..4e5712f78 100644
--- a/docs/latest/deployment/recipes/static-assets.md
+++ b/docs/latest/deployment/recipes/static-assets.md
@@ -127,7 +127,7 @@ through the trouble of using AWS/GCP/Azure, it's more likely you're doing so to
avoid using a proxy or to simplify authentication.
If that is the case, check out some of our more advanced `docker` deployments
-that target these providers from the left-hand sidebar.
+that target these providers from the left-hand sidepanel.
These guides can be a bit longer and a update more frequently. To provide
accurate documentation, we will link to each provider's own recommended steps:
diff --git a/extensions/_ohif-example-extension/src/index.js b/extensions/_ohif-example-extension/src/index.js
index 2d4e65b30..0ed2644f8 100644
--- a/extensions/_ohif-example-extension/src/index.js
+++ b/extensions/_ohif-example-extension/src/index.js
@@ -7,12 +7,28 @@ export default {
*/
id: 'example-extension',
- getViewportModule() {},
+ /**
+ * LIFECYCLE HOOKS
+ */
+
+ preRegistration(extensionConfiguration) {},
+
+ /**
+ * MODULE GETTERS
+ */
+
+ getViewportModule() {
+ return '... react component ...';
+ },
getSopClassHandlerModule() {
return sopClassHandlerModule;
},
- getPanelModule() {},
- getToolbarModule() {},
+ getPanelModule() {
+ return panelModule;
+ },
+ getToolbarModule() {
+ return panelModule;
+ },
getCommandsModule(/* store */) {
return commandsModule;
},
@@ -67,3 +83,23 @@ const sopClassHandlerModule = {
};
},
};
+
+/**
+ *
+ */
+const panelModule = {
+ menuOptions: [
+ {
+ icon: 'th-list',
+ label: 'Segments',
+ target: 'segment-panel',
+ },
+ ],
+ components: [
+ {
+ id: 'segment-panel',
+ component: '... react component ...',
+ },
+ ],
+ defaultContext: ['VIEWER'],
+};
diff --git a/extensions/ohif-cornerstone-extension/package.json b/extensions/ohif-cornerstone-extension/package.json
index 05199b5a3..f47bab1bc 100644
--- a/extensions/ohif-cornerstone-extension/package.json
+++ b/extensions/ohif-cornerstone-extension/package.json
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
- "version": "0.0.37",
+ "version": "0.0.38",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
diff --git a/extensions/ohif-cornerstone-extension/src/ConnectedCornerstoneViewport.js b/extensions/ohif-cornerstone-extension/src/ConnectedCornerstoneViewport.js
index c92f5e3ee..7f1979998 100644
--- a/extensions/ohif-cornerstone-extension/src/ConnectedCornerstoneViewport.js
+++ b/extensions/ohif-cornerstone-extension/src/ConnectedCornerstoneViewport.js
@@ -12,6 +12,15 @@ const mapStateToProps = (state, ownProps) => {
dataFromStore = state.extensions.cornerstone;
}
+ // TODO: This should be extension configuration
+ // ...dataFromStore -->
+ // availableTools,
+ // onNewImage,
+ // onRightClick,
+ // onTouchPress,
+ // onTouchStart,
+ // onMouseClick,
+
// If this is the active viewport, enable prefetching.
const { viewportIndex } = ownProps; //.viewportData;
const isActive = viewportIndex === state.viewports.activeViewportIndex;
diff --git a/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js b/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js
index 1c1e1d789..3a75bfa0e 100644
--- a/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js
+++ b/extensions/ohif-cornerstone-extension/src/OHIFCornerstoneViewport.js
@@ -1,5 +1,3 @@
-import './config';
-
import React, { Component } from 'react';
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
@@ -20,7 +18,7 @@ cornerstone.metaData.addProvider(
StackManager.setMetadataProvider(metadataProvider);
const SOP_CLASSES = {
- SEGMENTATION_STORAGE: '1.2.840.10008.5.1.4.1.1.66.4'
+ SEGMENTATION_STORAGE: '1.2.840.10008.5.1.4.1.1.66.4',
};
const specialCaseHandlers = {};
@@ -30,11 +28,11 @@ specialCaseHandlers[
class OHIFCornerstoneViewport extends Component {
state = {
- viewportData: null
+ viewportData: null,
};
static defaultProps = {
- customProps: {}
+ customProps: {},
};
static propTypes = {
@@ -42,7 +40,7 @@ class OHIFCornerstoneViewport extends Component {
displaySet: PropTypes.object,
viewportIndex: PropTypes.number,
children: PropTypes.node,
- customProps: PropTypes.object
+ customProps: PropTypes.object,
};
static id = 'OHIFCornerstoneViewport';
@@ -185,7 +183,7 @@ class OHIFCornerstoneViewport extends Component {
viewportData = {
studyInstanceUid,
displaySetInstanceUid,
- stack
+ stack,
};
break;
@@ -201,7 +199,7 @@ class OHIFCornerstoneViewport extends Component {
displaySetInstanceUid,
sopClassUids,
sopInstanceUid,
- frameIndex
+ frameIndex,
} = displaySet;
if (sopClassUids && sopClassUids.length > 1) {
@@ -221,7 +219,7 @@ class OHIFCornerstoneViewport extends Component {
frameIndex
).then(viewportData => {
this.setState({
- viewportData
+ viewportData,
});
});
}
@@ -252,7 +250,7 @@ class OHIFCornerstoneViewport extends Component {
childrenWithProps = this.props.children.map((child, index) => {
return React.cloneElement(child, {
viewportIndex: this.props.viewportIndex,
- key: index
+ key: index,
});
});
}
diff --git a/extensions/ohif-cornerstone-extension/src/config.js b/extensions/ohif-cornerstone-extension/src/config.js
deleted file mode 100644
index b8f75e7bf..000000000
--- a/extensions/ohif-cornerstone-extension/src/config.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import Hammer from 'hammerjs';
-import cornerstone from 'cornerstone-core';
-import cornerstoneMath from 'cornerstone-math';
-import cornerstoneTools from 'cornerstone-tools';
-
-// For debugging
-window.cornerstoneTools = cornerstoneTools;
-
-cornerstoneTools.external.cornerstone = cornerstone;
-cornerstoneTools.external.Hammer = Hammer;
-cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
-cornerstoneTools.init();
-
-// Set the tool font and font size
-// context.font = "[style] [variant] [weight] [size]/[line height] [font family]";
-const fontFamily =
- 'Roboto, OpenSans, HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif';
-cornerstoneTools.textStyle.setFont(`16px ${fontFamily}`);
-
-// Tool styles/colors
-cornerstoneTools.toolStyle.setToolWidth(2);
-cornerstoneTools.toolColors.setToolColor('rgb(255, 255, 0)');
-cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)');
-
-cornerstoneTools.store.state.touchProximity = 40;
diff --git a/img/designs/open-graph.fig b/img/designs/open-graph.fig
index 7b13a4b90..52ebfa497 100644
Binary files a/img/designs/open-graph.fig and b/img/designs/open-graph.fig differ
diff --git a/package.json b/package.json
index b6afc2d75..45e9f3353 100644
--- a/package.json
+++ b/package.json
@@ -76,7 +76,7 @@
},
"dependencies": {
"@babel/runtime": "^7.4.5",
- "@ohif/extension-cornerstone": "0.0.37",
+ "@ohif/extension-cornerstone": "0.0.38",
"@ohif/extension-dicom-html": "0.0.3",
"@ohif/extension-dicom-microscopy": "0.0.8",
"@ohif/extension-dicom-pdf": "0.0.7",
@@ -86,7 +86,7 @@
"classnames": "^2.2.6",
"cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8",
- "cornerstone-tools": "^3.13.0",
+ "cornerstone-tools": "^3.15.1",
"cornerstone-wado-image-loader": "^2.2.3",
"dcmjs": "^0.4.7",
"dicom-parser": "^1.8.3",
diff --git a/src/App.js b/src/App.js
index 53827a218..caf5072c0 100644
--- a/src/App.js
+++ b/src/App.js
@@ -17,24 +17,32 @@ import {
} from './utils/index.js';
import { I18nextProvider } from 'react-i18next';
+import initCornerstoneTools from './initCornerstoneTools.js';
+
+// ~~ EXTENSIONS
+import { GenericViewerCommands, MeasurementsPanel } from './appExtensions';
import OHIFCornerstoneExtension from '@ohif/extension-cornerstone';
import OHIFDicomHtmlExtension from '@ohif/extension-dicom-html';
import OHIFDicomMicroscopyExtension from '@ohif/extension-dicom-microscopy';
import OHIFDicomPDFExtension from '@ohif/extension-dicom-pdf';
import OHIFStandaloneViewer from './OHIFStandaloneViewer';
import OHIFVTKExtension from '@ohif/extension-vtk';
+// ~~ EXTENSIONS
import { OidcProvider } from 'redux-oidc';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import WhiteLabellingContext from './WhiteLabellingContext';
-import appCommands from './appCommands';
import { getActiveContexts } from './store/layout/selectors.js';
import i18n from '@ohif/i18n';
-import setupTools from './setupTools';
+import setupTools from './setupTools.js';
import store from './store';
// ~~~~ APP SETUP
+initCornerstoneTools({
+ globalToolSyncEnabled: true,
+});
+
const commandsManagerConfig = {
getAppState: () => store.getState(),
getActiveContexts: () => getActiveContexts(store.getState()),
@@ -44,22 +52,16 @@ const commandsManager = new CommandsManager(commandsManagerConfig);
const hotkeysManager = new HotkeysManager(commandsManager);
const extensionManager = new ExtensionManager({ commandsManager });
-// TODO: Should be done in extensions w/ commandsModule
-// ~~ ADD COMMANDS
-appCommands.init(commandsManager);
-if (window.config.hotkeys) {
- hotkeysManager.setHotkeys(window.config.hotkeys, true);
-}
-// ~~~~ END APP SETUP
-
+// CornerstoneTools and labeling/measurements?
setupTools(store);
-
-// const children = {
-// viewport: [],
-// };
+// ~~~~ END APP SETUP
/** TODO: extensions should be passed in as prop as soon as we have the extensions as separate packages and then registered by ExtensionsManager */
extensionManager.registerExtensions([
+ // Core
+ GenericViewerCommands,
+ MeasurementsPanel,
+ //
OHIFCornerstoneExtension,
OHIFVTKExtension,
OHIFDicomPDFExtension,
@@ -67,6 +69,11 @@ extensionManager.registerExtensions([
OHIFDicomMicroscopyExtension,
]);
+// Must run after extension commands are registered
+if (window.config.hotkeys) {
+ hotkeysManager.setHotkeys(window.config.hotkeys, true);
+}
+
// TODO[react] Use a provider when the whole tree is React
window.store = store;
diff --git a/src/appCommands/README.md b/src/appCommands/README.md
deleted file mode 100644
index 61c515e7b..000000000
--- a/src/appCommands/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Commands
diff --git a/src/appCommands/index.js b/src/appCommands/index.js
deleted file mode 100644
index 40591d5f1..000000000
--- a/src/appCommands/index.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import viewerCommandDefinitions from './viewer.js';
-
-const CONTEXTS = {
- viewer: 'VIEWER',
-};
-
-/**
- * Register all commands.
- * TODO: Extensions should self-register their commands
- */
-function init(commandsManager) {
- _registerViewerCommands(commandsManager);
-}
-
-/**
- * Register all Viewer commands
- *
- * @private
- */
-function _registerViewerCommands(commandsManager) {
- const commandContext = CONTEXTS.viewer;
-
- commandsManager.createContext(commandContext);
- Object.keys(viewerCommandDefinitions).forEach(commandName => {
- const commandDefinition = viewerCommandDefinitions[commandName];
-
- commandsManager.registerCommand(
- commandContext,
- commandName,
- commandDefinition
- );
- });
-}
-
-export default {
- init,
-};
diff --git a/src/appCommands/viewer.js b/src/appExtensions/GenericViewerCommands/commandsModule.js
similarity index 85%
rename from src/appCommands/viewer.js
rename to src/appExtensions/GenericViewerCommands/commandsModule.js
index fa7c02772..9453c637c 100644
--- a/src/appCommands/viewer.js
+++ b/src/appExtensions/GenericViewerCommands/commandsModule.js
@@ -1,11 +1,11 @@
import { redux } from 'ohif-core';
-import store from './../store';
+import store from './../../store';
const { setViewportActive } = redux.actions;
const actions = {
updateViewportDisplaySet: ({ direction }) => {
// TODO
- console.warn('updateDisplaySet: ', direction);
+ // console.warn('updateDisplaySet: ', direction);
},
updateActiveViewport: ({ viewports, direction }) => {
const { viewportSpecificData, activeViewportIndex } = viewports;
@@ -33,4 +33,7 @@ const definitions = {
},
};
-export default definitions;
+export default {
+ definitions,
+ defaultContext: 'VIEWER',
+};
diff --git a/src/appExtensions/GenericViewerCommands/index.js b/src/appExtensions/GenericViewerCommands/index.js
new file mode 100644
index 000000000..042fa32b1
--- /dev/null
+++ b/src/appExtensions/GenericViewerCommands/index.js
@@ -0,0 +1,8 @@
+import commandsModule from './commandsModule.js';
+
+export default {
+ id: 'generic-viewer-commands',
+ getCommandsModule() {
+ return commandsModule;
+ },
+};
diff --git a/src/connectedComponents/ConnectedMeasurementTable.js b/src/appExtensions/MeasurementsPanel/ConnectedMeasurementTable.js
similarity index 98%
rename from src/connectedComponents/ConnectedMeasurementTable.js
rename to src/appExtensions/MeasurementsPanel/ConnectedMeasurementTable.js
index 9f47aefeb..f443d49ee 100644
--- a/src/connectedComponents/ConnectedMeasurementTable.js
+++ b/src/appExtensions/MeasurementsPanel/ConnectedMeasurementTable.js
@@ -3,8 +3,10 @@ import { MeasurementTable } from 'react-viewerbase';
import OHIF from 'ohif-core';
import moment from 'moment';
import cornerstone from 'cornerstone-core';
-import jumpToRowItem from '../lib/jumpToRowItem.js';
-import getMeasurementLocationCallback from '../lib/getMeasurementLocationCallback';
+
+//
+import jumpToRowItem from './jumpToRowItem.js';
+import getMeasurementLocationCallback from './../../lib/getMeasurementLocationCallback';
const { setViewportSpecificData } = OHIF.redux.actions;
const { MeasurementApi } = OHIF.measurements;
diff --git a/src/appExtensions/MeasurementsPanel/index.js b/src/appExtensions/MeasurementsPanel/index.js
new file mode 100644
index 000000000..dc2357e24
--- /dev/null
+++ b/src/appExtensions/MeasurementsPanel/index.js
@@ -0,0 +1,23 @@
+import ConnectedMeasurementTable from './ConnectedMeasurementTable.js';
+
+export default {
+ id: 'measurements-table',
+ getPanelModule() {
+ return {
+ menuOptions: [
+ {
+ icon: 'list',
+ label: 'Measurements',
+ target: 'measurement-panel',
+ },
+ ],
+ components: [
+ {
+ id: 'measurement-panel',
+ component: ConnectedMeasurementTable,
+ },
+ ],
+ defaultContext: ['VIEWER'],
+ };
+ },
+};
diff --git a/src/lib/jumpToRowItem.js b/src/appExtensions/MeasurementsPanel/jumpToRowItem.js
similarity index 100%
rename from src/lib/jumpToRowItem.js
rename to src/appExtensions/MeasurementsPanel/jumpToRowItem.js
diff --git a/src/appExtensions/index.js b/src/appExtensions/index.js
new file mode 100644
index 000000000..fbc69aab4
--- /dev/null
+++ b/src/appExtensions/index.js
@@ -0,0 +1,4 @@
+import GenericViewerCommands from './GenericViewerCommands/index.js';
+import MeasurementsPanel from './MeasurementsPanel/index.js';
+
+export { GenericViewerCommands, MeasurementsPanel };
diff --git a/src/components/SidePanel.css b/src/components/SidePanel.css
new file mode 100644
index 000000000..45325b0de
--- /dev/null
+++ b/src/components/SidePanel.css
@@ -0,0 +1,40 @@
+.FlexboxLayout {
+ display: flex;
+ flex: 1;
+ flex-flow: row nowrap;
+ align-items: stretch;
+ height: calc(100% - var(--toolbar-height) - var(--top-bar-height));
+ width: 100%;
+ overflow: hidden;
+}
+
+.sidepanel {
+ flex: 1;
+ height: 100%;
+ transition: var(--sidepanel-transition);
+}
+
+.from-left {
+ border-right: var(--ui-border-thickness) solid var(--ui-border-color);
+ margin-left: calc(var(--left-sidepanel-menu-width) * -1);
+ max-width: var(--left-sidepanel-menu-width);
+}
+
+.from-right {
+ border-left: var(--ui-border-thickness) solid var(--ui-border-color);
+ margin-right: calc(var(--right-sidepanel-menu-width) * -1);
+ max-width: var(--right-sidepanel-menu-width);
+}
+
+.sidepanel.is-open {
+ margin-right: 0;
+ margin-left: 0;
+}
+
+.main-content {
+ flex: 1;
+ height: 100%;
+ overflow: hidden;
+ transition: var(--sidepanel-transition);
+ width: 100%;
+}
diff --git a/src/components/SidePanel.js b/src/components/SidePanel.js
new file mode 100644
index 000000000..5107f7629
--- /dev/null
+++ b/src/components/SidePanel.js
@@ -0,0 +1,41 @@
+import './SidePanel.css';
+
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+
+class SidePanel extends Component {
+ static propTypes = {
+ from: PropTypes.string.isRequired,
+ isOpen: PropTypes.bool.isRequired,
+ children: PropTypes.node,
+ width: PropTypes.string,
+ };
+
+ render() {
+ const fromSideClass =
+ this.props.from === 'right' ? 'from-right' : 'from-left';
+
+ const styles = this.props.width
+ ? {
+ maxWidth: this.props.width,
+ marginRight: this.props.isOpen
+ ? '0'
+ : Number.parseInt(this.props.width) * -1,
+ }
+ : {};
+
+ return (
+
+ {this.props.children}
+
+ );
+ }
+}
+
+export default SidePanel;
diff --git a/src/connectedComponents/ConnectedFlexboxLayout.js b/src/connectedComponents/ConnectedFlexboxLayout.js
deleted file mode 100644
index 75a20c8b3..000000000
--- a/src/connectedComponents/ConnectedFlexboxLayout.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { connect } from 'react-redux';
-import FlexboxLayout from './FlexboxLayout';
-
-const mapStateToProps = state => {
- return {
- leftSidebarOpen: state.ui.leftSidebarOpen,
- rightSidebarOpen: state.ui.rightSidebarOpen,
- };
-};
-
-const ConnectedFlexboxLayout = connect(
- mapStateToProps,
- null
-)(FlexboxLayout);
-
-export default ConnectedFlexboxLayout;
diff --git a/src/connectedComponents/ConnectedToolbarRow.js b/src/connectedComponents/ConnectedToolbarRow.js
index 66233bbc9..3df035c3e 100644
--- a/src/connectedComponents/ConnectedToolbarRow.js
+++ b/src/connectedComponents/ConnectedToolbarRow.js
@@ -1,8 +1,5 @@
-import {
- setLeftSidebarOpen,
- setRightSidebarOpen,
-} from './../store/layout/actions.js';
-
+// TODO: REPLACE THIS WITH A CONTEXT PROVIDER
+// EVERYTHING IN `VIEWER.JS` COULD USE THIS FOR APPROPRIATE CONTEXT
import ToolbarRow from './ToolbarRow';
import { connect } from 'react-redux';
import { getActiveContexts } from './../store/layout/selectors.js';
@@ -10,25 +7,9 @@ import { getActiveContexts } from './../store/layout/selectors.js';
const mapStateToProps = state => {
return {
activeContexts: getActiveContexts(state),
- leftSidebarOpen: state.ui.leftSidebarOpen,
- rightSidebarOpen: state.ui.rightSidebarOpen,
};
};
-const mapDispatchToProps = dispatch => {
- return {
- setLeftSidebarOpen: state => {
- dispatch(setLeftSidebarOpen(state));
- },
- setRightSidebarOpen: state => {
- dispatch(setRightSidebarOpen(state));
- },
- };
-};
-
-const ConnectedToolbarRow = connect(
- mapStateToProps,
- mapDispatchToProps
-)(ToolbarRow);
+const ConnectedToolbarRow = connect(mapStateToProps)(ToolbarRow);
export default ConnectedToolbarRow;
diff --git a/src/connectedComponents/ConnectedViewer.js b/src/connectedComponents/ConnectedViewer.js
index 11ce68723..35f01f4c9 100644
--- a/src/connectedComponents/ConnectedViewer.js
+++ b/src/connectedComponents/ConnectedViewer.js
@@ -4,6 +4,13 @@ import OHIF from 'ohif-core';
const { setTimepoints, setMeasurements } = OHIF.redux.actions;
+const mapStateToProps = (state, ownProps) => {
+ return {
+ viewports: state.viewports.viewportSpecificData,
+ activeViewportIndex: state.viewports.activeViewportIndex,
+ };
+};
+
const mapDispatchToProps = dispatch => {
return {
onTimepointsUpdated: timepoints => {
@@ -16,7 +23,7 @@ const mapDispatchToProps = dispatch => {
};
const ConnectedViewer = connect(
- null,
+ mapStateToProps,
mapDispatchToProps
)(Viewer);
diff --git a/src/connectedComponents/FlexboxLayout.css b/src/connectedComponents/FlexboxLayout.css
deleted file mode 100644
index b976200f5..000000000
--- a/src/connectedComponents/FlexboxLayout.css
+++ /dev/null
@@ -1,46 +0,0 @@
-.FlexboxLayout {
- display: flex;
- flex: 1;
- flex-flow: row nowrap;
- align-items: stretch;
- height: calc(100% - var(--toolbar-height));
- width: 100%;
- overflow: hidden;
-}
-
-.sidebar-menu {
- height: 100%;
- transition: var(--sidebar-transition);
-}
-
-.sidebar-left {
- border-right: var(--ui-border-thickness) solid var(--ui-border-color);
- flex: 1;
- margin-left: calc(var(--left-sidebar-menu-width) * -1);
- max-width: var(--left-sidebar-menu-width);
- order: 1;
-}
-
-.sidebar-left.sidebar-open {
- margin-left: 0;
-}
-
-.main-content {
- flex: 1;
- height: 100%;
- order: 2;
- overflow: hidden;
- transition: var(--sidebar-transition);
- width: 100%;
-}
-
-.sidebar-right {
- flex: 1;
- margin-right: calc(var(--right-sidebar-menu-width) * -1);
- max-width: var(--right-sidebar-menu-width);
- order: 3;
-}
-
-.sidebar-right.sidebar-open {
- margin-right: 0;
-}
diff --git a/src/connectedComponents/FlexboxLayout.js b/src/connectedComponents/FlexboxLayout.js
deleted file mode 100644
index 732e5828d..000000000
--- a/src/connectedComponents/FlexboxLayout.js
+++ /dev/null
@@ -1,128 +0,0 @@
-import './FlexboxLayout.css';
-
-import React, { Component } from 'react';
-
-import ConnectedMeasurementTable from './ConnectedMeasurementTable';
-import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
-import ConnectedViewerMain from './ConnectedViewerMain.js';
-import PropTypes from 'prop-types';
-
-class FlexboxLayout extends Component {
- static propTypes = {
- studies: PropTypes.array,
- leftSidebarOpen: PropTypes.bool.isRequired,
- rightSidebarOpen: PropTypes.bool.isRequired,
- };
-
- state = {
- studiesForBrowser: [],
- };
-
- componentDidMount() {
- if (this.props.studies) {
- const studiesForBrowser = this.getStudiesForBrowser();
-
- this.setState({
- studiesForBrowser,
- });
- }
- }
-
- componentDidUpdate(prevProps) {
- if (this.props.studies !== prevProps.studies) {
- const studiesForBrowser = this.getStudiesForBrowser();
-
- this.setState({
- studiesForBrowser,
- });
- }
- }
-
- getStudiesForBrowser = () => {
- const { studies } = this.props;
-
- // TODO[react]:
- // - Add sorting of display sets
- // - Add useMiddleSeriesInstanceAsThumbnail
- // - Add showStackLoadingProgressBar option
- return studies.map(study => {
- const { studyInstanceUid } = study;
-
- const thumbnails = study.displaySets.map(displaySet => {
- const {
- displaySetInstanceUid,
- seriesDescription,
- seriesNumber,
- instanceNumber,
- numImageFrames,
- // TODO: This is undefined
- // modality,
- } = 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,
- };
- });
- };
-
- render() {
- let mainContentClassName = 'main-content';
- if (this.props.leftSidebarOpen) {
- mainContentClassName += ' sidebar-left-open';
- }
-
- if (this.props.rightSidebarOpen) {
- mainContentClassName += ' sidebar-right-open';
- }
-
- // TODO[react]: Make ConnectedMeasurementTable extension with state.timepointManager
- return (
-
- );
- }
-}
-
-export default FlexboxLayout;
diff --git a/src/connectedComponents/ToolbarRow.js b/src/connectedComponents/ToolbarRow.js
index 43a945c75..a12dde0d4 100644
--- a/src/connectedComponents/ToolbarRow.js
+++ b/src/connectedComponents/ToolbarRow.js
@@ -1,7 +1,11 @@
import './ToolbarRow.css';
import React, { Component } from 'react';
-import { RoundedButtonGroup, ToolbarButton } from 'react-viewerbase';
+import {
+ RoundedButtonGroup,
+ ToolbarButton,
+ ExpandableToolMenu,
+} from 'react-viewerbase';
import { commandsManager, extensionManager } from './../App.js';
import ConnectedCineDialog from './ConnectedCineDialog';
@@ -11,19 +15,17 @@ import { MODULE_TYPES } from 'ohif-core';
import PropTypes from 'prop-types';
class ToolbarRow extends Component {
+ // TODO: Simplify these? isOpen can be computed if we say "any" value for selected,
+ // closed if selected is null/undefined
static propTypes = {
- leftSidebarOpen: PropTypes.bool.isRequired,
- rightSidebarOpen: PropTypes.bool.isRequired,
- setLeftSidebarOpen: PropTypes.func,
- setRightSidebarOpen: PropTypes.func,
+ isLeftSidePanelOpen: PropTypes.bool.isRequired,
+ isRightSidePanelOpen: PropTypes.bool.isRequired,
+ selectedLeftSidePanel: PropTypes.string.isRequired,
+ selectedRightSidePanel: PropTypes.string.isRequired,
+ handleSidePanelChange: PropTypes.func,
activeContexts: PropTypes.arrayOf(PropTypes.string).isRequired,
};
- static defaultProps = {
- leftSidebarOpen: false,
- rightSidebarOpen: false,
- };
-
constructor(props) {
super(props);
@@ -43,6 +45,43 @@ class ToolbarRow extends Component {
};
this._handleBuiltIn = _handleBuiltIn.bind(this);
+
+ const panelModules = extensionManager.modules[MODULE_TYPES.PANEL];
+ this.buttonGroups = {
+ left: [
+ // TODO: This should come from extensions, instead of being baked in
+ {
+ value: 'studies',
+ icon: 'th-large',
+ bottomLabel: 'Series',
+ },
+ ],
+ right: [],
+ };
+
+ panelModules.forEach(panelExtension => {
+ const panelModule = panelExtension.module;
+ const defaultContexts = Array.from(panelModule.defaultContext);
+
+ // MENU OPTIONS
+ panelModule.menuOptions.forEach(menuOption => {
+ const contexts = Array.from(menuOption.context || defaultContexts);
+
+ const activeContextIncludesAnyPanelContexts = this.props.activeContexts.some(
+ actx => contexts.includes(actx)
+ );
+ if (activeContextIncludesAnyPanelContexts) {
+ const menuOptionEntry = {
+ value: menuOption.target,
+ icon: menuOption.icon,
+ bottomLabel: menuOption.label,
+ };
+ const from = menuOption.from || 'right';
+
+ this.buttonGroups[from].push(menuOptionEntry);
+ }
+ });
+ });
}
componentDidUpdate(prevProps) {
@@ -56,39 +95,7 @@ class ToolbarRow extends Component {
}
}
- onLeftSidebarValueChanged = value => {
- this.props.setLeftSidebarOpen(!!value);
- };
-
- onRightSidebarValueChanged = value => {
- this.props.setRightSidebarOpen(!!value);
- };
-
render() {
- const leftSidebarToggle = [
- {
- value: 'studies',
- icon: 'th-large',
- bottomLabel: 'Series',
- },
- ];
-
- const rightSidebarToggle = [
- {
- value: 'measurements',
- icon: 'list',
- bottomLabel: 'Measurements',
- },
- ];
-
- const leftSidebarValue = this.props.leftSidebarOpen
- ? leftSidebarToggle[0].value
- : null;
-
- const rightSidebarValue = this.props.rightSidebarOpen
- ? rightSidebarToggle[0].value
- : null;
-
const buttonComponents = _getButtonComponents.call(
this,
this.state.toolbarButtons,
@@ -102,14 +109,20 @@ class ToolbarRow extends Component {
zIndex: 999,
};
+ const onPress = (side, value) => {
+ this.props.handleSidePanelChange(side, value);
+ };
+ const onPressLeft = onPress.bind(this, 'left');
+ const onPressRight = onPress.bind(this, 'right');
+
return (
<>
{buttonComponents}
@@ -119,11 +132,13 @@ class ToolbarRow extends Component {
className="pull-right m-t-1 rm-x-1"
style={{ marginLeft: 'auto' }}
>
-
+ {this.buttonGroups.right.length && (
+
+ )}
@@ -140,36 +155,64 @@ class ToolbarRow extends Component {
*/
function _getButtonComponents(toolbarButtons, activeButtons) {
return toolbarButtons.map((button, index) => {
- // TODO: If `button.buttons`, use `ExpandedToolMenu`
- // I don't believe any extensions currently leverage this
+ if (button.buttons) {
+ // Iterate over button definitions and update `onClick` behavior
+ const childButtons = button.buttons.map(childButton => {
+ childButton.onClick = _handleToolbarButtonClick.bind(this, childButton);
+ return childButton;
+ });
+
+ return (
+
+ );
+ }
+
return (
{
- if (button.commandName) {
- const options = Object.assign({ evt }, button.commandOptions);
- commandsManager.runCommand(button.commandName, options);
- }
-
- // TODO: Use Types ENUM
- // TODO: We can update this to be a `getter` on the extension to query
- // For the active tools after we apply our updates?
- if (button.type === 'setToolActive') {
- this.setState({
- activeButtons: [button.id],
- });
- } else if (button.type === 'builtIn') {
- this._handleBuiltIn(button.options);
- }
- }}
+ onClick={_handleToolbarButtonClick.bind(this, button)}
isActive={activeButtons.includes(button.id)}
/>
);
});
}
+/**
+ * A handy way for us to handle different button types. IE. firing commands for
+ * buttons, or initiation built in behavior.
+ *
+ * @param {*} button
+ * @param {*} evt
+ * @param {*} props
+ */
+function _handleToolbarButtonClick(button, evt, props) {
+ if (button.commandName) {
+ const options = Object.assign({ evt }, button.commandOptions);
+ commandsManager.runCommand(button.commandName, options);
+ }
+
+ // TODO: Use Types ENUM
+ // TODO: We can update this to be a `getter` on the extension to query
+ // For the active tools after we apply our updates?
+ if (button.type === 'setToolActive') {
+ this.setState({
+ activeButtons: [button.id],
+ });
+ } else if (button.type === 'builtIn') {
+ this._handleBuiltIn(button.options);
+ }
+}
+
+/**
+ *
+ */
function _getVisibleToolbarButtons() {
const toolbarModules = extensionManager.modules[MODULE_TYPES.TOOLBAR];
const toolbarButtonDefinitions = [];
diff --git a/src/connectedComponents/Viewer.js b/src/connectedComponents/Viewer.js
index 0fd4610d2..e6e900f26 100644
--- a/src/connectedComponents/Viewer.js
+++ b/src/connectedComponents/Viewer.js
@@ -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 */}
{whiteLabelling => (
@@ -196,16 +235,132 @@ class Viewer extends Component {
)}
-
-
- {/*
*/}
- {/*
*/}
-
-
+
+ {/* TOOLBAR */}
+
{
+ 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);
+ }}
+ />
+
+ {/**/}
+ {/**/}
+
+ {/* VIEWPORTS + SIDEPANELS */}
+
+ {/* LEFT */}
+
+ {VisiblePanelLeft ? (
+
+ ) : (
+
+ )}
+
+
+ {/* MAIN */}
+
+
+
+
+ {/* RIGHT */}
+
+ {VisiblePanelRight && (
+
+ )}
+
+
>
);
}
}
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,
+ };
+ });
+};
diff --git a/src/connectedComponents/ViewerMain.css b/src/connectedComponents/ViewerMain.css
index 8d393264b..faa71b191 100644
--- a/src/connectedComponents/ViewerMain.css
+++ b/src/connectedComponents/ViewerMain.css
@@ -15,27 +15,7 @@
transition: all 0.3s ease;
}
-.ViewerMain>div {
+.ViewerMain > div {
width: 100%;
height: 100%;
}
-
-.ViewerMain #imageViewerViewports .viewportContainer {
- /*theme('border', '%s solid $uiBorderColorDark' % $viewportBorderThickness)*/
- /*float: left;
- position: relative;*/
- outline: 0; /* Prevent blue outline in Chrome */
-}
-
-.ViewerMain #imageViewerViewports .viewportContainer:hover,
-.ViewerMain #imageViewerViewports .viewportContainer:active,
-.ViewerMain #imageViewerViewports .viewportContainer:hover.active {
- outline: 0; /* Prevent blue outline in Chrome */
-}
-
-/* &:hover
- //theme('border', '%s solid $uiBorderColor' % $viewportBorderThickness)
-
- &.active, &:hover.active
- //theme('border', '%s solid $uiBorderColorActive' % $viewportBorderThickness)
-*/
diff --git a/src/connectedComponents/ViewerMain.js b/src/connectedComponents/ViewerMain.js
index 02393911d..64ae2549a 100644
--- a/src/connectedComponents/ViewerMain.js
+++ b/src/connectedComponents/ViewerMain.js
@@ -139,9 +139,9 @@ class ViewerMain extends Component {
{this.state.displaySets.length && (
{/* Children to add to each viewport that support children */}
diff --git a/src/index.js b/src/index.js
index fff5f61bd..0ef2002c4 100644
--- a/src/index.js
+++ b/src/index.js
@@ -109,18 +109,9 @@ props.oidc = [
};*/
/*
-UI settings
-Plugins
- - Custom tools / buttons
- - Custom Sidebar module thing
- - Custom Viewports
- - Custom Sop Class Interpreters
-*/
-
/*"PUBLIC_SETTINGS": {
"ui": {
"studyListFunctionsEnabled": true,
- "leftSidebarOpen": false,
"displaySetNavigationLoopOverSeries": false,
"displaySetNavigationMultipleViewports": true,
"autoPositionMeasurementsTextCallOuts": "TRLB"
diff --git a/src/initCornerstoneTools.js b/src/initCornerstoneTools.js
new file mode 100644
index 000000000..a53f7d8e8
--- /dev/null
+++ b/src/initCornerstoneTools.js
@@ -0,0 +1,27 @@
+import Hammer from 'hammerjs';
+import cornerstone from 'cornerstone-core';
+import cornerstoneMath from 'cornerstone-math';
+import cornerstoneTools from 'cornerstone-tools';
+
+export default function(configuration = {}) {
+ // For debugging
+ window.cornerstoneTools = cornerstoneTools;
+
+ cornerstoneTools.external.cornerstone = cornerstone;
+ cornerstoneTools.external.Hammer = Hammer;
+ cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
+ cornerstoneTools.init(configuration);
+
+ // Set the tool font and font size
+ // context.font = "[style] [variant] [weight] [size]/[line height] [font family]";
+ const fontFamily =
+ 'Roboto, OpenSans, HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif';
+ cornerstoneTools.textStyle.setFont(`16px ${fontFamily}`);
+
+ // Tool styles/colors
+ cornerstoneTools.toolStyle.setToolWidth(2);
+ cornerstoneTools.toolColors.setToolColor('rgb(255, 255, 0)');
+ cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)');
+
+ cornerstoneTools.store.state.touchProximity = 40;
+}
diff --git a/src/lib/getMeasurementLocationCallback.js b/src/lib/getMeasurementLocationCallback.js
index 38460ab60..67858782d 100644
--- a/src/lib/getMeasurementLocationCallback.js
+++ b/src/lib/getMeasurementLocationCallback.js
@@ -12,6 +12,18 @@ export default function getMeasurementLocationCallback(
const ToolInstance = cornerstoneTools.getToolForElement(element, toolType);
+ if (
+ !ToolInstance ||
+ !ToolInstance.configuration ||
+ !ToolInstance.configuration.getMeasurementLocationCallback
+ ) {
+ console.warn(
+ 'Tool instance configuration is missing: getMeasurementLocationCallback'
+ );
+
+ return;
+ }
+
ToolInstance.configuration.getMeasurementLocationCallback(
tool,
eventData,
diff --git a/src/setupTools.js b/src/setupTools.js
index df77f1713..9295fa0d2 100644
--- a/src/setupTools.js
+++ b/src/setupTools.js
@@ -147,7 +147,7 @@ export default function setupTools(store) {
{ name: 'Wwwc', mouseButtonMasks: [1] },
{
name: 'Bidirectional',
- configuration: {
+ props: {
configuration: {
getMeasurementLocationCallback: toolLabellingFlowCallback,
},
@@ -156,7 +156,7 @@ export default function setupTools(store) {
},
{
name: 'Length',
- configuration: {
+ props: {
configuration: {
getMeasurementLocationCallback: toolLabellingFlowCallback,
},
@@ -165,7 +165,7 @@ export default function setupTools(store) {
},
{
name: 'Angle',
- configuration: {
+ props: {
configuration: {
getMeasurementLocationCallback: toolLabellingFlowCallback,
},
@@ -185,7 +185,7 @@ export default function setupTools(store) {
},
{
name: 'EllipticalRoi',
- configuration: {
+ props: {
configuration: {
getMeasurementLocationCallback: toolLabellingFlowCallback,
},
@@ -194,7 +194,7 @@ export default function setupTools(store) {
},
{
name: 'CircleRoi',
- configuration: {
+ props: {
configuration: {
getMeasurementLocationCallback: toolLabellingFlowCallback,
},
@@ -203,7 +203,7 @@ export default function setupTools(store) {
},
{
name: 'RectangleRoi',
- configuration: {
+ props: {
configuration: {
getMeasurementLocationCallback: toolLabellingFlowCallback,
},
diff --git a/src/store/layout/actions.js b/src/store/layout/actions.js
deleted file mode 100644
index 2b08800b1..000000000
--- a/src/store/layout/actions.js
+++ /dev/null
@@ -1,16 +0,0 @@
-export const setLeftSidebarOpen = state => ({
- type: 'SET_LEFT_SIDEBAR_OPEN',
- state,
-});
-
-export const setRightSidebarOpen = state => ({
- type: 'SET_RIGHT_SIDEBAR_OPEN',
- state,
-});
-
-const actions = {
- setLeftSidebarOpen,
- setRightSidebarOpen,
-};
-
-export default actions;
diff --git a/src/store/layout/reducers.js b/src/store/layout/reducers.js
index b292074ef..8e75a1179 100644
--- a/src/store/layout/reducers.js
+++ b/src/store/layout/reducers.js
@@ -1,17 +1,10 @@
const defaultState = {
- leftSidebarOpen: true,
- rightSidebarOpen: false,
labelling: {},
contextMenu: {},
};
const ui = (state = defaultState, action) => {
switch (action.type) {
- // ~ SIDEBAR
- case 'SET_LEFT_SIDEBAR_OPEN':
- return Object.assign({}, state, { leftSidebarOpen: action.state });
- case 'SET_RIGHT_SIDEBAR_OPEN':
- return Object.assign({}, state, { rightSidebarOpen: action.state });
case 'SET_LABELLING_FLOW_DATA': {
const labelling = Object.assign({}, action.labellingFlowData);
diff --git a/src/variables.css b/src/variables.css
index bc13b3c0e..bf0df7495 100644
--- a/src/variables.css
+++ b/src/variables.css
@@ -4,8 +4,8 @@
--top-bar-expanded-height: 160px;
--toolbar-height: 78px;
--toolbar-drawer-height: 62px;
- --left-sidebar-menu-width: 307px;
- --right-sidebar-menu-width: 323px;
+ --left-sidepanel-menu-width: 307px;
+ --right-sidepanel-menu-width: 323px;
--study-list-padding: 8%;
--study-list-padding-medium-screen: 10px;
}
@@ -21,7 +21,7 @@
:root {
--transition-duration: 0.3s;
--transition-effect: ease;
- --sidebar-transition: all 0.3s ease;
+ --sidepanel-transition: all 0.3s ease;
}
/* Thicknesses */
diff --git a/yarn.lock b/yarn.lock
index d2a58c69a..2e2f87fcf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1214,10 +1214,10 @@
universal-user-agent "^2.0.0"
url-template "^2.0.8"
-"@ohif/extension-cornerstone@0.0.37":
- version "0.0.37"
- resolved "https://registry.yarnpkg.com/@ohif/extension-cornerstone/-/extension-cornerstone-0.0.37.tgz#d9d31409ffbc773dde17767daa02b87f461cf11a"
- integrity sha512-090WPGXOYIxZjQe0mIM7SipgR3ggUZpbtWpwg7huWC0PQObIM3Juy4L8mtss23OKaKsmrmVxUaFc5NRQhs+Z+w==
+"@ohif/extension-cornerstone@0.0.38":
+ version "0.0.38"
+ resolved "https://registry.yarnpkg.com/@ohif/extension-cornerstone/-/extension-cornerstone-0.0.38.tgz#c1cdf2796bef441a293bcc045f9748c7b2c1395f"
+ integrity sha512-xWvK04s2xwjNlIvz4BpJ5pWP8wNmW9kpud1zkywR+TFYgthH2X64Tr01VZTOh8Mi+0Xkwe5IElb4Sw2aVA4SBg==
dependencies:
"@babel/runtime" "^7.2.0"
classnames "^2.2.6"
@@ -3796,10 +3796,10 @@ cornerstone-math@^0.1.8:
resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5"
integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw==
-cornerstone-tools@^3.13.0:
- version "3.14.1"
- resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-3.14.1.tgz#9a15bca1c4ab96698e91b35f38b5cfa165d58032"
- integrity sha512-gfYhRawZ7oZhft1klF2b6IcTciN3f05YM8v4AWT6sVcPiA+MkRPf9GVcrmUOUDsfUtg8RG0TAAO7lN9lRr92BQ==
+cornerstone-tools@^3.15.1:
+ version "3.15.1"
+ resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-3.15.1.tgz#9c14a0dde32d8d8cc60e1abf3c222b681964fb9d"
+ integrity sha512-nD9M6cbcQJoU5XYUfbrHyi8lt9SD32pQFJ87PfIVRgMvJQA3awobcWwnXZJAD7lB7xPvO1n763FzeuQ5CqWcpQ==
dependencies:
"@babel/runtime" "7.1.2"
cornerstone-math "0.1.7"