diff --git a/extensions/ohif-cornerstone-extension/package.json b/extensions/ohif-cornerstone-extension/package.json index 9fb20cef9..9e925397f 100644 --- a/extensions/ohif-cornerstone-extension/package.json +++ b/extensions/ohif-cornerstone-extension/package.json @@ -1,6 +1,6 @@ { "name": "ohif-cornerstone-extension", - "version": "0.0.28", + "version": "0.0.29", "description": "OHIF extension for Cornerstone", "author": "OHIF", "license": "MIT", @@ -35,6 +35,12 @@ "react-viewerbase": "^0.2.10", "redux": "^4.0.1" }, + "dependencies": { + "@babel/runtime": "^7.2.0", + "classnames": "^2.2.6", + "lodash.throttle": "^4.1.1", + "react-cornerstone-viewport": "0.1.26" + }, "devDependencies": { "@babel/core": "^7.2.2", "@babel/plugin-external-helpers": "^7.2.0", @@ -87,11 +93,5 @@ ], "files": [ "dist" - ], - "dependencies": { - "@babel/runtime": "^7.2.0", - "classnames": "^2.2.6", - "lodash.throttle": "^4.1.1", - "react-cornerstone-viewport": "^0.1.25" - } + ] } diff --git a/extensions/ohif-cornerstone-extension/src/ConnectedCineDialog.js b/extensions/ohif-cornerstone-extension/src/ConnectedCineDialog.js index 94486ddd1..6247f6bf2 100644 --- a/extensions/ohif-cornerstone-extension/src/ConnectedCineDialog.js +++ b/extensions/ohif-cornerstone-extension/src/ConnectedCineDialog.js @@ -1,111 +1,102 @@ import { connect } from 'react-redux'; import { CineDialog } from 'react-viewerbase'; import OHIF from 'ohif-core'; +import csTools from 'cornerstone-tools'; +// Our target output kills the `as` and "import" throws a keyword error +// import { import as toolImport, getToolState } from 'cornerstone-tools'; import cloneDeep from 'lodash.clonedeep'; +const toolImport = csTools.import; +const scrollToIndex = toolImport('util/scrollToIndex'); const { setViewportSpecificData } = OHIF.redux.actions; -// TODO: I'm guessing this function will be used in other connect locations -// so we might want to put it somewhere shared -function getActiveViewportSpecificData(state) { - const { viewportSpecificData, activeViewportIndex } = state.viewports; - return viewportSpecificData[activeViewportIndex]; -} - +// Why do I need or care about any of this info? +// A dispatch action should be able to pull this at the time of an event? +// `isPlaying` and `cineFrameRate` might matter, but I think we can prop pass for those. const mapStateToProps = state => { - // TODO: - // - Test if including CineDialog in the toolbarRow will prevent it - // from hovering over the rest of the UI when visible. - // - // - Create custom ToolbarButton which just shows Play state - // - Connect this ToolbarButton to Redux - const activeViewportSpecificData = getActiveViewportSpecificData(state); + // Get activeViewport's `cine` and `stack` + const { viewportSpecificData, activeViewportIndex } = state.viewports; + const { cine, dom } = viewportSpecificData[activeViewportIndex] || {}; - let stack = { - imageIds: [], - currentImageIdIndex: 0 - }; - if (activeViewportSpecificData && activeViewportSpecificData.stack) { - stack = activeViewportSpecificData.stack - } + const cineData = cine || { + isPlaying: false, + cineFrameRate: 24 + }; - let cine = { - isPlaying: false, - cineFrameRate: 24 - }; - if (activeViewportSpecificData && activeViewportSpecificData.cine) { - cine = activeViewportSpecificData.cine - } - - - // TODO: activeViewportStackData won't currently change anything on - // CornerstoneViewport. The updates are too frequent and it's killing - // performance. Need to revisit how we can do this. - return { - activeViewportStackData: stack, - activeViewportCineData: cine, - activeViewportIndex: state.viewports.activeViewportIndex - }; + // New props we're creating? + return { + activeEnabledElement: dom, + activeViewportCineData: cineData, + activeViewportIndex: state.viewports.activeViewportIndex + }; }; const mapDispatchToProps = dispatch => { - return { - dispatchSetViewportSpecificData: (viewportIndex, data) => { - dispatch(setViewportSpecificData(viewportIndex, data)); - }, - }; + return { + dispatchSetViewportSpecificData: (viewportIndex, data) => { + dispatch(setViewportSpecificData(viewportIndex, data)); + } + }; }; const mergeProps = (propsFromState, propsFromDispatch, ownProps) => { - const { activeViewportStackData, activeViewportCineData, activeViewportIndex } = propsFromState; + const { + activeEnabledElement, + activeViewportCineData, + activeViewportIndex + } = propsFromState; - return { - cineFrameRate: activeViewportCineData.cineFrameRate, - isPlaying: activeViewportCineData.isPlaying, - onPlayPauseChanged: isPlaying => { - const cine = cloneDeep(activeViewportCineData); - cine.isPlaying = !cine.isPlaying; + return { + cineFrameRate: activeViewportCineData.cineFrameRate, + isPlaying: activeViewportCineData.isPlaying, + onPlayPauseChanged: isPlaying => { + const cine = cloneDeep(activeViewportCineData); + cine.isPlaying = !cine.isPlaying; - propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { cine }); - }, - onFrameRateChanged: frameRate => { - const cine = cloneDeep(activeViewportCineData); - cine.cineFrameRate = frameRate; + propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { + cine + }); + }, + onFrameRateChanged: frameRate => { + const cine = cloneDeep(activeViewportCineData); + cine.cineFrameRate = frameRate; - propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { cine }); - }, - onClickNextButton: () => { - const stack = cloneDeep(activeViewportStackData); - const largestPossibleIndex = stack.imageIds.length - 1; - stack.currentImageIdIndex = Math.min(stack.currentImageIdIndex + 1, largestPossibleIndex) - - propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack }); - }, - onClickBackButton: () => { - const stack = cloneDeep(activeViewportStackData); - stack.currentImageIdIndex = Math.max(stack.currentImageIdIndex - 1, 0); - - propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack }); - }, - onClickSkipToStart: () => { - const stack = cloneDeep(activeViewportStackData); - stack.currentImageIdIndex = 0; - - propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack }); - }, - onClickSkipToEnd: () => { - const stack = cloneDeep(activeViewportStackData); - stack.currentImageIdIndex = stack.imageIds.length; - - propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack }); - } - }; + propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { + cine + }); + }, + onClickNextButton: () => { + const stackData = csTools.getToolState(activeEnabledElement, 'stack'); + if (!stackData || !stackData.data || !stackData.data.length) return; + const { currentImageIdIndex, imageIds } = stackData.data[0]; + if (currentImageIdIndex >= imageIds.length - 1) return; + scrollToIndex(activeEnabledElement, currentImageIdIndex + 1); + }, + onClickBackButton: () => { + const stackData = csTools.getToolState(activeEnabledElement, 'stack'); + if (!stackData || !stackData.data || !stackData.data.length) return; + const { currentImageIdIndex } = stackData.data[0]; + if (currentImageIdIndex === 0) return; + scrollToIndex(activeEnabledElement, currentImageIdIndex - 1); + }, + onClickSkipToStart: () => { + const stackData = csTools.getToolState(activeEnabledElement, 'stack'); + if (!stackData || !stackData.data || !stackData.data.length) return; + scrollToIndex(activeEnabledElement, 0); + }, + onClickSkipToEnd: () => { + const stackData = csTools.getToolState(activeEnabledElement, 'stack'); + if (!stackData || !stackData.data || !stackData.data.length) return; + const lastIndex = stackData.data[0].imageIds.length - 1; + scrollToIndex(activeEnabledElement, lastIndex); + } + }; }; const ConnectedCineDialog = connect( - mapStateToProps, - mapDispatchToProps, - mergeProps + mapStateToProps, + mapDispatchToProps, + mergeProps )(CineDialog); export default ConnectedCineDialog; diff --git a/extensions/ohif-cornerstone-extension/src/ConnectedCornerstoneViewport.js b/extensions/ohif-cornerstone-extension/src/ConnectedCornerstoneViewport.js index ac299ba7a..4f8e65d56 100644 --- a/extensions/ohif-cornerstone-extension/src/ConnectedCornerstoneViewport.js +++ b/extensions/ohif-cornerstone-extension/src/ConnectedCornerstoneViewport.js @@ -50,6 +50,21 @@ const mapDispatchToProps = (dispatch, ownProps) => { dispatch(clearViewportSpecificData(viewportIndex)); }, + /** + * Our component "enables" the underlying dom element on "componentDidMount" + * It listens for that event, and then emits the enabledElement. We can grab + * a reference to it here, to make playing with cornerstone's native methods + * easier. + */ + onElementEnabled: event => { + const enabledElement = event.detail.element; + dispatch( + setViewportSpecificData(viewportIndex, { + dom: enabledElement + }) + ); + }, + onMeasurementsChanged: (event, action) => { const { onAdded, diff --git a/extensions/ohif-cornerstone-extension/src/ToolbarModule.js b/extensions/ohif-cornerstone-extension/src/ToolbarModule.js index 657441b60..df3fb99ee 100644 --- a/extensions/ohif-cornerstone-extension/src/ToolbarModule.js +++ b/extensions/ohif-cornerstone-extension/src/ToolbarModule.js @@ -6,7 +6,7 @@ import ConnectedCineDialog from './ConnectedCineDialog'; class ToolbarModule extends Component { state = { cineDialogOpen: false - } + }; onClickCineToolbarButton = () => { this.setState({ @@ -16,16 +16,20 @@ class ToolbarModule extends Component { render() { const cineDialogContainerStyle = { - display: this.state.cineDialogOpen ? 'inline-block' : 'none' + display: this.state.cineDialogOpen ? 'block' : 'none', + position: 'absolute', + top: '82px', + zIndex: 999 }; - return (
- - + +
diff --git a/extensions/ohif-cornerstone-extension/yarn.lock b/extensions/ohif-cornerstone-extension/yarn.lock index 6d9a8116a..7fbfa0064 100644 --- a/extensions/ohif-cornerstone-extension/yarn.lock +++ b/extensions/ohif-cornerstone-extension/yarn.lock @@ -4418,10 +4418,10 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -react-cornerstone-viewport@^0.1.23: - version "0.1.23" - resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.23.tgz#4f065c4d1a5e721a45b819d1b7e94489af69b5c1" - integrity sha512-uukvBIrtvExY0oOo6d/79CZRrALtU2Pq8NBovYVvD02Hg40yYtGYLZ2AnbjbQEtsf0LjpLhcfT/QU6ZOB2v8rw== +react-cornerstone-viewport@0.1.26: + version "0.1.26" + resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.26.tgz#eb09ae7c5d0f24e20a852fdc3ce648935d182370" + integrity sha512-t4onVJeib+FHWNHZNVrJyndLw8dCf7cI/69r5pNE4Sefjxw7pDuPdXfmZdcT8VWpl9cLairTqtqaLW+gXxTxIA== dependencies: lodash.debounce "^4.0.8" moment "^2.23.0" diff --git a/package.json b/package.json index cc94fb3f4..19ee475ca 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "lodash.isequal": "4.5.0", "moment": "^2.24.0", "ohif-core": "0.5.2", - "ohif-cornerstone-extension": "^0.0.28", + "ohif-cornerstone-extension": "0.0.29", "ohif-dicom-html-extension": "^0.0.2", "ohif-dicom-microscopy-extension": "^0.0.5", "ohif-dicom-pdf-extension": "^0.0.6", diff --git a/src/config.js b/src/config.js index f21f38de6..8385e7ead 100644 --- a/src/config.js +++ b/src/config.js @@ -20,7 +20,6 @@ OHIF.user.getAccessToken = () => { // TODO: Get the Redux store from somewhere else const state = window.store.getState() if (!state.oidc || !state.oidc.user) { - console.warn('failed to grab access token') return } diff --git a/yarn.lock b/yarn.lock index 2beb85360..04fa2ca6d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9617,15 +9617,15 @@ ohif-core@0.5.2: lodash.merge "^4.6.1" validate.js "^0.12.0" -ohif-cornerstone-extension@^0.0.28: - version "0.0.28" - resolved "https://registry.yarnpkg.com/ohif-cornerstone-extension/-/ohif-cornerstone-extension-0.0.28.tgz#79e07133e0b08f2c38f4bdb77032c4c7182266c4" - integrity sha512-GPXMqrmB+kmRAEOlSD0nBO4vwOvYLyn4gt9AtP016JMCLADpAsEDymUzE8Psr0NPLSvk6QrPL0/bKglfsGl7sA== +ohif-cornerstone-extension@0.0.29: + version "0.0.29" + resolved "https://registry.yarnpkg.com/ohif-cornerstone-extension/-/ohif-cornerstone-extension-0.0.29.tgz#288f3acb9b85b2434e39cae728ffd6dd2d67e0c2" + integrity sha512-1tFQokdYhW6Z5bhABvU84vLo4u7SHkFqgNpqwyw4mz3UlsbUBB30P9K88a68Txu8/wsMxTa9h8cAEZa1WfJo+g== dependencies: "@babel/runtime" "^7.2.0" classnames "^2.2.6" lodash.throttle "^4.1.1" - react-cornerstone-viewport "^0.1.25" + react-cornerstone-viewport "0.1.26" ohif-dicom-html-extension@^0.0.2: version "0.0.2" @@ -11401,10 +11401,10 @@ react-bootstrap-modal@4.2.0, react-bootstrap-modal@^4.2.0: react-overlays "^0.8.0" react-transition-group "^2.0.0" -react-cornerstone-viewport@^0.1.25: - version "0.1.25" - resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.25.tgz#8d55d35db76f56c4844770ad14b21aeb20640724" - integrity sha512-h+00YdJubWiPylVAeRLhbOA3908ST3aB+ideoMsnDzNH3PGhL0KIgey5e8caAhWXkmFOS+SRJbXjpLCD8aGcLw== +react-cornerstone-viewport@0.1.26: + version "0.1.26" + resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.26.tgz#eb09ae7c5d0f24e20a852fdc3ce648935d182370" + integrity sha512-t4onVJeib+FHWNHZNVrJyndLw8dCf7cI/69r5pNE4Sefjxw7pDuPdXfmZdcT8VWpl9cLairTqtqaLW+gXxTxIA== dependencies: lodash.debounce "^4.0.8" moment "^2.23.0"