Merge branch 'master' of github.com:OHIF/Viewers

# Conflicts:
#	extensions/ohif-cornerstone-extension/package.json
#	package.json
#	src/components/Header/Header.js
#	src/components/Labelling/LabellingFlow.js
#	src/components/OHIFLogo/OHIFLogo.js
#	src/connectedComponents/ConnectedPluginSwitch.js
#	src/connectedComponents/ToolbarRow.js
#	src/utils/getDefaultToolbarButtons.js
#	src/utils/getDefaultToolbarButtons.test.js
#	yarn.lock
This commit is contained in:
dannyrb 2019-05-30 00:46:00 -04:00
commit ffbd6ad6e4
64 changed files with 988 additions and 961 deletions

View File

@ -3,7 +3,7 @@
"printWidth": 80,
"proseWrap": "always",
"tabWidth": 2,
"semi": false,
"semi": true,
"singleQuote": true,
"endOfLine": "lf"
}

View File

@ -4418,10 +4418,10 @@ randomfill@^1.0.3:
randombytes "^2.0.5"
safe-buffer "^5.1.0"
react-cornerstone-viewport@0.1.28:
version "0.1.28"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.28.tgz#ca1b0062c9f08868c653c11d6cdef8f5d519f90a"
integrity sha512-lGXFB1pxUJE+tozAHjQD8KcmnGuGG/nD3CGrK44cFqx8e3W/CKU9/bZM6qomy/X1fK5TlnJUaZ9NOCaDLhzJrQ==
react-cornerstone-viewport@0.1.29:
version "0.1.29"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.29.tgz#7ef3c2f3d7dc45339b14c17a53e05db367a28394"
integrity sha512-KUjdqjti53v4eNyFTT3tXJ4cTyb64eMW3QF1cQqd+1+Re8UXSF1+k9uku15vR/Qt/YOv9Bsh74PabBct7BxWog==
dependencies:
lodash.debounce "^4.0.8"
moment "^2.23.0"

View File

@ -1,47 +1,47 @@
import React, { Component } from 'react'
import { BrowserRouter as Router } from 'react-router-dom'
import { Provider } from 'react-redux'
import { createStore, combineReducers } from 'redux'
import PropTypes from 'prop-types'
import OHIF from 'ohif-core'
import './config'
import ui from './redux/ui.js'
import OHIFStandaloneViewer from './OHIFStandaloneViewer'
import WhiteLabellingContext from './WhiteLabellingContext'
import OHIFCornerstoneExtension from 'ohif-cornerstone-extension'
import OHIFVTKExtension from 'ohif-vtk-extension'
import OHIFDicomPDFExtension from 'ohif-dicom-pdf-extension'
import OHIFDicomHtmlExtension from 'ohif-dicom-html-extension'
import OHIFDicomMicroscopyExtension from 'ohif-dicom-microscopy-extension'
import { OidcProvider, reducer as oidcReducer } from 'redux-oidc'
import React, { Component } from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore, combineReducers } from 'redux';
import PropTypes from 'prop-types';
import OHIF from 'ohif-core';
import './config';
import ui from './redux/ui.js';
import OHIFStandaloneViewer from './OHIFStandaloneViewer';
import WhiteLabellingContext from './WhiteLabellingContext';
import OHIFCornerstoneExtension from 'ohif-cornerstone-extension';
import OHIFVTKExtension from 'ohif-vtk-extension';
import OHIFDicomPDFExtension from 'ohif-dicom-pdf-extension';
import OHIFDicomHtmlExtension from 'ohif-dicom-html-extension';
import OHIFDicomMicroscopyExtension from 'ohif-dicom-microscopy-extension';
import { OidcProvider, reducer as oidcReducer } from 'redux-oidc';
import {
getDefaultToolbarButtons,
getUserManagerForOpenIdConnectClient,
initWebWorkers,
} from './utils/index.js'
import setupTools from './setupTools'
import ConnectedToolContextMenu from './connectedComponents/ConnectedToolContextMenu'
} from './utils/index.js';
import setupTools from './setupTools';
import ConnectedToolContextMenu from './connectedComponents/ConnectedToolContextMenu';
const { ExtensionManager } = OHIF.extensions
const { reducers, localStorage } = OHIF.redux
const { ExtensionManager } = OHIF.extensions;
const { reducers, localStorage } = OHIF.redux;
reducers.ui = ui
reducers.oidc = oidcReducer
reducers.ui = ui;
reducers.oidc = oidcReducer;
const combined = combineReducers(reducers)
const store = createStore(combined, localStorage.loadState())
const combined = combineReducers(reducers);
const store = createStore(combined, localStorage.loadState());
store.subscribe(() => {
localStorage.saveState({
preferences: store.getState().preferences,
})
})
});
});
setupTools(store)
setupTools(store);
const children = {
viewport: [<ConnectedToolContextMenu />],
}
};
/** TODO: extensions should be passed in as prop as soon as we have the extensions as separate packages and then registered by ExtensionsManager */
const extensions = [
@ -50,15 +50,15 @@ const extensions = [
new OHIFDicomPDFExtension(),
new OHIFDicomHtmlExtension(),
new OHIFDicomMicroscopyExtension(),
]
ExtensionManager.registerExtensions(store, extensions)
];
ExtensionManager.registerExtensions(store, extensions);
// TODO[react] Use a provider when the whole tree is React
window.store = store
window.store = store;
function handleServers(servers) {
if (servers) {
OHIF.utils.addServers(servers, store)
OHIF.utils.addServers(servers, store);
}
}
@ -69,39 +69,41 @@ class App extends Component {
servers: PropTypes.object.isRequired,
oidc: PropTypes.array,
whiteLabelling: PropTypes.object,
}
};
static defaultProps = {
whiteLabelling: {},
oidc: [],
}
};
constructor(props) {
super(props)
super(props);
//
const defaultButtons = getDefaultToolbarButtons(this.props.routerBasename)
const buttonsAction = OHIF.redux.actions.setAvailableButtons(defaultButtons)
const defaultButtons = getDefaultToolbarButtons(this.props.routerBasename);
const buttonsAction = OHIF.redux.actions.setAvailableButtons(
defaultButtons
);
store.dispatch(buttonsAction)
store.dispatch(buttonsAction);
if (this.props.oidc.length) {
const firstOpenIdClient = this.props.oidc[0]
const firstOpenIdClient = this.props.oidc[0];
this.userManager = getUserManagerForOpenIdConnectClient(
store,
firstOpenIdClient
)
);
}
handleServers(this.props.servers)
handleServers(this.props.servers);
initWebWorkers(
this.props.routerBasename,
this.props.relativeWebWorkerScriptsPath
)
);
}
render() {
const userManager = this.userManager
const userManager = this.userManager;
if (userManager) {
return (
@ -114,7 +116,7 @@ class App extends Component {
</Router>
</OidcProvider>
</Provider>
)
);
}
return (
@ -125,8 +127,8 @@ class App extends Component {
</WhiteLabellingContext.Provider>
</Router>
</Provider>
)
);
}
}
export default App
export default App;

View File

@ -6,7 +6,7 @@ import { CallbackComponent } from 'redux-oidc';
class CallbackPage extends Component {
static propTypes = {
userManager: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
history: PropTypes.object.isRequired,
};
render() {

View File

@ -1,19 +1,19 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import React, { Component } from 'react';
import PropTypes from 'prop-types';
// import asyncComponent from './components/AsyncComponent.js'
import IHEInvokeImageDisplay from './routes/IHEInvokeImageDisplay.js'
import ViewerRouting from './routes/ViewerRouting.js'
import StudyListRouting from './studylist/StudyListRouting.js'
import StandaloneRouting from './routes/StandaloneRouting.js'
import CallbackPage from './CallbackPage.js'
import { withRouter } from 'react-router'
import { Route, Switch } from 'react-router-dom'
import { connect } from 'react-redux'
import { ViewerbaseDragDropContext } from 'react-viewerbase'
import IHEInvokeImageDisplay from './routes/IHEInvokeImageDisplay.js';
import ViewerRouting from './routes/ViewerRouting.js';
import StudyListRouting from './studylist/StudyListRouting.js';
import StandaloneRouting from './routes/StandaloneRouting.js';
import CallbackPage from './CallbackPage.js';
import { withRouter } from 'react-router';
import { Route, Switch } from 'react-router-dom';
import { connect } from 'react-redux';
import { ViewerbaseDragDropContext } from 'react-viewerbase';
import './OHIFStandaloneViewer.css'
import './variables.css'
import './theme-tide.css'
import './OHIFStandaloneViewer.css';
import './variables.css';
import './theme-tide.css';
// Dynamic Import Routes (CodeSplitting)
// const IHEInvokeImageDisplay = asyncComponent(() =>
@ -29,35 +29,35 @@ import './theme-tide.css'
// const CallbackPage = asyncComponent(() => import('./CallbackPage.js'))
//
const reload = () => window.location.reload()
const reload = () => window.location.reload();
class OHIFStandaloneViewer extends Component {
static propTypes = {
history: PropTypes.object.isRequired,
user: PropTypes.object,
}
};
componentDidMount() {
this.unlisten = this.props.history.listen((location, action) => {
if (this.props.setContext) {
this.props.setContext(window.location.pathname)
this.props.setContext(window.location.pathname);
}
})
});
}
componentWillUnmount() {
this.unlisten()
this.unlisten();
}
render() {
const { user, userManager } = this.props
const { user, userManager } = this.props;
const userNotLoggedIn = userManager && (!user || user.expired)
const userNotLoggedIn = userManager && (!user || user.expired);
if (userNotLoggedIn) {
const pathname = this.props.location.pathname
const pathname = this.props.location.pathname;
if (pathname !== '/callback') {
sessionStorage.setItem('ohif-redirect-to', pathname)
sessionStorage.setItem('ohif-redirect-to', pathname);
}
return (
@ -70,13 +70,13 @@ class OHIFStandaloneViewer extends Component {
/>
<Route
component={() => {
userManager.signinRedirect()
userManager.signinRedirect();
return null
return null;
}}
/>
</Switch>
)
);
}
return (
@ -97,21 +97,21 @@ class OHIFStandaloneViewer extends Component {
/>
<Route render={() => <div> Sorry, this page does not exist. </div>} />
</Switch>
)
);
}
}
const mapStateToProps = state => {
return {
user: state.oidc.user,
}
}
};
};
const ConnectedOHIFStandaloneViewer = connect(
mapStateToProps,
null
)(OHIFStandaloneViewer)
)(OHIFStandaloneViewer);
export default ViewerbaseDragDropContext(
withRouter(ConnectedOHIFStandaloneViewer)
)
);

View File

@ -2,7 +2,7 @@ import OHIFLogo from './components/OHIFLogo/OHIFLogo.js';
import React from 'react';
const defaultContextValues = {
logoComponent: OHIFLogo()
logoComponent: OHIFLogo(),
};
const WhiteLabellingContext = React.createContext(defaultContextValues);

View File

@ -2,4 +2,4 @@ export default {
webWorkerManager: {
initialize: jest.fn(),
},
}
};

View File

@ -1,3 +1,3 @@
// https://jestjs.io/docs/en/webpack#handling-static-assets
module.exports = 'test-file-stub'
module.exports = 'test-file-stub';

View File

@ -1,4 +1,4 @@
const createUserManager = () => jest.fn().mockReturnValue({})
const loadUser = () => jest.fn()
const createUserManager = () => jest.fn().mockReturnValue({});
const loadUser = () => jest.fn();
export { createUserManager, loadUser }
export { createUserManager, loadUser };

View File

@ -1,9 +1,9 @@
const _ = require('lodash')
const originalConsoleError = console.error
const _ = require('lodash');
const originalConsoleError = console.error;
// JSDom's CSS Parser has limited support for certain features
// This supresses error warnings caused by it
console.error = function(msg) {
if (_.startsWith(msg, 'Error: Could not parse CSS stylesheet')) return
originalConsoleError(msg)
}
if (_.startsWith(msg, 'Error: Could not parse CSS stylesheet')) return;
originalConsoleError(msg);
};

View File

@ -4,34 +4,34 @@
* Link: https://serverless-stack.com/chapters/code-splitting-in-create-react-app.html
*/
import React, { Component } from 'react'
import React, { Component } from 'react';
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props)
super(props);
this.state = {
component: null,
}
};
}
async componentDidMount() {
// Add dynamically loaded component to state
const { default: component } = await importComponent()
const { default: component } = await importComponent();
this.setState({
component: component,
})
});
}
render() {
const C = this.state.component
const C = this.state.component;
// Render the loaded component, or null
return C ? <C {...this.props} /> : null
return C ? <C {...this.props} /> : null;
}
}
return AsyncComponent
return AsyncComponent;
}

View File

@ -1,18 +1,18 @@
import { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import SimpleDialog from '../SimpleDialog/SimpleDialog.js'
import { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import SimpleDialog from '../SimpleDialog/SimpleDialog.js';
import bounding from '../../lib/utils/bounding.js'
import { getDialogStyle } from './../Labelling/labellingPositionUtils.js'
import bounding from '../../lib/utils/bounding.js';
import { getDialogStyle } from './../Labelling/labellingPositionUtils.js';
import './EditDescriptionDialog.css'
import './EditDescriptionDialog.css';
export default class EditDescriptionDialog extends Component {
static defaultProps = {
componentRef: React.createRef(),
componentStyle: {},
}
};
static propTypes = {
measurementData: PropTypes.object.isRequired,
@ -20,32 +20,32 @@ export default class EditDescriptionDialog extends Component {
componentRef: PropTypes.object,
componentStyle: PropTypes.object,
onUpdate: PropTypes.func.isRequired,
}
};
constructor(props) {
super(props)
super(props);
this.state = {
description: props.measurementData.description || '',
}
};
this.mainElement = React.createRef()
this.mainElement = React.createRef();
}
componentDidMount = () => {
bounding(this.mainElement)
}
bounding(this.mainElement);
};
componentDidUpdate(prevProps) {
if (this.props.description !== prevProps.description) {
this.setState({
description: this.props.description,
})
});
}
}
render() {
const style = getDialogStyle(this.props.componentStyle)
const style = getDialogStyle(this.props.componentStyle);
return (
<SimpleDialog
@ -65,18 +65,18 @@ export default class EditDescriptionDialog extends Component {
onChange={this.handleChange}
/>
</SimpleDialog>
)
);
}
onClose = () => {
this.props.onCancel()
}
this.props.onCancel();
};
onConfirm = () => {
this.props.onUpdate(this.state.description)
}
this.props.onUpdate(this.state.description);
};
handleChange = event => {
this.setState({ description: event.target.value })
}
this.setState({ description: event.target.value });
};
}

View File

@ -1,3 +1,3 @@
import Header from './Header'
import Header from './Header';
export default Header
export default Header;

View File

@ -1,12 +1,12 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import cloneDeep from 'lodash.clonedeep'
import cloneDeep from 'lodash.clonedeep';
import EditDescriptionDialog from './../EditDescriptionDialog/EditDescriptionDialog.js'
import LabellingFlow from './LabellingFlow.js'
import EditDescriptionDialog from './../EditDescriptionDialog/EditDescriptionDialog.js';
import LabellingFlow from './LabellingFlow.js';
import './LabellingManager.css'
import './LabellingManager.css';
export default class LabellingManager extends Component {
static propTypes = {
@ -20,24 +20,24 @@ export default class LabellingManager extends Component {
editLocation: PropTypes.bool,
editDescription: PropTypes.bool,
editDescriptionOnDialog: PropTypes.bool,
}
};
static defaultProps = {
skipAddLabelButton: false,
editLocation: false,
editDescription: false,
editDescriptionOnDialog: false,
}
};
constructor(props) {
super(props)
super(props);
const measurementData = cloneDeep(props.measurementData)
this.treatMeasurementData(measurementData)
const measurementData = cloneDeep(props.measurementData);
this.treatMeasurementData(measurementData);
let editLocation = props.editLocation
let editLocation = props.editLocation;
if (!props.editDescription && !props.editLocation) {
editLocation = true
editLocation = true;
}
this.state = {
@ -47,19 +47,19 @@ export default class LabellingManager extends Component {
editDescription: props.editDescription,
editDescriptionOnDialog: props.editDescriptionOnDialog,
measurementData: measurementData,
}
};
}
componentDidMount = () => {
document.addEventListener('touchstart', this.onTouchStart)
}
document.addEventListener('touchstart', this.onTouchStart);
};
componentWillUnmount = () => {
document.removeEventListener('touchstart', this.onTouchStart)
}
document.removeEventListener('touchstart', this.onTouchStart);
};
render() {
return this.getRenderComponent()
return this.getRenderComponent();
}
getRenderComponent = () => {
@ -68,7 +68,7 @@ export default class LabellingManager extends Component {
editDescription,
editDescriptionOnDialog,
measurementData,
} = this.state
} = this.state;
if (editDescriptionOnDialog) {
return (
@ -79,7 +79,7 @@ export default class LabellingManager extends Component {
componentStyle={this.state.componentStyle}
measurementData={measurementData}
/>
)
);
}
if (editLocation || editDescription) {
@ -88,43 +88,43 @@ export default class LabellingManager extends Component {
{...this.props}
componentStyle={this.state.componentStyle}
/>
)
);
}
}
};
treatMeasurementData = measurementData => {
const { editDescription, editLocation } = this.props
const { editDescription, editLocation } = this.props;
if (editDescription) {
measurementData.description = undefined
measurementData.description = undefined;
}
if (editLocation) {
measurementData.location = undefined
measurementData.location = undefined;
}
}
};
responseDialogUpdate = response => {
this.props.updateLabelling({
response,
})
this.props.labellingDoneCallback()
}
});
this.props.labellingDoneCallback();
};
descriptionDialogUpdate = description => {
this.props.updateLabelling({
description,
})
this.props.labellingDoneCallback()
}
});
this.props.labellingDoneCallback();
};
}
function getComponentPosition(eventData) {
const {
event: { clientX: left, clientY: top },
} = eventData
} = eventData;
return {
left,
top,
}
};
}

View File

@ -1,20 +1,20 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { CSSTransition } from 'react-transition-group'
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { CSSTransition } from 'react-transition-group';
import './LabellingTransition.css'
import './LabellingTransition.css';
// If these variables changes, CSS must be updated
const transitionDuration = 500
const transitionClassName = 'labelling'
const transitionOnAppear = true
const transitionDuration = 500;
const transitionClassName = 'labelling';
const transitionOnAppear = true;
export default class LabellingTransition extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
displayComponent: PropTypes.bool.isRequired,
onTransitionExit: PropTypes.func.isRequired,
}
};
render() {
return (
<CSSTransition
@ -26,6 +26,6 @@ export default class LabellingTransition extends Component {
>
{this.props.children}
</CSSTransition>
)
);
}
}

View File

@ -27,13 +27,13 @@ const items = [
'Spleen',
'Stomach',
'Subcutaneous',
]
];
const OHIFLabellingData = items.map(item => {
return {
label: item,
value: item,
}
})
};
});
export default OHIFLabellingData
export default OHIFLabellingData;

View File

@ -1,53 +1,53 @@
import cornerstone from 'cornerstone-core'
import cornerstone from 'cornerstone-core';
const buttonSize = {
width: 96,
height: 28,
}
};
export function getAddLabelButtonStyle(measurementData, eventData) {
const { start, end } = measurementData.handles
const { client } = eventData.currentPoints
const clientStart = cornerstone.pixelToCanvas(eventData.element, start)
const clientEnd = cornerstone.pixelToCanvas(eventData.element, end)
const canvasOffSetLeft = client.x - clientStart.x
const canvasOffSetTop = client.y - clientStart.y
const { start, end } = measurementData.handles;
const { client } = eventData.currentPoints;
const clientStart = cornerstone.pixelToCanvas(eventData.element, start);
const clientEnd = cornerstone.pixelToCanvas(eventData.element, end);
const canvasOffSetLeft = client.x - clientStart.x;
const canvasOffSetTop = client.y - clientStart.y;
const position = {
left: clientEnd.x + canvasOffSetLeft,
top: clientEnd.y + canvasOffSetTop,
}
};
if (start.y > end.y) {
position.top -= buttonSize.height
position.top -= buttonSize.height;
}
if (start.x > end.x) {
position.left -= buttonSize.width
position.left -= buttonSize.width;
}
return position
return position;
}
export function getDialogStyle(componentStyle) {
const style = Object.assign({}, componentStyle)
const style = Object.assign({}, componentStyle);
const dialogProps = {
width: 320,
height: 230,
}
};
// Get max values to avoid position out of the screen
const maxLeft = window.innerWidth - dialogProps.width
const maxTop = window.innerHeight - dialogProps.height
const maxLeft = window.innerWidth - dialogProps.width;
const maxTop = window.innerHeight - dialogProps.height;
// Positioning the dialog with its center on the click event
style.left -= dialogProps.width / 2
style.top -= dialogProps.height / 2
style.left -= dialogProps.width / 2;
style.top -= dialogProps.height / 2;
if (style.left > maxLeft) {
style.left = maxLeft
style.left = maxLeft;
}
if (style.top > maxTop) {
style.top = maxTop
style.top = maxTop;
}
return style
return style;
}

View File

@ -1,14 +1,14 @@
import { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import './SimpleDialog.css'
import './SimpleDialog.css';
class SimpleDialog extends Component {
static defaultProps = {
componentStyle: {},
rootClass: '',
}
};
render() {
return (
@ -43,7 +43,7 @@ class SimpleDialog extends Component {
</div>
</form>
</div>
)
);
}
}
@ -51,6 +51,6 @@ SimpleDialog.propTypes = {
headerTitle: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onConfirm: PropTypes.func.isRequired,
}
};
export default SimpleDialog
export default SimpleDialog;

View File

@ -1,43 +1,42 @@
import { Component } from "react";
import PropTypes from "prop-types";
import { Component } from 'react';
import PropTypes from 'prop-types';
import OHIF from 'ohif-core';
class StudyLoadingMonitor extends Component {
static propTypes = {
studies: PropTypes.array.isRequired,
setStudyLoadingProgress: PropTypes.func.isRequired,
clearStudyLoadingProgress: PropTypes.func.isRequired,
static propTypes = {
studies: PropTypes.array.isRequired,
setStudyLoadingProgress: PropTypes.func.isRequired,
clearStudyLoadingProgress: PropTypes.func.isRequired,
};
componentDidMount() {
// TODO: This is pretty ugly. The thing is that the StudyLoadingListener
// needs to update the Redux store, but shouldn't know that it exists.
// I am therefore passing in some functions to update the store instead,
// but this should definitely be cleaned up somehow.
const options = {
_setProgressData: (progressId, progressData) => {
this.props.setStudyLoadingProgress(progressId, progressData);
},
_clearProgressById: progressId => {
this.props.clearStudyLoadingProgress(progressId);
},
};
componentDidMount() {
// TODO: This is pretty ugly. The thing is that the StudyLoadingListener
// needs to update the Redux store, but shouldn't know that it exists.
// I am therefore passing in some functions to update the store instead,
// but this should definitely be cleaned up somehow.
const options = {
_setProgressData: (progressId, progressData) => {
this.props.setStudyLoadingProgress(progressId, progressData)
},
_clearProgressById: (progressId) => {
this.props.clearStudyLoadingProgress(progressId)
}
}
const { StudyLoadingListener } = OHIF.classes;
this.studyLoadingListener = StudyLoadingListener.getInstance(options);
this.studyLoadingListener.clear();
this.studyLoadingListener.addStudies(this.props.studies);
}
const { StudyLoadingListener } = OHIF.classes;
this.studyLoadingListener = StudyLoadingListener.getInstance(options);
this.studyLoadingListener.clear();
this.studyLoadingListener.addStudies(this.props.studies);
}
render() {
return null;
}
render() {
return null;
}
componentWillUnmount() {
// Destroy stack loading listeners when we close the viewer
this.studyLoadingListener.clear();
}
componentWillUnmount() {
// Destroy stack loading listeners when we close the viewer
this.studyLoadingListener.clear();
}
}
export default StudyLoadingMonitor;

View File

@ -1,30 +1,29 @@
import { Component } from "react";
import PropTypes from "prop-types";
import { Component } from 'react';
import PropTypes from 'prop-types';
import OHIF from 'ohif-core';
const { StudyPrefetcher } = OHIF.classes;
class StudyPrefetcherComponent extends Component {
static propTypes = {
studies: PropTypes.array,
};
static propTypes = {
studies: PropTypes.array,
};
componentDidMount() {
const { studies } = this.props;
componentDidMount() {
const { studies } = this.props;
this.studyPrefetcher = StudyPrefetcher.getInstance();
this.studyPrefetcher.setStudies(studies);
}
this.studyPrefetcher = StudyPrefetcher.getInstance();
this.studyPrefetcher.setStudies(studies);
}
render() {
return null;
}
render() {
return null;
}
componentWillUnmount() {
// Stop prefetching when we close the viewer
this.studyPrefetcher.destroy();
}
componentWillUnmount() {
// Stop prefetching when we close the viewer
this.studyPrefetcher.destroy();
}
}
export default StudyPrefetcherComponent;

View File

@ -1,37 +1,37 @@
import dicomParser from 'dicom-parser'
import cornerstone from 'cornerstone-core'
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import OHIF from 'ohif-core'
import version from './version.js'
import dicomParser from 'dicom-parser';
import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import OHIF from 'ohif-core';
import version from './version.js';
window.info = {
version,
homepage: `${process.env.PUBLIC_URL}`,
}
};
// For debugging
window.cornerstone = cornerstone
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader
window.cornerstone = cornerstone;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
cornerstoneWADOImageLoader.external.cornerstone = cornerstone
cornerstoneWADOImageLoader.external.dicomParser = dicomParser
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState()
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return
return;
}
return state.oidc.user.access_token
}
return state.oidc.user.access_token;
};
cornerstoneWADOImageLoader.configure({
beforeSend: function(xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader()
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization)
xhr.setRequestHeader('Authorization', headers.Authorization);
}
},
})
});

View File

@ -2,15 +2,15 @@ import { connect } from 'react-redux';
import FlexboxLayout from './FlexboxLayout';
const mapStateToProps = state => {
return {
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen,
};
return {
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen,
};
};
const ConnectedFlexboxLayout = connect(
mapStateToProps,
null
mapStateToProps,
null
)(FlexboxLayout);
export default ConnectedFlexboxLayout;

View File

@ -4,7 +4,7 @@ import { setUserPreferencesModalOpen } from '../redux/actions.js';
const mapStateToProps = state => {
return {
isOpen: state.ui.userPreferencesModalOpen
isOpen: state.ui.userPreferencesModalOpen,
};
};
@ -12,7 +12,7 @@ const mapDispatchToProps = dispatch => {
return {
openUserPreferencesModal: () => {
dispatch(setUserPreferencesModalOpen(true));
}
},
};
};

View File

@ -1,24 +1,24 @@
import { connect } from 'react-redux'
import LabellingOverlay from './LabellingOverlay'
import { connect } from 'react-redux';
import LabellingOverlay from './LabellingOverlay';
const mapStateToProps = state => {
if (!state.ui || !state.ui.labelling) {
return {
visible: false,
}
};
}
const labellingFlowData = state.ui.labelling
const labellingFlowData = state.ui.labelling;
return {
visible: false,
...labellingFlowData,
}
}
};
};
const ConnectedLabellingOverlay = connect(
mapStateToProps,
null
)(LabellingOverlay)
)(LabellingOverlay);
export default ConnectedLabellingOverlay
export default ConnectedLabellingOverlay;

View File

@ -1,41 +1,41 @@
import { connect } from 'react-redux';
import { LayoutButton } from 'react-viewerbase';
import OHIF from 'ohif-core'
import OHIF from 'ohif-core';
const { setLayout } = OHIF.redux.actions;
const mapStateToProps = state => {
return {
currentLayout: state.viewports.layout,
};
return {
currentLayout: state.viewports.layout,
};
};
const mapDispatchToProps = dispatch => {
return {
// TODO: Change if layout switched becomes more complex
onChange: selectedCell => {
let viewports = [];
const rows = selectedCell.row + 1;
const columns = selectedCell.col + 1;
const numViewports = rows * columns;
for (let i = 0; i < numViewports; i++) {
viewports.push({
height: `${100 / rows}%`,
width: `${100 / columns}%`
});
}
const layout = {
viewports
};
return {
// TODO: Change if layout switched becomes more complex
onChange: selectedCell => {
let viewports = [];
const rows = selectedCell.row + 1;
const columns = selectedCell.col + 1;
const numViewports = rows * columns;
for (let i = 0; i < numViewports; i++) {
viewports.push({
height: `${100 / rows}%`,
width: `${100 / columns}%`,
});
}
const layout = {
viewports,
};
dispatch(setLayout(layout))
}
};
dispatch(setLayout(layout));
},
};
};
const ConnectedLayoutButton = connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(LayoutButton);
export default ConnectedLayoutButton;

View File

@ -27,7 +27,7 @@ const mapStateToProps = state => {
layout: state.viewports.layout,
activeViewportIndex: state.viewports.activeViewportIndex,
availablePlugins,
defaultPlugin
defaultPlugin,
};
};

View File

@ -1,41 +1,41 @@
import { connect } from 'react-redux'
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 { connect } from 'react-redux';
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';
const { setViewportSpecificData } = OHIF.redux.actions
const { MeasurementApi } = OHIF.measurements
const { setViewportSpecificData } = OHIF.redux.actions;
const { MeasurementApi } = OHIF.measurements;
function groupBy(list, props) {
return list.reduce((a, b) => {
;(a[b[props]] = a[b[props]] || []).push(b)
return a
}, {})
(a[b[props]] = a[b[props]] || []).push(b);
return a;
}, {});
}
function getAllTools() {
const config = OHIF.measurements.MeasurementApi.getConfiguration()
let tools = []
const config = OHIF.measurements.MeasurementApi.getConfiguration();
let tools = [];
config.measurementTools.forEach(
toolGroup => (tools = tools.concat(toolGroup.childTools))
)
);
return tools
return tools;
}
function getMeasurementText(measurementData) {
const { location, description } = measurementData
let text = '...'
const { location, description } = measurementData;
let text = '...';
if (location) {
text = location
text = location;
if (description) {
text += `(${description})`
text += `(${description})`;
}
}
return text
return text;
}
function getDataForEachMeasurementNumber(
@ -43,54 +43,54 @@ function getDataForEachMeasurementNumber(
timepoints,
displayFunction
) {
const data = []
const data = [];
// on each measurement number we should get each measurement data by available timepoint
measurementNumberList.forEach(measurement => {
timepoints.forEach(timepoint => {
const eachData = {
displayText: '...',
}
};
if (measurement.timepointId === timepoint.timepointId) {
eachData.displayText = displayFunction(measurement)
eachData.displayText = displayFunction(measurement);
}
data.push(eachData)
})
})
data.push(eachData);
});
});
return data
return data;
}
function convertMeasurementsToTableData(toolCollections, timepoints) {
const config = OHIF.measurements.MeasurementApi.getConfiguration()
const toolGroups = config.measurementTools
const tools = getAllTools()
const config = OHIF.measurements.MeasurementApi.getConfiguration();
const toolGroups = config.measurementTools;
const tools = getAllTools();
const tableMeasurements = toolGroups.map(toolGroup => {
return {
groupName: toolGroup.name,
groupId: toolGroup.id,
measurements: [],
}
})
};
});
Object.keys(toolCollections).forEach(toolId => {
const toolMeasurements = toolCollections[toolId]
const tool = tools.find(tool => tool.id === toolId)
const { displayFunction } = tool.options.measurementTable
const toolMeasurements = toolCollections[toolId];
const tool = tools.find(tool => tool.id === toolId);
const { displayFunction } = tool.options.measurementTable;
// Group by measurementNumber so we can display then all in the same line
const groupedMeasurements = groupBy(toolMeasurements, 'measurementNumber')
const groupedMeasurements = groupBy(toolMeasurements, 'measurementNumber');
Object.keys(groupedMeasurements).forEach(groupedMeasurementsIndex => {
const measurementNumberList =
groupedMeasurements[groupedMeasurementsIndex]
const measurementData = measurementNumberList[0]
groupedMeasurements[groupedMeasurementsIndex];
const measurementData = measurementNumberList[0];
const {
measurementNumber,
lesionNamingNumber,
toolType,
} = measurementData
const measurementId = measurementData._id
} = measurementData;
const measurementId = measurementData._id;
//check if all measurements with same measurementNumber will have same LABEL
const tableMeasurement = {
@ -109,30 +109,30 @@ function convertMeasurementsToTableData(toolCollections, timepoints) {
timepoints,
displayFunction
),
}
};
// find the group object for the tool
const toolGroupMeasurements = tableMeasurements.find(group => {
return group.groupId === tool.toolGroup
})
return group.groupId === tool.toolGroup;
});
// inject the new measurement for this measurementNumer
toolGroupMeasurements.measurements.push(tableMeasurement)
})
})
toolGroupMeasurements.measurements.push(tableMeasurement);
});
});
// Sort measurements by lesion naming number
tableMeasurements.forEach(tm => {
tm.measurements.sort((m1, m2) =>
m1.lesionNamingNumber > m2.lesionNamingNumber ? 1 : -1
)
})
);
});
return tableMeasurements
return tableMeasurements;
}
function convertTimepointsToTableData(timepoints) {
if (!timepoints || !timepoints.length) {
return []
return [];
}
return [
@ -140,11 +140,11 @@ function convertTimepointsToTableData(timepoints) {
label: 'Study date:',
date: moment(timepoints[0].latestDate).format('DD-MMM-YY'),
},
]
];
}
const mapStateToProps = state => {
const { timepoints, measurements } = state.timepointManager
const { timepoints, measurements } = state.timepointManager;
return {
timepoints: convertTimepointsToTableData(timepoints),
measurementCollection: convertMeasurementsToTableData(
@ -153,22 +153,22 @@ const mapStateToProps = state => {
),
timepointManager: state.timepointManager,
viewports: state.viewports,
}
}
};
};
const mapDispatchToProps = dispatch => {
return {
dispatchRelabel: (event, measurementData, viewportsState) => {
const activeViewportIndex =
(viewportsState && viewportsState.activeViewportIndex) || 0
(viewportsState && viewportsState.activeViewportIndex) || 0;
const enabledElements = cornerstone.getEnabledElements()
const enabledElements = cornerstone.getEnabledElements();
if (!enabledElements || enabledElements.length <= activeViewportIndex) {
OHIF.log.error('Failed to find the enabled element')
return
OHIF.log.error('Failed to find the enabled element');
return;
}
const { element } = enabledElements[activeViewportIndex]
const { element } = enabledElements[activeViewportIndex];
const eventData = {
event: {
@ -176,33 +176,33 @@ const mapDispatchToProps = dispatch => {
clientY: event.clientY,
},
element,
}
};
const { toolType, measurementId } = measurementData
const { toolType, measurementId } = measurementData;
const tool = MeasurementApi.Instance.tools[toolType].find(measurement => {
return measurement._id === measurementId
})
return measurement._id === measurementId;
});
const options = {
skipAddLabelButton: true,
editLocation: true,
}
};
// Clone the tool not to set empty location initially
const toolForLocation = Object.assign({}, tool, { location: null })
getMeasurementLocationCallback(eventData, toolForLocation, options)
const toolForLocation = Object.assign({}, tool, { location: null });
getMeasurementLocationCallback(eventData, toolForLocation, options);
},
dispatchEditDescription: (event, measurementData, viewportsState) => {
const activeViewportIndex =
(viewportsState && viewportsState.activeViewportIndex) || 0
(viewportsState && viewportsState.activeViewportIndex) || 0;
const enabledElements = cornerstone.getEnabledElements()
const enabledElements = cornerstone.getEnabledElements();
if (!enabledElements || enabledElements.length <= activeViewportIndex) {
OHIF.log.error('Failed to find the enabled element')
return
OHIF.log.error('Failed to find the enabled element');
return;
}
const { element } = enabledElements[activeViewportIndex]
const { element } = enabledElements[activeViewportIndex];
const eventData = {
event: {
@ -210,18 +210,18 @@ const mapDispatchToProps = dispatch => {
clientY: event.clientY,
},
element,
}
};
const { toolType, measurementId } = measurementData
const { toolType, measurementId } = measurementData;
const tool = MeasurementApi.Instance.tools[toolType].find(measurement => {
return measurement._id === measurementId
})
return measurement._id === measurementId;
});
const options = {
editDescriptionOnDialog: true,
}
};
getMeasurementLocationCallback(eventData, tool, options)
getMeasurementLocationCallback(eventData, tool, options);
},
dispatchJumpToRowItem: (
measurementData,
@ -235,40 +235,40 @@ const mapDispatchToProps = dispatch => {
timepointManagerState,
dispatch,
options
)
);
actionData.viewportSpecificData.forEach(viewportSpecificData => {
const { viewportIndex, displaySet } = viewportSpecificData
const { viewportIndex, displaySet } = viewportSpecificData;
dispatch(setViewportSpecificData(viewportIndex, displaySet))
})
dispatch(setViewportSpecificData(viewportIndex, displaySet));
});
const { toolType, measurementNumber } = measurementData
const measurementApi = MeasurementApi.Instance
const { toolType, measurementNumber } = measurementData;
const measurementApi = MeasurementApi.Instance;
Object.keys(measurementApi.tools).forEach(toolType => {
const measurements = measurementApi.tools[toolType]
const measurements = measurementApi.tools[toolType];
measurements.forEach(measurement => {
measurement.active = false
})
})
measurement.active = false;
});
});
const measurementsToActive = measurementApi.tools[toolType].filter(
measurement => {
return measurement.measurementNumber === measurementNumber
return measurement.measurementNumber === measurementNumber;
}
)
);
measurementsToActive.forEach(measurementToActive => {
measurementToActive.active = true
})
measurementToActive.active = true;
});
measurementApi.syncMeasurementsAndToolData()
measurementApi.syncMeasurementsAndToolData();
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element)
})
cornerstone.updateImage(enabledElement.element);
});
// Needs to update viewports.layout state to set layout
//const layout = actionData.layout;
@ -284,8 +284,8 @@ const mapDispatchToProps = dispatch => {
// (later): Needs to set some property on state.extensions.cornerstone to synchronize viewport scrolling
},
}
}
};
};
const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
return {
@ -298,37 +298,37 @@ const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
// TODO: Tooltype should be on the level below? This should
// provide the entire row item?
const viewportsState = propsFromState.viewports
const timepointManagerState = propsFromState.timepointManager
const viewportsState = propsFromState.viewports;
const timepointManagerState = propsFromState.timepointManager;
// TODO: invertViewportTimepointsOrder should be stored in / read from user preferences
// TODO: childToolKey should come from the measurement table when it supports child tools
const options = {
invertViewportTimepointsOrder: false,
childToolKey: null,
}
};
propsFromDispatch.dispatchJumpToRowItem(
measurementData,
viewportsState,
timepointManagerState,
options
)
);
},
onRelabelClick: (event, measurementData) => {
const viewportsState = propsFromState.viewports
propsFromDispatch.dispatchRelabel(event, measurementData, viewportsState)
const viewportsState = propsFromState.viewports;
propsFromDispatch.dispatchRelabel(event, measurementData, viewportsState);
},
onEditDescriptionClick: (event, measurementData) => {
const viewportsState = propsFromState.viewports
const viewportsState = propsFromState.viewports;
propsFromDispatch.dispatchEditDescription(
event,
measurementData,
viewportsState
)
);
},
onDeleteClick: (event, measurementData) => {
const { MeasurementHandlers } = OHIF.measurements
const { MeasurementHandlers } = OHIF.measurements;
MeasurementHandlers.onRemoved({
detail: {
@ -339,15 +339,15 @@ const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
measurementNumber: measurementData.measurementNumber,
},
},
})
});
},
}
}
};
};
const ConnectedMeasurementTable = connect(
mapStateToProps,
mapDispatchToProps,
mergeProps
)(MeasurementTable)
)(MeasurementTable);
export default ConnectedMeasurementTable
export default ConnectedMeasurementTable;

View File

@ -6,35 +6,35 @@ import cloneDeep from 'lodash.clonedeep';
// - Determine in which display set is active from Redux (activeViewportIndex and layout viewportData)
// - Pass in errors and stack loading progress from Redux
const mapStateToProps = (state, ownProps) => {
// If we know that the stack loading progress details have changed,
// we can try to update the component state so that the thumbnail
// progress bar is updated
const stackLoadingProgressMap = state.loading.progress;
const studiesWithLoadingData = cloneDeep(ownProps.studies);
// If we know that the stack loading progress details have changed,
// we can try to update the component state so that the thumbnail
// progress bar is updated
const stackLoadingProgressMap = state.loading.progress;
const studiesWithLoadingData = cloneDeep(ownProps.studies);
studiesWithLoadingData.forEach(study => {
study.thumbnails.forEach(data => {
const { displaySetInstanceUid } = data
const stackId = `StackProgress:${displaySetInstanceUid}`;
const stackProgressData = stackLoadingProgressMap[stackId];
studiesWithLoadingData.forEach(study => {
study.thumbnails.forEach(data => {
const { displaySetInstanceUid } = data;
const stackId = `StackProgress:${displaySetInstanceUid}`;
const stackProgressData = stackLoadingProgressMap[stackId];
let stackPercentComplete = 0;
if (stackProgressData) {
stackPercentComplete = stackProgressData.percentComplete;
}
let stackPercentComplete = 0;
if (stackProgressData) {
stackPercentComplete = stackProgressData.percentComplete;
}
data.stackPercentComplete = stackPercentComplete;
})
data.stackPercentComplete = stackPercentComplete;
});
});
return {
studies: studiesWithLoadingData
};
return {
studies: studiesWithLoadingData,
};
};
const ConnectedStudyBrowser = connect(
mapStateToProps,
null
mapStateToProps,
null
)(StudyBrowser);
export default ConnectedStudyBrowser;

View File

@ -1,23 +1,26 @@
import { connect } from 'react-redux';
import StudyLoadingMonitor from '../components/StudyLoadingMonitor.js';
import OHIF from 'ohif-core'
import OHIF from 'ohif-core';
const { setStudyLoadingProgress, clearStudyLoadingProgress } = OHIF.redux.actions;
const {
setStudyLoadingProgress,
clearStudyLoadingProgress,
} = OHIF.redux.actions;
const mapDispatchToProps = dispatch => {
return {
setStudyLoadingProgress: (progressId, progressData) => {
dispatch(setStudyLoadingProgress(progressId, progressData))
},
clearStudyLoadingProgress: progressId => {
dispatch(clearStudyLoadingProgress(progressId))
}
};
return {
setStudyLoadingProgress: (progressId, progressData) => {
dispatch(setStudyLoadingProgress(progressId, progressData));
},
clearStudyLoadingProgress: progressId => {
dispatch(clearStudyLoadingProgress(progressId));
},
};
};
const ConnectedStudyLoadingMonitor = connect(
null,
mapDispatchToProps
null,
mapDispatchToProps
)(StudyLoadingMonitor);
export default ConnectedStudyLoadingMonitor;

View File

@ -1,34 +1,34 @@
import { connect } from 'react-redux'
import ToolContextMenu from './ToolContextMenu'
import { connect } from 'react-redux';
import ToolContextMenu from './ToolContextMenu';
const mapStateToProps = (state, ownProps) => {
if (!state.ui || !state.ui.contextMenu) {
return {
visible: false,
}
};
}
const { viewportIndex } = ownProps
const toolContextMenuData = state.ui.contextMenu[viewportIndex]
let availableTools
const { viewportIndex } = ownProps;
const toolContextMenuData = state.ui.contextMenu[viewportIndex];
let availableTools;
if (
state.extensions &&
state.extensions.cornerstone &&
state.extensions.cornerstone.availableTools
) {
availableTools = state.extensions.cornerstone.availableTools
availableTools = state.extensions.cornerstone.availableTools;
}
return {
...toolContextMenuData,
availableTools,
}
}
};
};
const ConnectedToolContextMenu = connect(
mapStateToProps,
null
)(ToolContextMenu)
)(ToolContextMenu);
export default ConnectedToolContextMenu
export default ConnectedToolContextMenu;

View File

@ -5,34 +5,38 @@ import { setLeftSidebarOpen, setRightSidebarOpen } from '../redux/actions.js';
const defaultPlugin = 'cornerstone';
const mapStateToProps = state => {
const { layout, viewportSpecificData, activeViewportIndex } = state.viewports;
const pluginInLayout = layout.viewports[activeViewportIndex] && layout.viewports[activeViewportIndex].plugin;
const pluginInViewportData = viewportSpecificData[activeViewportIndex] && viewportSpecificData[activeViewportIndex].plugin;
const pluginInActiveViewport = pluginInLayout || pluginInViewportData || defaultPlugin;
// const extensionData = state.extensions[pluginInActiveViewport];
const { layout, viewportSpecificData, activeViewportIndex } = state.viewports;
const pluginInLayout =
layout.viewports[activeViewportIndex] &&
layout.viewports[activeViewportIndex].plugin;
const pluginInViewportData =
viewportSpecificData[activeViewportIndex] &&
viewportSpecificData[activeViewportIndex].plugin;
const pluginInActiveViewport =
pluginInLayout || pluginInViewportData || defaultPlugin;
// const extensionData = state.extensions[pluginInActiveViewport];
return {
pluginId: pluginInActiveViewport,
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen
};
return {
pluginId: pluginInActiveViewport,
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen,
};
};
const mapDispatchToProps = dispatch => {
return {
setLeftSidebarOpen: state => {
dispatch(setLeftSidebarOpen(state))
},
setRightSidebarOpen: state => {
dispatch(setRightSidebarOpen(state))
}
};
return {
setLeftSidebarOpen: state => {
dispatch(setLeftSidebarOpen(state));
},
setRightSidebarOpen: state => {
dispatch(setRightSidebarOpen(state));
},
};
};
const ConnectedToolbarRow = connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(ToolbarRow);
export default ConnectedToolbarRow;

View File

@ -1,29 +1,29 @@
import { connect } from 'react-redux';
import { ToolbarSection } from 'react-viewerbase';
import OHIF from 'ohif-core'
import OHIF from 'ohif-core';
const { setToolActive } = OHIF.redux.actions;
const mapStateToProps = state => {
const activeButton = state.tools.buttons.find(tool => tool.active === true);
const activeButton = state.tools.buttons.find(tool => tool.active === true);
return {
buttons: state.tools.buttons,
activeCommand: activeButton && activeButton.command
};
return {
buttons: state.tools.buttons,
activeCommand: activeButton && activeButton.command,
};
};
const mapDispatchToProps = dispatch => {
return {
setToolActive: tool => {
dispatch(setToolActive(tool.command))
}
};
return {
setToolActive: tool => {
dispatch(setToolActive(tool.command));
},
};
};
const ConnectedToolbarSection = connect(
mapStateToProps,
mapDispatchToProps
mapStateToProps,
mapDispatchToProps
)(ToolbarSection);
export default ConnectedToolbarSection;

View File

@ -15,7 +15,7 @@ const mapStateToProps = state => {
: {},
hotKeysData: state.preferences[contextName]
? state.preferences[contextName].hotKeysData
: {}
: {},
};
};
@ -36,7 +36,7 @@ const mapDispatchToProps = dispatch => {
dispatch(setUserPreferences());
dispatch(setUserPreferencesModalOpen(false));
OHIF.hotkeysUtil.setHotkeys();
}
},
};
};

View File

@ -11,7 +11,7 @@ const mapDispatchToProps = dispatch => {
},
onMeasurementsUpdated: measurements => {
dispatch(setMeasurements(measurements));
}
},
};
};

View File

@ -1,44 +1,44 @@
import { connect } from 'react-redux'
import ViewerMain from './ViewerMain'
import OHIF from 'ohif-core'
import { connect } from 'react-redux';
import ViewerMain from './ViewerMain';
import OHIF from 'ohif-core';
const {
setViewportSpecificData,
clearViewportSpecificData,
setToolActive,
setActiveViewportSpecificData,
} = OHIF.redux.actions
} = OHIF.redux.actions;
const mapStateToProps = state => {
const { activeViewportIndex, layout, viewportSpecificData } = state.viewports
const { activeViewportIndex, layout, viewportSpecificData } = state.viewports;
return {
activeViewportIndex,
layout,
viewportSpecificData,
}
}
};
};
const mapDispatchToProps = dispatch => {
return {
setViewportSpecificData: (viewportIndex, data) => {
dispatch(setViewportSpecificData(viewportIndex, data))
dispatch(setViewportSpecificData(viewportIndex, data));
},
clearViewportSpecificData: () => {
dispatch(clearViewportSpecificData())
dispatch(clearViewportSpecificData());
},
setToolActive: tool => {
dispatch(setToolActive(tool))
dispatch(setToolActive(tool));
},
setActiveViewportSpecificData: viewport => {
dispatch(setActiveViewportSpecificData(viewport))
dispatch(setActiveViewportSpecificData(viewport));
},
}
}
};
};
const ConnectedViewerMain = connect(
mapStateToProps,
mapDispatchToProps
)(ViewerMain)
)(ViewerMain);
export default ConnectedViewerMain
export default ConnectedViewerMain;

View File

@ -1,19 +1,19 @@
import { connect } from 'react-redux'
import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js'
import { connect } from 'react-redux';
import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js';
const isActive = a => a.active === true
const isActive = a => a.active === true;
const mapStateToProps = state => {
const activeServer = state.servers.servers.find(isActive)
const activeServer = state.servers.servers.find(isActive);
return {
server: activeServer,
}
}
};
};
const ConnectedViewerRetrieveStudyData = connect(
mapStateToProps,
null
)(ViewerRetrieveStudyData)
)(ViewerRetrieveStudyData);
export default ConnectedViewerRetrieveStudyData
export default ConnectedViewerRetrieveStudyData;

View File

@ -1,38 +1,38 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js'
import ConnectedViewerMain from './ConnectedViewerMain.js'
import ConnectedMeasurementTable from './ConnectedMeasurementTable'
import './FlexboxLayout.css'
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
import ConnectedViewerMain from './ConnectedViewerMain.js';
import ConnectedMeasurementTable from './ConnectedMeasurementTable';
import './FlexboxLayout.css';
class FlexboxLayout extends Component {
static propTypes = {
studies: PropTypes.array.isRequired,
leftSidebarOpen: PropTypes.bool.isRequired,
rightSidebarOpen: PropTypes.bool.isRequired,
}
};
state = {
studiesForBrowser: [],
}
};
componentDidMount() {
const studiesForBrowser = this.getStudiesForBrowser()
const studiesForBrowser = this.getStudiesForBrowser();
this.setState({
studiesForBrowser,
})
});
}
getStudiesForBrowser = () => {
const { studies } = this.props
const { studies } = this.props;
// TODO[react]:
// - Add sorting of display sets
// - Add useMiddleSeriesInstanceAsThumbnail
// - Add showStackLoadingProgressBar option
return studies.map(study => {
const { studyInstanceUid } = study
const { studyInstanceUid } = study;
const thumbnails = study.displaySets.map(displaySet => {
const {
@ -41,12 +41,12 @@ class FlexboxLayout extends Component {
seriesNumber,
instanceNumber,
numImageFrames,
} = displaySet
} = displaySet;
let imageId
let imageId;
if (displaySet.images && displaySet.images.length) {
imageId = displaySet.images[0].getImageId()
imageId = displaySet.images[0].getImageId();
}
return {
@ -56,24 +56,24 @@ class FlexboxLayout extends Component {
seriesNumber,
instanceNumber,
numImageFrames,
}
})
};
});
return {
studyInstanceUid,
thumbnails,
}
})
}
};
});
};
render() {
let mainContentClassName = 'main-content'
let mainContentClassName = 'main-content';
if (this.props.leftSidebarOpen) {
mainContentClassName += ' sidebar-left-open'
mainContentClassName += ' sidebar-left-open';
}
if (this.props.rightSidebarOpen) {
mainContentClassName += ' sidebar-right-open'
mainContentClassName += ' sidebar-right-open';
}
// TODO[react]: Make ConnectedMeasurementTable extension with state.timepointManager
@ -101,8 +101,8 @@ class FlexboxLayout extends Component {
<ConnectedMeasurementTable />
</div>
</div>
)
);
}
}
export default FlexboxLayout
export default FlexboxLayout;

View File

@ -1,23 +1,23 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import LabellingManager from '../components/Labelling/LabellingManager'
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import LabellingManager from '../components/Labelling/LabellingManager';
class LabellingOverlay extends Component {
static propTypes = {
visible: PropTypes.bool.isRequired,
}
};
static defaultProps = {
visible: false,
}
};
render() {
if (!this.props.visible) {
return null
return null;
}
return <LabellingManager {...this.props} />
return <LabellingManager {...this.props} />;
}
}
export default LabellingOverlay
export default LabellingOverlay;

View File

@ -5,7 +5,7 @@ import './PluginSwitch.css';
class PluginSwitch extends Component {
static propTypes = {
buttons: PropTypes.array
buttons: PropTypes.array,
};
static defaultProps = {};

View File

@ -1,10 +1,10 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import cornerstone from 'cornerstone-core'
import cornerstoneTools from 'cornerstone-tools'
import getMeasurementLocationCallback from '../lib/getMeasurementLocationCallback'
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
import getMeasurementLocationCallback from '../lib/getMeasurementLocationCallback';
import './ToolContextMenu.css'
import './ToolContextMenu.css';
const toolTypes = [
'Angle',
@ -14,87 +14,87 @@ const toolTypes = [
'EllipticalRoi',
'CircleRoi',
'RectangleRoi',
]
];
let defaultDropdownItems = [
{
actionType: 'Delete',
action: ({ nearbyToolData, eventData }) => {
const element = eventData.element
const element = eventData.element;
cornerstoneTools.removeToolState(
element,
nearbyToolData.toolType,
nearbyToolData.tool
)
cornerstone.updateImage(element)
);
cornerstone.updateImage(element);
},
},
{
actionType: 'setLabel',
action: ({ nearbyToolData, eventData }) => {
const { tool } = nearbyToolData
const { tool } = nearbyToolData;
const options = {
skipAddLabelButton: true,
editLocation: true,
}
};
getMeasurementLocationCallback(eventData, tool, options)
getMeasurementLocationCallback(eventData, tool, options);
},
},
{
actionType: 'setDescription',
action: ({ nearbyToolData, eventData }) => {
const { tool } = nearbyToolData
const { tool } = nearbyToolData;
const options = {
editDescriptionOnDialog: true,
}
};
getMeasurementLocationCallback(eventData, tool, options)
getMeasurementLocationCallback(eventData, tool, options);
},
},
]
];
function getNearbyToolData(element, coords, toolTypes) {
const nearbyTool = {}
let pointNearTool = false
const nearbyTool = {};
let pointNearTool = false;
toolTypes.forEach(toolType => {
const toolData = cornerstoneTools.getToolState(element, toolType)
const toolData = cornerstoneTools.getToolState(element, toolType);
if (!toolData) {
return
return;
}
toolData.data.forEach(function(data, index) {
// TODO: Fix this, it's ugly
let toolInterface = cornerstoneTools.getToolForElement(element, toolType)
let toolInterface = cornerstoneTools.getToolForElement(element, toolType);
if (!toolInterface) {
toolInterface = cornerstoneTools.getToolForElement(
element,
`${toolType}Tool`
)
);
}
if (!toolInterface) {
throw new Error('Tool not found.')
throw new Error('Tool not found.');
}
if (toolInterface.pointNearTool(element, data, coords)) {
pointNearTool = true
nearbyTool.tool = data
nearbyTool.index = index
nearbyTool.toolType = toolType
pointNearTool = true;
nearbyTool.tool = data;
nearbyTool.index = index;
nearbyTool.toolType = toolType;
}
})
});
if (pointNearTool) {
return false
return false;
}
})
});
return pointNearTool ? nearbyTool : undefined
return pointNearTool ? nearbyTool : undefined;
}
function getDropdownItems(eventData, isTouchEvent = false, availableTools) {
@ -103,7 +103,7 @@ function getDropdownItems(eventData, isTouchEvent = false, availableTools) {
eventData.currentPoints.canvas,
toolTypes,
availableTools
)
);
// Annotate tools for touch events already have a press handle to edit it, has a better UX for deleting it
if (
@ -111,36 +111,36 @@ function getDropdownItems(eventData, isTouchEvent = false, availableTools) {
nearbyToolData &&
nearbyToolData.toolType === 'arrowAnnotate'
) {
return
return;
}
let dropdownItems = []
let dropdownItems = [];
if (nearbyToolData) {
defaultDropdownItems.forEach(function(item) {
item.params = {
eventData,
nearbyToolData,
}
};
if (item.actionType === 'Delete') {
item.text = 'Delete measurement'
item.text = 'Delete measurement';
}
if (item.actionType === 'setLabel') {
item.text = 'Relabel'
item.text = 'Relabel';
}
if (item.actionType === 'setDescription') {
item.text = `${
nearbyToolData.tool.description ? 'Edit' : 'Add'
} Description`
} Description`;
}
dropdownItems.push(item)
})
dropdownItems.push(item);
});
}
return dropdownItems
return dropdownItems;
}
class ToolContextMenu extends Component {
@ -150,43 +150,43 @@ class ToolContextMenu extends Component {
onClose: PropTypes.func,
availableTools: PropTypes.array,
visible: PropTypes.bool.isRequired,
}
};
static defaultProps = {
visible: true,
isTouchEvent: false,
}
};
constructor(props) {
super(props)
super(props);
this.mainElement = React.createRef()
this.mainElement = React.createRef();
}
render() {
if (!this.props.eventData) {
return null
return null;
}
const { isTouchEvent, eventData, availableTools } = this.props
const { isTouchEvent, eventData, availableTools } = this.props;
const dropdownItems = getDropdownItems(
eventData,
isTouchEvent,
availableTools
)
);
// Skip if there is no dropdown item
if (!dropdownItems.length) {
return ''
return '';
}
const dropdownComponents = dropdownItems.map(item => {
const itemOnClick = event => {
item.action(item.params)
item.action(item.params);
if (this.props.onClose) {
this.props.onClose()
this.props.onClose();
}
}
};
return (
<li key={item.actionType}>
@ -194,32 +194,32 @@ class ToolContextMenu extends Component {
<span key={item.actionType}>{item.text}</span>
</button>
</li>
)
})
);
});
const position = {
top: `${eventData.currentPoints.canvas.y}px`,
left: `${eventData.currentPoints.canvas.x}px`,
}
};
return (
<div className="ToolContextMenu" style={position} ref={this.mainElement}>
<ul className="bounded">{dropdownComponents}</ul>
</div>
)
);
}
componentDidMount = () => {
if (this.mainElement.current) {
this.updateElementPosition()
this.updateElementPosition();
}
}
};
componentDidUpdate = () => {
if (this.mainElement.current) {
this.updateElementPosition()
this.updateElementPosition();
}
}
};
updateElementPosition = () => {
const {
@ -228,25 +228,26 @@ class ToolContextMenu extends Component {
offsetHeight,
offsetWidth,
offsetLeft,
} = this.mainElement.current
} = this.mainElement.current;
const { eventData } = this.props
const { eventData } = this.props;
if (offsetTop + offsetHeight > offsetParent.offsetHeight) {
const offBoundPixels =
offsetTop + offsetHeight - offsetParent.offsetHeight
const top = eventData.currentPoints.canvas.y - offBoundPixels
offsetTop + offsetHeight - offsetParent.offsetHeight;
const top = eventData.currentPoints.canvas.y - offBoundPixels;
this.mainElement.current.style.top = `${top > 0 ? top : 0}px`
this.mainElement.current.style.top = `${top > 0 ? top : 0}px`;
}
if (offsetLeft + offsetWidth > offsetParent.offsetWidth) {
const offBoundPixels = offsetLeft + offsetWidth - offsetParent.offsetWidth
const left = eventData.currentPoints.canvas.x - offBoundPixels
const offBoundPixels =
offsetLeft + offsetWidth - offsetParent.offsetWidth;
const left = eventData.currentPoints.canvas.x - offBoundPixels;
this.mainElement.current.style.left = `${left > 0 ? left : 0}px`
this.mainElement.current.style.left = `${left > 0 ? left : 0}px`;
}
}
};
}
export default ToolContextMenu
export default ToolContextMenu;

View File

@ -52,7 +52,7 @@ class Viewer extends Component {
studies: PropTypes.array,
studyInstanceUids: PropTypes.array,
onTimepointsUpdated: PropTypes.func,
onMeasurementsUpdated: PropTypes.func
onMeasurementsUpdated: PropTypes.func,
};
constructor(props) {
@ -60,8 +60,8 @@ class Viewer extends Component {
OHIF.measurements.MeasurementApi.setConfiguration({
dataExchange: {
retrieve: this.retrieveMeasurements,
store: this.storeMeasurements
}
store: this.storeMeasurements,
},
});
OHIF.measurements.TimepointApi.setConfiguration({
@ -70,8 +70,8 @@ class Viewer extends Component {
store: this.storeTimepoints,
remove: this.removeTimepoint,
update: this.updateTimepoint,
disassociate: this.disassociateStudy
}
disassociate: this.disassociateStudy,
},
});
}
@ -115,8 +115,8 @@ class Viewer extends Component {
patientId: filter.patientId,
earliestDate,
latestDate,
isLocked: false
}
isLocked: false,
},
]);
};
@ -158,11 +158,11 @@ class Viewer extends Component {
const currentTimepointId = 'TimepointId';
const timepointApi = new TimepointApi(currentTimepointId, {
onTimepointsUpdated: this.onTimepointsUpdated
onTimepointsUpdated: this.onTimepointsUpdated,
});
const measurementApi = new MeasurementApi(timepointApi, {
onMeasurementsUpdated: this.onMeasurementsUpdated
onMeasurementsUpdated: this.onMeasurementsUpdated,
});
const patientId = studies[0] && studies[0].patientId;

View File

@ -1,9 +1,9 @@
import { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { OHIF } from 'ohif-core'
import ConnectedLayoutManager from './ConnectedLayoutManager.js'
import './ViewerMain.css'
import { Component } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import { OHIF } from 'ohif-core';
import ConnectedLayoutManager from './ConnectedLayoutManager.js';
import './ViewerMain.css';
class ViewerMain extends Component {
static propTypes = {
@ -12,10 +12,10 @@ class ViewerMain extends Component {
clearViewportSpecificData: PropTypes.func.isRequired,
setToolActive: PropTypes.func.isRequired,
setActiveViewportSpecificData: PropTypes.func.isRequired,
}
};
constructor(props) {
super(props)
super(props);
// Initialize hotkeys
new OHIF.HotkeysUtil('viewer', {
@ -23,39 +23,41 @@ class ViewerMain extends Component {
clearViewportSpecificData: props.clearViewportSpecificData,
setToolActive: props.setToolActive,
setActiveViewportSpecificData: props.setActiveViewportSpecificData,
})
});
this.state = {
displaySets: [],
}
};
this.cachedViewportData = {};
}
getDisplaySets(studies) {
const displaySets = []
const displaySets = [];
studies.forEach(study => {
study.displaySets.forEach(dSet => {
if (!dSet.plugin) {
dSet.plugin = 'cornerstone'
dSet.plugin = 'cornerstone';
}
displaySets.push(dSet)
})
})
displaySets.push(dSet);
});
});
return displaySets
return displaySets;
}
findDisplaySet(studies, studyInstanceUid, displaySetInstanceUid) {
const study = studies.find(study => {
return study.studyInstanceUid === studyInstanceUid
})
return study.studyInstanceUid === studyInstanceUid;
});
if (!study) {
return
return;
}
return study.displaySets.find(displaySet => {
return displaySet.displaySetInstanceUid === displaySetInstanceUid
})
return displaySet.displaySetInstanceUid === displaySetInstanceUid;
});
}
componentDidMount() {
@ -63,50 +65,68 @@ class ViewerMain extends Component {
//window.addEventListener('beforeunload', unloadHandlers.beforeUnload);
// Get all the display sets for the viewer studies
const displaySets = this.getDisplaySets(this.props.studies)
const displaySets = this.getDisplaySets(this.props.studies);
this.setState({
displaySets,
})
});
}
getViewportData = () => {
const viewportData = []
const { layout, viewportSpecificData } = this.props
const viewportData = [];
const { layout, viewportSpecificData } = this.props;
for (
let viewportIndex = 0;
viewportIndex < layout.viewports.length;
viewportIndex++
) {
let displaySet = viewportSpecificData[viewportIndex]
let displaySet = viewportSpecificData[viewportIndex];
// If the viewport is empty, get one available in study
if (!displaySet || !displaySet.displaySetInstanceUid) {
const { displaySets } = this.state
// Use the cached display set in viewport if the new one is empty
if (displaySet && !displaySet.displaySetInstanceUid) {
displaySet = this.cachedViewportData[viewportIndex];
}
if (
displaySet &&
displaySet.studyInstanceUid &&
displaySet.displaySetInstanceUid
) {
// Get missing fields from original display set
const originalDisplaySet = this.findDisplaySet(
this.props.studies,
displaySet.studyInstanceUid,
displaySet.displaySetInstanceUid
);
viewportData.push(Object.assign({}, originalDisplaySet, displaySet));
} else {
// If the viewport is empty, get one available in study
const { displaySets } = this.state;
displaySet = displaySets.find(
ds =>
!viewportData.some(
v => v.displaySetInstanceUid === ds.displaySetInstanceUid
)
)
);
viewportData.push(Object.assign({}, displaySet));
}
viewportData.push(displaySet)
}
return viewportData
}
this.cachedViewportData = viewportData;
return viewportData;
};
setViewportData = ({ viewportIndex, item }) => {
const displaySet = this.findDisplaySet(
this.props.studies,
item.studyInstanceUid,
item.displaySetInstanceUid
)
);
this.props.setViewportSpecificData(viewportIndex, displaySet)
}
this.props.setViewportSpecificData(viewportIndex, displaySet);
};
render() {
return (
@ -117,15 +137,15 @@ class ViewerMain extends Component {
setViewportData={this.setViewportData}
/>
</div>
)
);
}
componentWillUnmount() {
// Clear the entire viewport specific data
const { viewportSpecificData } = this.props
const { viewportSpecificData } = this.props;
Object.keys(viewportSpecificData).forEach(viewportIndex => {
this.props.clearViewportSpecificData(viewportIndex)
})
this.props.clearViewportSpecificData(viewportIndex);
});
// Remove beforeUnload event handler...
//window.removeEventListener('beforeunload', unloadHandlers.beforeUnload);
@ -144,4 +164,4 @@ class ViewerMain extends Component {
}
}
export default ViewerMain
export default ViewerMain;

View File

@ -9,12 +9,12 @@ class ViewerRetrieveStudyData extends Component {
static propTypes = {
studyInstanceUids: PropTypes.array.isRequired,
seriesInstanceUids: PropTypes.array,
server: PropTypes.object
server: PropTypes.object,
};
state = {
studies: null,
error: null
error: null,
};
componentDidMount() {
@ -33,12 +33,12 @@ class ViewerRetrieveStudyData extends Component {
const updatedStudies = createDisplaySets(studies);
this.setState({
studies: updatedStudies
studies: updatedStudies,
});
})
.catch(error => {
this.setState({
error: true
error: true,
});
throw new Error(error);

View File

@ -2,21 +2,21 @@
* Entry point for development and production PWA builds.
* Packaged (NPM) builds go through `index_publish.js`
*/
import App from './App.js'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App.js';
import React from 'react';
import ReactDOM from 'react-dom';
export { App }
export { App };
window.config = window.config || {}
window.config = window.config || {};
const applicationDefaults = {
routerBasename: '/',
relativeWebWorkerScriptsPath: '',
}
const applicationProps = Object.assign({}, applicationDefaults, window.config)
const app = React.createElement(App, applicationProps, null)
};
const applicationProps = Object.assign({}, applicationDefaults, window.config);
const app = React.createElement(App, applicationProps, null);
ReactDOM.render(app, document.getElementById('root'))
ReactDOM.render(app, document.getElementById('root'));
/*
Example config with OIDC

View File

@ -1,21 +1,21 @@
import cornerstoneTools from 'cornerstone-tools'
import updateTableWithNewMeasurementData from './updateTableWithNewMeasurementData'
import cornerstoneTools from 'cornerstone-tools';
import updateTableWithNewMeasurementData from './updateTableWithNewMeasurementData';
export default function getMeasurementLocationCallback(
eventData,
tool,
options
) {
const { toolType } = tool
const { element } = eventData
const doneCallback = updateTableWithNewMeasurementData
const { toolType } = tool;
const { element } = eventData;
const doneCallback = updateTableWithNewMeasurementData;
const ToolInstance = cornerstoneTools.getToolForElement(element, toolType)
const ToolInstance = cornerstoneTools.getToolForElement(element, toolType);
ToolInstance.configuration.getMeasurementLocationCallback(
tool,
eventData,
doneCallback,
options
)
);
}

View File

@ -1,4 +1,4 @@
import { OHIF } from 'ohif-core'
import { OHIF } from 'ohif-core';
// TODO: Move this function to OHIF itself so we can use it on the OHIF measurment table (when it is finished)
@ -16,48 +16,48 @@ export default function jumpToRowItem(
timepointManagerState,
options = { invertViewportTimepointsOrder: false, childToolKey: null }
) {
const numViewports = viewportsState.layout.viewports.length
const numTimepoints = timepointManagerState.timepoints.length
const { measurements, timepoints } = timepointManagerState
const numViewportsToUpdate = Math.min(numTimepoints, numViewports)
const { toolType, measurementNumber } = measurementData
const numViewports = viewportsState.layout.viewports.length;
const numTimepoints = timepointManagerState.timepoints.length;
const { measurements, timepoints } = timepointManagerState;
const numViewportsToUpdate = Math.min(numTimepoints, numViewports);
const { toolType, measurementNumber } = measurementData;
if (options.invertViewportTimepointsOrder) {
timepoints.reverse()
timepoints.reverse();
}
const measurementsForToolGroup = measurements[toolType]
const measurementsForToolGroup = measurements[toolType];
// Retrieve the measurements data
const measurementsToJumpTo = []
const measurementsToJumpTo = [];
for (let i = 0; i < numViewportsToUpdate; i++) {
const { timepointId } = timepoints[i]
const { timepointId } = timepoints[i];
const dataAtThisTimepoint = measurementsForToolGroup.find(entry => {
return (
entry.timepointId === timepointId &&
entry.measurementNumber === measurementNumber
)
})
);
});
if (!dataAtThisTimepoint) {
measurementsToJumpTo.push(null)
continue
measurementsToJumpTo.push(null);
continue;
}
let measurement = dataAtThisTimepoint
let measurement = dataAtThisTimepoint;
const { tool } = OHIF.measurements.MeasurementApi.getToolConfiguration(
toolType
)
);
if (options.childToolKey) {
measurement = dataAtThisTimepoint[options.childToolKey]
measurement = dataAtThisTimepoint[options.childToolKey];
} else if (Array.isArray(tool.childTools)) {
const key = tool.childTools.find(key => !!dataAtThisTimepoint[key])
measurement = dataAtThisTimepoint[key]
const key = tool.childTools.find(key => !!dataAtThisTimepoint[key]);
measurement = dataAtThisTimepoint[key];
}
measurementsToJumpTo.push(measurement)
measurementsToJumpTo.push(measurement);
}
// TODO: Add a single viewports state action which allows
@ -70,41 +70,41 @@ export default function jumpToRowItem(
const displaySetContainsSopInstance = (displaySet, sopInstanceUid) =>
displaySet.images.find(
image => image.getSOPInstanceUID() === sopInstanceUid
)
);
const viewportSpecificData = []
const viewportSpecificData = [];
measurementsToJumpTo.forEach((data, viewportIndex) => {
// Skip if there is no measurement to jump
if (!data) {
return
return;
}
const study = OHIF.utils.studyMetadataManager.get(data.studyInstanceUid)
const study = OHIF.utils.studyMetadataManager.get(data.studyInstanceUid);
if (!study) {
throw new Error('Study not found.')
throw new Error('Study not found.');
}
const displaySet = study.findDisplaySet(displaySet => {
return displaySetContainsSopInstance(displaySet, data.sopInstanceUid)
})
return displaySetContainsSopInstance(displaySet, data.sopInstanceUid);
});
if (!displaySet) {
throw new Error('Display set not found.')
throw new Error('Display set not found.');
}
displaySet.sopInstanceUid = data.sopInstanceUid
displaySet.sopInstanceUid = data.sopInstanceUid;
if (data.frameIndex) {
displaySet.frameIndex = data.frameIndex
displaySet.frameIndex = data.frameIndex;
}
viewportSpecificData.push({
viewportIndex,
displaySet,
})
})
});
});
return {
viewportSpecificData,
layout: [], // TODO: if we need to change layout, we should return this here
}
};
}

View File

@ -1,5 +1,5 @@
import OHIF from 'ohif-core'
import cornerstone from 'cornerstone-core'
import OHIF from 'ohif-core';
import cornerstone from 'cornerstone-core';
export default function updateTableWithNewMeasurementData({
toolType,
@ -8,22 +8,22 @@ export default function updateTableWithNewMeasurementData({
description,
}) {
// Update all measurements by measurement number
const measurementApi = OHIF.measurements.MeasurementApi.Instance
const measurementApi = OHIF.measurements.MeasurementApi.Instance;
const measurements = measurementApi.tools[toolType].filter(
m => m.measurementNumber === measurementNumber
)
);
measurements.forEach(measurement => {
measurement.location = location
measurement.description = description
measurement.location = location;
measurement.description = description;
measurementApi.updateMeasurement(measurement.toolType, measurement)
})
measurementApi.updateMeasurement(measurement.toolType, measurement);
});
measurementApi.syncMeasurementsAndToolData()
measurementApi.syncMeasurementsAndToolData();
// Update images in all active viewports
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element)
})
cornerstone.updateImage(enabledElement.element);
});
}

View File

@ -1,46 +1,46 @@
export default function bounding(elementRef, currentPosition = {}) {
if (!elementRef.current) {
return
return;
}
const currentElement = elementRef.current
const currentElement = elementRef.current;
const {
offsetParent,
offsetTop,
offsetHeight,
offsetLeft,
offsetWidth,
} = currentElement
let top = currentPosition.top || offsetTop
let left = currentPosition.left || offsetLeft
} = currentElement;
let top = currentPosition.top || offsetTop;
let left = currentPosition.left || offsetLeft;
if (!offsetParent) {
return
return;
}
let maxHeight = `${offsetParent.offsetHeight}px`
let maxHeight = `${offsetParent.offsetHeight}px`;
if (offsetHeight + top > offsetParent.offsetHeight) {
top = top - (offsetHeight + top - offsetParent.offsetHeight)
top = top - (offsetHeight + top - offsetParent.offsetHeight);
if (top < 0) {
top = 0
top = 0;
}
}
if (left + offsetWidth > offsetParent.offsetWidth) {
left = offsetParent.offsetWidth - offsetWidth
left = offsetParent.offsetWidth - offsetWidth;
if (left < 0) {
left = 0
left = 0;
}
}
if (maxHeight && currentElement.style.maxHeight !== maxHeight) {
currentElement.style.maxHeight = maxHeight
currentElement.style.maxHeight = maxHeight;
}
if (currentElement.style.top !== `${top}px`) {
currentElement.style.top = `${top}px`
currentElement.style.top = `${top}px`;
}
if (currentElement.style.left !== `${left}px`) {
currentElement.style.left = `${left}px`
currentElement.style.left = `${left}px`;
}
}

View File

@ -1,16 +1,16 @@
export const setLeftSidebarOpen = state => ({
type: 'SET_LEFT_SIDEBAR_OPEN',
state
state,
});
export const setRightSidebarOpen = state => ({
type: 'SET_RIGHT_SIDEBAR_OPEN',
state
state,
});
export const setUserPreferencesModalOpen = state => ({
type: 'SET_USER_PREFERENCES_MODAL_OPEN',
state
state,
});
const actions = {

View File

@ -4,39 +4,39 @@ const defaultState = {
userPreferencesModalOpen: false,
labelling: {},
contextMenu: {},
}
};
const ui = (state = defaultState, action) => {
switch (action.type) {
case 'SET_LEFT_SIDEBAR_OPEN':
return Object.assign({}, state, { leftSidebarOpen: action.state })
return Object.assign({}, state, { leftSidebarOpen: action.state });
case 'SET_RIGHT_SIDEBAR_OPEN':
return Object.assign({}, state, { rightSidebarOpen: action.state })
return Object.assign({}, state, { rightSidebarOpen: action.state });
case 'SET_USER_PREFERENCES_MODAL_OPEN':
return Object.assign({}, state, {
userPreferencesModalOpen: action.state,
})
});
case 'SET_LABELLING_FLOW_DATA':
const labelling = Object.assign({}, action.labellingFlowData)
const labelling = Object.assign({}, action.labellingFlowData);
return Object.assign({}, state, { labelling })
return Object.assign({}, state, { labelling });
case 'SET_TOOL_CONTEXT_MENU_DATA':
const contextMenu = Object.assign({}, state.contextMenu)
const contextMenu = Object.assign({}, state.contextMenu);
contextMenu[action.viewportIndex] = Object.assign(
{},
action.toolContextMenuData
)
);
return Object.assign({}, state, { contextMenu })
return Object.assign({}, state, { contextMenu });
case 'RESET_LABELLING_AND_CONTEXT_MENU':
return Object.assign({}, state, {
labelling: defaultState.labelling,
contextMenu: defaultState.contextMenu,
})
});
default:
return state
return state;
}
}
};
export default ui
export default ui;

View File

@ -1,45 +1,43 @@
import React from "react";
import PropTypes from "prop-types";
import ViewerRetrieveStudyData from "../connectedComponents/ViewerRetrieveStudyData.js";
import React from 'react';
import PropTypes from 'prop-types';
import ViewerRetrieveStudyData from '../connectedComponents/ViewerRetrieveStudyData.js';
function IHEInvokeImageDisplay({ match }) {
const requestType = match.params.query.requestType;
let studyInstanceUids;
//let patientUids;
let displayStudyList = false;
const requestType = match.params.query.requestType;
let studyInstanceUids;
//let patientUids;
let displayStudyList = false;
if (requestType === "STUDY") {
studyInstanceUids = match.params.query.studyUID.split(';');
} else if (requestType === "STUDYBASE64") {
const uids = this.params.query.studyUID;
const decodedData = window.atob(uids);
studyInstanceUids = decodedData.split(';');
} else if (requestType === "PATIENT") {
//patientUidspatientUids = this.params.query.patientID.split(';');
displayStudyList = true
} else {
displayStudyList = true
}
if (requestType === 'STUDY') {
studyInstanceUids = match.params.query.studyUID.split(';');
} else if (requestType === 'STUDYBASE64') {
const uids = this.params.query.studyUID;
const decodedData = window.atob(uids);
studyInstanceUids = decodedData.split(';');
} else if (requestType === 'PATIENT') {
//patientUidspatientUids = this.params.query.patientID.split(';');
displayStudyList = true;
} else {
displayStudyList = true;
}
if (displayStudyList) {
return ('')//<StudyList patientUids={patientUids}/>);
}
if (displayStudyList) {
return ''; //<StudyList patientUids={patientUids}/>);
}
return (
<ViewerRetrieveStudyData studyInstanceUids={studyInstanceUids}/>
);
return <ViewerRetrieveStudyData studyInstanceUids={studyInstanceUids} />;
}
IHEInvokeImageDisplay.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
query: PropTypes.shape({
requestType: PropTypes.string.isRequired,
studyUID: PropTypes.string,
patientID: PropTypes.string
})
})
})
match: PropTypes.shape({
params: PropTypes.shape({
query: PropTypes.shape({
requestType: PropTypes.string.isRequired,
studyUID: PropTypes.string,
patientID: PropTypes.string,
}),
}),
}),
};
export default IHEInvokeImageDisplay;

View File

@ -9,12 +9,12 @@ const { createDisplaySets } = OHIF.utils;
class StandaloneRouting extends Component {
state = {
studies: null,
error: null
error: null,
};
static propTypes = {
location: PropTypes.object,
store: PropTypes.object
store: PropTypes.object,
};
static parseQueryAndFetchStudies(query) {

View File

@ -1,32 +1,35 @@
import React from "react";
import PropTypes from "prop-types";
import ConnectedViewerRetrieveStudyData from "../connectedComponents/ConnectedViewerRetrieveStudyData";
import React from 'react';
import PropTypes from 'prop-types';
import ConnectedViewerRetrieveStudyData from '../connectedComponents/ConnectedViewerRetrieveStudyData';
function ViewerRouting({ match }) {
const { studyInstanceUids, seriesInstanceUids } = match.params;
const { studyInstanceUids, seriesInstanceUids } = match.params;
let studyUIDs;
let seriesUIDs;
let studyUIDs;
let seriesUIDs;
if (studyInstanceUids && !seriesInstanceUids) {
studyUIDs = studyInstanceUids.split(';');
} else if (studyInstanceUids && seriesInstanceUids) {
studyUIDs = [match.params.studyInstanceUid];
seriesUIDs = match.params.seriesInstanceUids.split(';');
}
if (studyInstanceUids && !seriesInstanceUids) {
studyUIDs = studyInstanceUids.split(';');
} else if (studyInstanceUids && seriesInstanceUids) {
studyUIDs = [match.params.studyInstanceUid];
seriesUIDs = match.params.seriesInstanceUids.split(';');
}
return (
<ConnectedViewerRetrieveStudyData studyInstanceUids={studyUIDs} seriesInstanceUids={seriesUIDs}/>
);
return (
<ConnectedViewerRetrieveStudyData
studyInstanceUids={studyUIDs}
seriesInstanceUids={seriesUIDs}
/>
);
}
ViewerRouting.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
studyInstanceUids: PropTypes.string.isRequired,
seriesInstanceUids: PropTypes.string
})
})
match: PropTypes.shape({
params: PropTypes.shape({
studyInstanceUids: PropTypes.string.isRequired,
seriesInstanceUids: PropTypes.string,
}),
}),
};
export default ViewerRouting;

View File

@ -1,8 +1,8 @@
describe('Sanity Test', () => {
test('how many marbles?', () => {
const expectedMarbles = 4
const actualMarbles = 2 + 2
const expectedMarbles = 4;
const actualMarbles = 2 + 2;
expect(actualMarbles).toEqual(expectedMarbles)
})
})
expect(actualMarbles).toEqual(expectedMarbles);
});
});

View File

@ -1,15 +1,15 @@
import OHIF from 'ohif-core'
import updateTableWithNewMeasurementData from './lib/updateTableWithNewMeasurementData'
import OHIF from 'ohif-core';
import updateTableWithNewMeasurementData from './lib/updateTableWithNewMeasurementData';
function getToolLabellingFlowCallback(store) {
const setLabellingFlowDataAction = labellingFlowData => ({
type: 'SET_LABELLING_FLOW_DATA',
labellingFlowData,
})
});
const setLabellingFlowData = labellingFlowData => {
store.dispatch(setLabellingFlowDataAction(labellingFlowData))
}
store.dispatch(setLabellingFlowDataAction(labellingFlowData));
};
return function toolLabellingFlowCallback(
measurementData,
@ -21,21 +21,21 @@ function getToolLabellingFlowCallback(store) {
// Update the measurement data with the labelling parameters
if (location) {
measurementData.location = location
measurementData.location = location;
}
if (description) {
measurementData.description = description
measurementData.description = description;
}
if (response) {
measurementData.response = response
measurementData.response = response;
}
updateTableWithNewMeasurementData(measurementData)
}
updateTableWithNewMeasurementData(measurementData);
};
const labellingDoneCallback = () => {
setLabellingFlowData({ visible: false })
}
setLabellingFlowData({ visible: false });
};
const labellingFlowData = {
visible: true,
@ -48,99 +48,99 @@ function getToolLabellingFlowCallback(store) {
editDescriptionOnDialog: options.editDescriptionOnDialog,
labellingDoneCallback,
updateLabelling,
}
};
setLabellingFlowData(labellingFlowData)
}
setLabellingFlowData(labellingFlowData);
};
}
const resetLabellingAndContextMenuAction = state => ({
type: 'RESET_LABELLING_AND_CONTEXT_MENU',
state,
})
});
const setToolContextMenuDataAction = (viewportIndex, toolContextMenuData) => ({
type: 'SET_TOOL_CONTEXT_MENU_DATA',
viewportIndex,
toolContextMenuData,
})
});
function getOnRightClickCallback(store) {
const setToolContextMenuData = (viewportIndex, toolContextMenuData) => {
store.dispatch(resetLabellingAndContextMenuAction())
store.dispatch(resetLabellingAndContextMenuAction());
store.dispatch(
setToolContextMenuDataAction(viewportIndex, toolContextMenuData)
)
}
);
};
const getOnCloseCallback = viewportIndex => {
return function onClose() {
const toolContextMenuData = {
visible: false,
}
};
store.dispatch(
setToolContextMenuDataAction(viewportIndex, toolContextMenuData)
)
}
}
);
};
};
return function onRightClick(event) {
const eventData = event.detail
const viewportIndex = parseInt(eventData.element.dataset.viewportIndex, 10)
const eventData = event.detail;
const viewportIndex = parseInt(eventData.element.dataset.viewportIndex, 10);
const toolContextMenuData = {
eventData,
isTouchEvent: false,
onClose: getOnCloseCallback(viewportIndex),
}
};
setToolContextMenuData(viewportIndex, toolContextMenuData)
}
setToolContextMenuData(viewportIndex, toolContextMenuData);
};
}
function getOnTouchPressCallback(store) {
const setToolContextMenuData = (viewportIndex, toolContextMenuData) => {
store.dispatch(resetLabellingAndContextMenuAction())
store.dispatch(resetLabellingAndContextMenuAction());
store.dispatch(
setToolContextMenuDataAction(viewportIndex, toolContextMenuData)
)
}
);
};
const getOnCloseCallback = viewportIndex => {
return function onClose() {
const toolContextMenuData = {
visible: false,
}
};
store.dispatch(
setToolContextMenuDataAction(viewportIndex, toolContextMenuData)
)
}
}
);
};
};
return function onTouchPress(event) {
const eventData = event.detail
const viewportIndex = parseInt(eventData.element.dataset.viewportIndex, 10)
const eventData = event.detail;
const viewportIndex = parseInt(eventData.element.dataset.viewportIndex, 10);
const toolContextMenuData = {
eventData,
isTouchEvent: true,
onClose: getOnCloseCallback(viewportIndex),
}
};
setToolContextMenuData(viewportIndex, toolContextMenuData)
}
setToolContextMenuData(viewportIndex, toolContextMenuData);
};
}
function getResetLabellingAndContextMenu(store) {
return function resetLabellingAndContextMenu() {
store.dispatch(resetLabellingAndContextMenuAction())
}
store.dispatch(resetLabellingAndContextMenuAction());
};
}
export default function setupTools(store) {
const toolLabellingFlowCallback = getToolLabellingFlowCallback(store)
const toolLabellingFlowCallback = getToolLabellingFlowCallback(store);
const availableTools = [
{ name: 'Pan', mouseButtonMasks: [1, 4] },
{ name: 'Zoom', mouseButtonMasks: [1, 2] },
@ -214,13 +214,13 @@ export default function setupTools(store) {
{ name: 'ZoomTouchPinch' },
{ name: 'StackScrollMouseWheel' },
{ name: 'StackScrollMultiTouch' },
]
];
const onRightClick = getOnRightClickCallback(store)
const onTouchPress = getOnTouchPressCallback(store)
const onNewImage = getResetLabellingAndContextMenu(store)
const onMouseClick = getResetLabellingAndContextMenu(store)
const onTouchStart = getResetLabellingAndContextMenu(store)
const onRightClick = getOnRightClickCallback(store);
const onTouchPress = getOnTouchPressCallback(store);
const onNewImage = getResetLabellingAndContextMenu(store);
const onMouseClick = getResetLabellingAndContextMenu(store);
const onTouchStart = getResetLabellingAndContextMenu(store);
const toolAction = OHIF.redux.actions.setExtensionData('cornerstone', {
availableTools,
onNewImage,
@ -228,7 +228,7 @@ export default function setupTools(store) {
onTouchPress,
onTouchStart,
onMouseClick,
})
});
store.dispatch(toolAction)
store.dispatch(toolAction);
}

View File

@ -1,21 +1,21 @@
import { connect } from 'react-redux'
import { connect } from 'react-redux';
import StudyListWithData from './StudyListWithData.js'
import StudyListWithData from './StudyListWithData.js';
const isActive = a => a.active === true
const isActive = a => a.active === true;
const mapStateToProps = state => {
const activeServer = state.servers.servers.find(isActive)
const activeServer = state.servers.servers.find(isActive);
return {
server: activeServer,
user: state.oidc.user,
}
}
};
};
const ConnectedStudyList = connect(
mapStateToProps,
null
)(StudyListWithData)
)(StudyListWithData);
export default ConnectedStudyList
export default ConnectedStudyList;

View File

@ -1,24 +1,22 @@
import React from "react";
import PropTypes from "prop-types";
import ConnectedStudyList from "./ConnectedStudyList";
import React from 'react';
import PropTypes from 'prop-types';
import ConnectedStudyList from './ConnectedStudyList';
// TODO: Move to react-viewerbase
function StudyListRouting({ match }) {
// TODO: Figure out which filters we want to pass in via a URL
//const { patientId } = match.params;
// TODO: Figure out which filters we want to pass in via a URL
//const { patientId } = match.params;
return (
<ConnectedStudyList/>
);
return <ConnectedStudyList />;
}
StudyListRouting.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({
patientIds: PropTypes.string,
})
})
match: PropTypes.shape({
params: PropTypes.shape({
patientIds: PropTypes.string,
}),
}),
};
export default StudyListRouting;

View File

@ -10,14 +10,14 @@ class StudyListWithData extends Component {
state = {
searchData: {},
studies: null,
error: null
error: null,
};
static propTypes = {
patientId: PropTypes.string,
server: PropTypes.object,
user: PropTypes.object,
history: PropTypes.object
history: PropTypes.object,
};
static rowsPerPage = 25;
@ -42,7 +42,7 @@ class StudyListWithData extends Component {
rowsPerPage: StudyListWithData.rowsPerPage,
studyDateFrom: StudyListWithData.defaultStudyDateFrom,
studyDateTo: StudyListWithData.defaultStudyDateTo,
sortData: StudyListWithData.defaultSort
sortData: StudyListWithData.defaultSort,
}
) => {
const { server } = this.props;
@ -55,7 +55,7 @@ class StudyListWithData extends Component {
studyDateFrom: searchData.studyDateFrom,
studyDateTo: searchData.studyDateTo,
limit: searchData.rowsPerPage,
offset: searchData.currentPage * searchData.rowsPerPage
offset: searchData.currentPage * searchData.rowsPerPage,
};
// TODO: add sorting
@ -97,12 +97,12 @@ class StudyListWithData extends Component {
});
this.setState({
studies: sortedStudies
studies: sortedStudies,
});
})
.catch(error => {
this.setState({
error: true
error: true,
});
throw new Error(error);

View File

@ -1,5 +1,5 @@
// https://github.com/maxmantz/redux-oidc/blob/master/docs/API.md
import { loadUser, createUserManager } from 'redux-oidc'
import { loadUser, createUserManager } from 'redux-oidc';
/**
* Creates a userManager from oidcSettings;
@ -17,7 +17,7 @@ import { loadUser, createUserManager } from 'redux-oidc'
*/
export default function(store, oidcSettings) {
if (!store || !oidcSettings) {
return
return;
}
const settings = {
@ -27,11 +27,11 @@ export default function(store, oidcSettings) {
revokeAccessTokenOnSignout: true,
filterProtocolClaims: true,
loadUserInfo: true,
}
};
const userManager = createUserManager(settings)
const userManager = createUserManager(settings);
loadUser(store, userManager)
loadUser(store, userManager);
return userManager
return userManager;
}

View File

@ -1,23 +1,23 @@
import getUserManagerForOpenIdConnectClient from './getUserManagerForOpenIdConnectClient.js'
import getUserManagerForOpenIdConnectClient from './getUserManagerForOpenIdConnectClient.js';
describe('getUserManagerForOpenIdConnectClient', () => {
it('returns undefined if store is not provided', () => {
const expectedReturnVal = undefined
const returnVal = getUserManagerForOpenIdConnectClient(undefined, {})
const expectedReturnVal = undefined;
const returnVal = getUserManagerForOpenIdConnectClient(undefined, {});
expect(returnVal).toEqual(expectedReturnVal)
})
expect(returnVal).toEqual(expectedReturnVal);
});
it('returns undefined if oidcSettings are not provided', () => {
const expectedReturnVal = undefined
const returnVal = getUserManagerForOpenIdConnectClient({}, undefined)
const expectedReturnVal = undefined;
const returnVal = getUserManagerForOpenIdConnectClient({}, undefined);
expect(returnVal).toEqual(expectedReturnVal)
})
expect(returnVal).toEqual(expectedReturnVal);
});
it('does not return undefined when store and oidcSettings are defined', () => {
const returnVal = getUserManagerForOpenIdConnectClient({}, {})
const returnVal = getUserManagerForOpenIdConnectClient({}, {});
expect(returnVal).not.toBe(undefined)
})
})
expect(returnVal).not.toBe(undefined);
});
});

View File

@ -1,9 +1,9 @@
import getDefaultToolbarButtons from './getDefaultToolbarButtons.js'
import getUserManagerForOpenIdConnectClient from './getUserManagerForOpenIdConnectClient.js'
import initWebWorkers from './initWebWorkers.js'
import getDefaultToolbarButtons from './getDefaultToolbarButtons.js';
import getUserManagerForOpenIdConnectClient from './getUserManagerForOpenIdConnectClient.js';
import initWebWorkers from './initWebWorkers.js';
export {
getDefaultToolbarButtons,
getUserManagerForOpenIdConnectClient,
initWebWorkers,
}
};

View File

@ -1,8 +1,8 @@
import * as utils from './index.js'
import * as utils from './index.js';
describe('utils', () => {
it('has the expected exports', () => {
const utilExports = Object.keys(utils).sort()
const utilExports = Object.keys(utils).sort();
expect(utilExports).toEqual(
[
@ -10,6 +10,6 @@ describe('utils', () => {
'getUserManagerForOpenIdConnectClient',
'initWebWorkers',
].sort()
)
})
})
);
});
});

View File

@ -1,4 +1,4 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
/**
*
@ -8,7 +8,7 @@ import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
export default function(baseDirectory, webWorkScriptsPath) {
const scriptsPath = `${window.location.protocol}//${
window.location.host
}${baseDirectory}/${webWorkScriptsPath}`
}${baseDirectory}/${webWorkScriptsPath}`;
const config = {
maxWebWorkers: Math.max(navigator.hardwareConcurrency - 1, 1),
startWebWorkersOnDemand: true,
@ -22,7 +22,7 @@ export default function(baseDirectory, webWorkScriptsPath) {
strict: false,
},
},
}
};
cornerstoneWADOImageLoader.webWorkerManager.initialize(config)
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
}

View File

@ -1,15 +1,15 @@
import initWebWorkers from './initWebWorkers.js'
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import initWebWorkers from './initWebWorkers.js';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
describe('initWebWorkers', () => {
it("initializes cornerstoneWADOImageLoader's web workers", () => {
const basePath = '/'
const relativeWebWorkerScriptsPath = ''
const basePath = '/';
const relativeWebWorkerScriptsPath = '';
initWebWorkers(basePath, relativeWebWorkerScriptsPath)
initWebWorkers(basePath, relativeWebWorkerScriptsPath);
expect(
cornerstoneWADOImageLoader.webWorkerManager.initialize
).toHaveBeenCalled()
})
})
).toHaveBeenCalled();
});
});