Add ohif-viewer package to work on sharing components across viewers and test the standalone viewer via script tag
This commit is contained in:
parent
6e291a18b8
commit
6e91df26ee
9
.gitmodules
vendored
Normal file
9
.gitmodules
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
[submodule "Packages-react/ohif-core"]
|
||||
path = Packages-react/ohif-core
|
||||
url = git@github.com:OHIF/ohif-core.git
|
||||
[submodule "Packages-react/react-viewerbase"]
|
||||
path = Packages-react/react-viewerbase
|
||||
url = git@github.com:OHIF/react-viewerbase.git
|
||||
[submodule "Packages-react/react-cornerstone-viewport"]
|
||||
path = Packages-react/react-cornerstone-viewport
|
||||
url = git@github.com:cornerstonejs/react-cornerstone-viewport.git
|
||||
@ -6,7 +6,7 @@
|
||||
"homepage": "http://localhost:5000/",
|
||||
"dependencies": {
|
||||
"cornerstone-core": "2.2.8",
|
||||
"cornerstone-math": "0.1.7",
|
||||
"cornerstone-math": "0.1.8",
|
||||
"cornerstone-tools": "3.0.0-b.1512",
|
||||
"cornerstone-wado-image-loader": "2.2.3",
|
||||
"dicom-parser": "1.8.3",
|
||||
@ -14,18 +14,19 @@
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"moment": "^2.22.2",
|
||||
"ohif-core": "^0.1.5",
|
||||
"ohif-core": "^0.1.6",
|
||||
"ohif-viewer": "0.0.1",
|
||||
"oidc-client": "^1.6.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"query-string": "^6.2.0",
|
||||
"react": "^16.4.1",
|
||||
"react-cornerstone-viewport": "^0.1.7",
|
||||
"react": "^16.7.0",
|
||||
"react-cornerstone-viewport": "^0.1.8",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-redux": "^6.0.0",
|
||||
"react-router": "^4.3.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-scripts": "^2.1.1",
|
||||
"react-viewerbase": "^0.2.0",
|
||||
"react-scripts": "^2.1.3",
|
||||
"react-viewerbase": "^0.2.1",
|
||||
"redux": "^4.0.1",
|
||||
"redux-oidc": "^3.1.0"
|
||||
},
|
||||
@ -47,6 +48,8 @@
|
||||
"not op_mini all"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/plugin-proposal-class-properties": "^7.2.3",
|
||||
"husky": "^1.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,16 +4,16 @@ import { withRouter } from 'react-router';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { ViewerbaseDragDropContext } from 'react-viewerbase';
|
||||
import ViewerRouting from "./ViewerRouting.js";
|
||||
import StudyListRouting from './StudyListRouting.js';
|
||||
import StandaloneRouting from './StandaloneRouting.js';
|
||||
import IHEInvokeImageDisplay from './IHEInvokeImageDisplay.js';
|
||||
import CallbackPage from './CallbackPage.js';
|
||||
import routes from './routes/';
|
||||
import StudyListRouting from './studylist/StudyListRouting.js';
|
||||
//import CallbackPage from './CallbackPage.js';'
|
||||
import userManager from "./userManager.js";
|
||||
import './App.css';
|
||||
import './variables.css';
|
||||
import './theme-tide.css';
|
||||
|
||||
const { ViewerRouting, StandaloneRouting, IHEInvokeImageDisplay } = routes;
|
||||
|
||||
const reload = () => window.location.reload();
|
||||
|
||||
function setContext(context) {
|
||||
@ -94,7 +94,7 @@ class App extends Component {
|
||||
userManager.signinRedirect();
|
||||
}}
|
||||
/>
|
||||
<Route path="/callback" component={CallbackPage} />
|
||||
{/*<Route path="/callback" component={CallbackPage} />*/}
|
||||
<Route render={() =>
|
||||
<div> Sorry, this page does not exist. </div>}
|
||||
/>
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { LayoutManager } from 'react-viewerbase';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
layout: state.viewports.layout,
|
||||
activeViewportIndex: state.viewports.activeViewportIndex
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedLayoutManager = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(LayoutManager);
|
||||
|
||||
export default ConnectedLayoutManager;
|
||||
@ -1,132 +0,0 @@
|
||||
import { Component } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import cornerstoneMath from "cornerstone-math";
|
||||
import { OHIF } from 'ohif-core';
|
||||
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport.js';
|
||||
import ConnectedLayoutManager from './ConnectedLayoutManager.js';
|
||||
import './ViewerMain.css';
|
||||
|
||||
// Attempt to fix weird undefined dep issue
|
||||
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
|
||||
|
||||
const { StackManager } = OHIF.utils;
|
||||
|
||||
function getCornerstoneStack(studies, viewportData) {
|
||||
const {
|
||||
displaySetInstanceUid,
|
||||
studyInstanceUid,
|
||||
} = viewportData;
|
||||
|
||||
// Create shortcut to displaySet
|
||||
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
|
||||
|
||||
const displaySet = study.displaySets.find((set) => {
|
||||
return set.displaySetInstanceUid === displaySetInstanceUid;
|
||||
});
|
||||
|
||||
// Get stack from Stack Manager
|
||||
return StackManager.findOrCreateStack(study, displaySet);
|
||||
}
|
||||
|
||||
class ViewerMain extends Component {
|
||||
state = {
|
||||
displaySets: [],
|
||||
viewportData: [],
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
studies: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// Add beforeUnload event handler to check for unsaved changes
|
||||
//window.addEventListener('beforeunload', unloadHandlers.beforeUnload);
|
||||
|
||||
// Get all the display sets for the viewer studies
|
||||
const displaySets = [];
|
||||
const studies = this.props.studies;
|
||||
studies.forEach((study) => {
|
||||
study.displaySets.forEach(dSet => dSet.images.length && displaySets.push(dSet));
|
||||
});
|
||||
|
||||
// TODO: re-add plugins back in
|
||||
// TODO: We shouldn't just hang display sets by default
|
||||
const viewportData = displaySets.map((dSet, index) => {
|
||||
return this.getCornerstoneViewport(dSet, index);
|
||||
});
|
||||
|
||||
this.setState({
|
||||
displaySets,
|
||||
viewportData
|
||||
});
|
||||
}
|
||||
|
||||
getCornerstoneViewport = (data, index) => {
|
||||
// Clone the stack here so we don't mutate it later
|
||||
const stack = Object.assign({}, getCornerstoneStack(this.props.studies, data));
|
||||
stack.currentImageIdIndex = 0;
|
||||
const viewportData = {
|
||||
stack,
|
||||
...data,
|
||||
viewportIndex: index
|
||||
};
|
||||
|
||||
return (<ConnectedCornerstoneViewport
|
||||
key={index}
|
||||
viewportData={viewportData}
|
||||
cornerstone={cornerstone}
|
||||
cornerstoneTools={cornerstoneTools}
|
||||
/>);
|
||||
}
|
||||
|
||||
setViewportData = ({viewportIndex, item}) => {
|
||||
// TODO: Replace this with mapDispatchToProps call
|
||||
// if we decide to put viewport info into redux
|
||||
|
||||
// Note: Use Slice because React does a shallow equality check. Mutating the array
|
||||
// would not trigger a re-render. We have to create a copy.
|
||||
const updatedViewportData = this.state.viewportData.slice(0);
|
||||
const data = {
|
||||
studyInstanceUid: item.studyInstanceUid,
|
||||
displaySetInstanceUid: item.displaySetInstanceUid
|
||||
};
|
||||
|
||||
updatedViewportData[viewportIndex] = this.getCornerstoneViewport(data, viewportIndex);
|
||||
|
||||
this.setState({
|
||||
viewportData: updatedViewportData
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="ViewerMain">
|
||||
<ConnectedLayoutManager viewportData={this.state.viewportData} setViewportData={this.setViewportData}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// Remove beforeUnload event handler...
|
||||
//window.removeEventListener('beforeunload', unloadHandlers.beforeUnload);
|
||||
|
||||
// Destroy the synchronizer used to update reference lines
|
||||
OHIF.viewer.updateImageSynchronizer.destroy();
|
||||
|
||||
// Clear references to all stacks in the StackManager
|
||||
StackManager.clearStacks();
|
||||
|
||||
// @TypeSafeStudies
|
||||
// Clears OHIF.viewer.Studies collection
|
||||
//OHIF.viewer.Studies.removeAll();
|
||||
|
||||
// @TypeSafeStudies
|
||||
// Clears OHIF.viewer.StudyMetadataList collection
|
||||
//OHIF.viewer.StudyMetadataList.removeAll();
|
||||
}
|
||||
}
|
||||
|
||||
export default ViewerMain;
|
||||
@ -0,0 +1,20 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ExampleViewportPlugin from './ExampleViewportPlugin';
|
||||
|
||||
// We don't really need Redux to do this, this is just an example
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { displaySet } = ownProps.viewportData;
|
||||
const { studyInstanceUid, seriesInstanceUid } = displaySet;
|
||||
|
||||
return {
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedExampleViewportPlugin = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(ExampleViewportPlugin);
|
||||
|
||||
export default ConnectedExampleViewportPlugin;
|
||||
@ -1,91 +0,0 @@
|
||||
.dd-menu {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.dd-title {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.dd-menu-toggle {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dd-caret-down {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-top: 0.5rem;
|
||||
margin-left: 0.5rem;
|
||||
border-top: 5px solid;
|
||||
border-right: 5px solid transparent;
|
||||
border-left: 5px solid transparent;
|
||||
}
|
||||
|
||||
.dd-menu-list {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
margin-top: 10px;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-ms-border-radius: 5px;
|
||||
-o-border-radius: 5px;
|
||||
overflow: hidden;
|
||||
transition: all 300ms ease;
|
||||
-webkit-transition: all 300ms ease;
|
||||
-moz-transition: all 300ms ease;
|
||||
-ms-transition: all 300ms ease;
|
||||
-o-transition: all 300ms ease;
|
||||
}
|
||||
|
||||
.dd-menu-list.open {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dd-menu-list.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.dd-menu-list.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.dd-menu-list.center {
|
||||
left: 50%;
|
||||
transform: translateX(-50%) ;
|
||||
-webkit-transform: translateX(-50%) ;
|
||||
-moz-transform: translateX(-50%) ;
|
||||
-ms-transform: translateX(-50%) ;
|
||||
-o-transform: translateX(-50%) ;
|
||||
}
|
||||
|
||||
.dd-item {
|
||||
display: flex;
|
||||
color: var(--text-color-active);
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.dd-item:hover {
|
||||
text-decoration: none;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.dd-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.dd-item-icon {
|
||||
width: 10px;
|
||||
text-align: center;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.dd-item-icon:before {
|
||||
vertical-align: middle;
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from "prop-types";
|
||||
import './Dropdown.css'
|
||||
|
||||
class Dropdown extends Component {
|
||||
state = {
|
||||
open: false
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
titleElement: PropTypes.node,
|
||||
title: PropTypes.string,
|
||||
align: PropTypes.oneOf(['left', 'center', 'right']),
|
||||
list: PropTypes.arrayOf(PropTypes.shape({
|
||||
title: PropTypes.string.isRequired,
|
||||
icon: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
link: PropTypes.string,
|
||||
}))
|
||||
};
|
||||
|
||||
renderList = () => {
|
||||
const { list, align } = this.props
|
||||
|
||||
if (!this.state.open) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`dd-menu-list ${align || 'left'}`}>
|
||||
{
|
||||
list.map(({ icon, title, link, onClick }, key) => (
|
||||
<a
|
||||
href={link || "#"}
|
||||
key={key}
|
||||
className='dd-item'
|
||||
onClick={() => this.handleOnClick(onClick)}
|
||||
>
|
||||
{icon && <span className={`dd-item-icon ${icon}`}></span>}
|
||||
<span>{title}</span>
|
||||
</a>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
handleOnClick = (onClick) => {
|
||||
this.toggleList()
|
||||
|
||||
if (onClick) {
|
||||
onClick()
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseClick = (e) => {
|
||||
if (this.node.contains(e.target)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.toggleList()
|
||||
}
|
||||
|
||||
renderTitleElement = () => {
|
||||
const { titleElement, title } = this.props
|
||||
|
||||
if (titleElement) {
|
||||
return titleElement;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span className="dd-title">{title}</span>
|
||||
<span className="dd-caret-down"></span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
toggleList = () => {
|
||||
const { open } = this.state
|
||||
let state = true
|
||||
|
||||
document.addEventListener('mousedown', this.handleMouseClick, false)
|
||||
|
||||
if (open) {
|
||||
document.removeEventListener('mousedown', this.handleMouseClick, false)
|
||||
state = false
|
||||
}
|
||||
|
||||
this.setState({
|
||||
open: state
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='dd-menu' ref={node => this.node = node}>
|
||||
<div className="dd-menu-toggle" onClick={this.toggleList}>
|
||||
{this.renderTitleElement()}
|
||||
</div>
|
||||
|
||||
{this.renderList()}
|
||||
</div >
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Dropdown
|
||||
24
OHIFViewer-react/src/components/ExampleViewportPlugin.js
Normal file
24
OHIFViewer-react/src/components/ExampleViewportPlugin.js
Normal file
@ -0,0 +1,24 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class ExampleViewportPlugin extends Component {
|
||||
static propTypes = {
|
||||
studyInstanceUid: PropTypes.string.isRequired,
|
||||
seriesInstanceUid: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
studyInstanceUid: '',
|
||||
seriesInstanceUid: ''
|
||||
};
|
||||
|
||||
render() {
|
||||
return (<div className="ExampleViewportPlugin">
|
||||
<p>StudyInstanceUid: {this.props.studyInstanceUid}</p>
|
||||
<p>SeriesInstanceUid: {this.props.seriesInstanceUid}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ExampleViewportPlugin;
|
||||
@ -1,10 +1,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { Dropdown } from "../components";
|
||||
import Icons from "../images/icons.svg";
|
||||
import { Dropdown } from "react-viewerbase";
|
||||
import './Header.css';
|
||||
import list from './HeaderMenuList.json';
|
||||
//import Icons from "../../images/icons.svg";
|
||||
|
||||
const Icons = '/icons.svg';
|
||||
|
||||
function Header({ home, location }) {
|
||||
const { state } = location
|
||||
@ -4,7 +4,7 @@ import OHIF from 'ohif-core';
|
||||
|
||||
const { StudyPrefetcher } = OHIF.classes;
|
||||
|
||||
class StudyPrefetch extends Component {
|
||||
class StudyPrefetcherComponent extends Component {
|
||||
static propTypes = {
|
||||
studies: PropTypes.array,
|
||||
};
|
||||
@ -27,4 +27,4 @@ class StudyPrefetch extends Component {
|
||||
}
|
||||
|
||||
|
||||
export default StudyPrefetch;
|
||||
export default StudyPrefetcherComponent;
|
||||
@ -1,5 +0,0 @@
|
||||
import Dropdown from './Dropdown/Dropdown'
|
||||
|
||||
export {
|
||||
Dropdown
|
||||
}
|
||||
@ -30,6 +30,7 @@ cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
|
||||
|
||||
OHIF.external.cornerstone = cornerstone;
|
||||
OHIF.external.cornerstoneTools = cornerstoneTools;
|
||||
OHIF.external.cornerstoneMath = cornerstoneMath;
|
||||
OHIF.external.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
|
||||
|
||||
// TODO: Is there a better way to guess ROOT_URL?
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { LayoutManager } from 'react-viewerbase';
|
||||
import OHIF from 'ohif-core';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const viewportPluginIds = state.plugins.availablePlugins.filter(plugin => plugin.type === OHIF.plugins.PLUGIN_TYPES.VIEWPORT).map(plugin => plugin.id);
|
||||
const availablePlugins = {}
|
||||
viewportPluginIds.forEach(id => {
|
||||
const plugin = OHIF.plugins.availablePlugins.find(plugin => plugin.id === id);
|
||||
if (plugin) {
|
||||
availablePlugins[id] = plugin.component;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO Use something like state.plugins.defaultPlugin[OHIF.plugins.PLUGIN_TYPES.VIEWPORT]
|
||||
let defaultPlugin;
|
||||
if (viewportPluginIds && viewportPluginIds.length) {
|
||||
defaultPlugin = viewportPluginIds[0];
|
||||
}
|
||||
|
||||
return {
|
||||
layout: state.viewports.layout,
|
||||
activeViewportIndex: state.viewports.activeViewportIndex,
|
||||
availablePlugins,
|
||||
defaultPlugin
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedLayoutManager = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(LayoutManager);
|
||||
|
||||
export default ConnectedLayoutManager;
|
||||
@ -1,5 +1,5 @@
|
||||
import { connect } from 'react-redux';
|
||||
import StudyLoadingMonitor from './StudyLoadingMonitor.js';
|
||||
import StudyLoadingMonitor from '../components/StudyLoadingMonitor.js';
|
||||
import OHIF from 'ohif-core'
|
||||
|
||||
const { setStudyLoadingProgress, clearStudyLoadingProgress } = OHIF.redux.actions;
|
||||
@ -1,6 +1,5 @@
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import ViewerFromStudyData from './ViewerFromStudyData.js';
|
||||
import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js';
|
||||
|
||||
const isActive = (a) => a.active === true;
|
||||
|
||||
@ -12,9 +11,9 @@ const mapStateToProps = state => {
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedViewerFromStudyData = connect(
|
||||
const ConnectedViewerRetrieveStudyData = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(ViewerFromStudyData);
|
||||
)(ViewerRetrieveStudyData);
|
||||
|
||||
export default ConnectedViewerFromStudyData;
|
||||
export default ConnectedViewerRetrieveStudyData;
|
||||
@ -1,6 +1,8 @@
|
||||
import { connect } from 'react-redux';
|
||||
import CornerstoneViewport from 'react-cornerstone-viewport';
|
||||
import OHIF from 'ohif-core'
|
||||
import cornerstone from "cornerstone-core";
|
||||
import cornerstoneTools from "cornerstone-tools";
|
||||
|
||||
const { setViewportActive, setViewportSpecificData, clearViewportSpecificData } = OHIF.redux.actions;
|
||||
|
||||
@ -8,7 +10,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
const activeButton = state.tools.buttons.find(tool => tool.active === true);
|
||||
|
||||
// If this is the active viewport, enable prefetching.
|
||||
const { viewportIndex } = ownProps.viewportData;
|
||||
const { viewportIndex } = ownProps;//.viewportData;
|
||||
const isActive = viewportIndex === state.viewports.activeViewportIndex;
|
||||
const viewportSpecificData = state.viewports.viewportSpecificData[viewportIndex] || {};
|
||||
|
||||
@ -18,12 +20,14 @@ const mapStateToProps = (state, ownProps) => {
|
||||
activeTool: activeButton && activeButton.command,
|
||||
enableStackPrefetch: isActive,
|
||||
//stack: viewportSpecificData.stack,
|
||||
cineToolData: viewportSpecificData.cine
|
||||
cineToolData: viewportSpecificData.cine,
|
||||
cornerstone,
|
||||
cornerstoneTools
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
||||
const viewportIndex = ownProps.viewportData.viewportIndex;
|
||||
const { viewportIndex } = ownProps;
|
||||
|
||||
return {
|
||||
setViewportActive: () => {
|
||||
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
function OHIFComponentPlugin(props) {
|
||||
return (<React.Fragment>{props.children}</React.Fragment>);
|
||||
}
|
||||
|
||||
OHIFComponentPlugin.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
init: PropTypes.func.isRequired,
|
||||
destroy: PropTypes.func.isRequired,
|
||||
children: PropTypes.node.isRequired
|
||||
};
|
||||
|
||||
export default OHIFComponentPlugin;
|
||||
@ -0,0 +1,72 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import OHIF from 'ohif-core';
|
||||
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
|
||||
import OHIFComponentPlugin from './OHIFComponentPlugin.js';
|
||||
|
||||
const { StackManager } = OHIF.utils;
|
||||
|
||||
class OHIFCornerstoneViewportPlugin extends Component {
|
||||
static propTypes = {
|
||||
studies: PropTypes.object,
|
||||
displaySet: PropTypes.object,
|
||||
viewportIndex: PropTypes.number,
|
||||
};
|
||||
|
||||
static id = 'CornerstoneViewportPlugin';
|
||||
|
||||
static init() {
|
||||
console.log('CornerstoneViewportPlugin init()');
|
||||
}
|
||||
|
||||
static destroy() {
|
||||
console.log('CornerstoneViewportPlugin destroy()');
|
||||
StackManager.clearStacks();
|
||||
}
|
||||
|
||||
static getCornerstoneStack(studies, studyInstanceUid, displaySetInstanceUid) {
|
||||
// Create shortcut to displaySet
|
||||
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
|
||||
|
||||
const displaySet = study.displaySets.find((set) => {
|
||||
return set.displaySetInstanceUid === displaySetInstanceUid;
|
||||
});
|
||||
|
||||
// Get stack from Stack Manager
|
||||
return StackManager.findOrCreateStack(study, displaySet);
|
||||
}
|
||||
|
||||
static getPluginViewportData = (studies, studyInstanceUid, displaySetInstanceUid) => {
|
||||
const currentStack = OHIFCornerstoneViewportPlugin.getCornerstoneStack(studies, studyInstanceUid, displaySetInstanceUid);
|
||||
|
||||
// Clone the stack here so we don't mutate it later
|
||||
const stack = Object.assign({}, currentStack);
|
||||
stack.currentImageIdIndex = 0;
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { viewportIndex } = this.props;
|
||||
const { studies, displaySet } = this.props.viewportData;
|
||||
const { studyInstanceUid, displaySetInstanceUid } = displaySet;
|
||||
const stack = OHIFCornerstoneViewportPlugin.getPluginViewportData(studies, studyInstanceUid, displaySetInstanceUid);
|
||||
const viewportData = {
|
||||
studyInstanceUid,
|
||||
displaySetInstanceUid,
|
||||
stack
|
||||
};
|
||||
|
||||
const { id, init, destroy } = OHIFCornerstoneViewportPlugin;
|
||||
const pluginProps = { id, init, destroy };
|
||||
|
||||
return (
|
||||
<OHIFComponentPlugin {...pluginProps}>
|
||||
<ConnectedCornerstoneViewport viewportData={viewportData} viewportIndex={viewportIndex}/>
|
||||
</OHIFComponentPlugin>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default OHIFCornerstoneViewportPlugin;
|
||||
@ -1,11 +1,13 @@
|
||||
import React, { Component } from "react";
|
||||
import ConnectedToolbarSection from './ConnectedToolbarSection';
|
||||
import ConnectedLayoutButton from './ConnectedLayoutButton';
|
||||
import ConnectedCineDialog from '../components/ConnectedCineDialog.js';
|
||||
import ConnectedCineDialog from './ConnectedCineDialog.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import { RoundedButtonGroup } from 'react-viewerbase';
|
||||
import './ToolbarRow.css';
|
||||
import Icons from "../images/icons.svg"
|
||||
//import Icons from "../images/icons.svg"
|
||||
|
||||
const Icons = '/icons.svg';
|
||||
|
||||
class ToolbarRow extends Component {
|
||||
static propTypes = {
|
||||
@ -5,11 +5,11 @@ import cornerstoneTools from 'cornerstone-tools';
|
||||
import OHIF from 'ohif-core';
|
||||
//import { CineDialog } from 'react-viewerbase';
|
||||
|
||||
import Header from '../Header'
|
||||
import ConnectedFlexboxLayout from '../FlexboxLayout/ConnectedFlexboxLayout.js';
|
||||
import ConnectedToolbarRow from "../FlexboxLayout/ConnectedToolbarRow";
|
||||
import Header from '../components/Header'
|
||||
import ConnectedFlexboxLayout from './ConnectedFlexboxLayout.js';
|
||||
import ConnectedToolbarRow from "./ConnectedToolbarRow";
|
||||
import ConnectedStudyLoadingMonitor from './ConnectedStudyLoadingMonitor.js';
|
||||
import StudyPrefetch from './StudyPrefetch.js';
|
||||
import StudyPrefetcher from '../components/StudyPrefetcher.js';
|
||||
import './Viewer.css';
|
||||
|
||||
const { StackManager } = OHIF.utils;
|
||||
@ -26,9 +26,6 @@ const { StackManager } = OHIF.utils;
|
||||
// Gets all StudyMetadata objects: necessary for Hanging Protocol to access study metadata
|
||||
const studyMetadataList = OHIF.viewer.StudyMetadataList.all();
|
||||
|
||||
// Caches Layout Manager: Hanging Protocol uses it for layout management according to current protocol
|
||||
const layoutManager = OHIF.viewerbase.layoutManager;
|
||||
|
||||
// Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata
|
||||
const studyMetadataSource = new OHIF.studies.classes.OHIFStudyMetadataSource();
|
||||
|
||||
@ -85,7 +82,7 @@ class Viewer extends Component {
|
||||
<div id="viewer" className='Viewer'>
|
||||
<ConnectedToolbarRow />
|
||||
<ConnectedStudyLoadingMonitor studies={this.props.studies} />
|
||||
<StudyPrefetch studies={this.props.studies} />
|
||||
<StudyPrefetcher studies={this.props.studies} />
|
||||
<ConnectedFlexboxLayout studies={this.props.studies} />
|
||||
</div>
|
||||
</>
|
||||
117
OHIFViewer-react/src/connectedComponents/ViewerMain.js
Normal file
117
OHIFViewer-react/src/connectedComponents/ViewerMain.js
Normal file
@ -0,0 +1,117 @@
|
||||
import { Component } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import cornerstoneMath from "cornerstone-math";
|
||||
import { OHIF } from 'ohif-core';
|
||||
import ConnectedLayoutManager from './ConnectedLayoutManager.js';
|
||||
import './ViewerMain.css';
|
||||
|
||||
// Attempt to fix weird undefined dep issue
|
||||
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
|
||||
|
||||
class ViewerMain extends Component {
|
||||
state = {
|
||||
viewportData: [],
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
studies: PropTypes.array.isRequired
|
||||
};
|
||||
|
||||
getDisplaySetsWithImages(studies) {
|
||||
const displaySets = [];
|
||||
studies.forEach((study) => {
|
||||
study.displaySets.forEach(dSet => {
|
||||
if (dSet.images.length) {
|
||||
displaySets.push(dSet);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return displaySets;
|
||||
}
|
||||
|
||||
findDisplaySet(studies, studyInstanceUid, displaySetInstanceUid) {
|
||||
const study = studies.find(study => {
|
||||
return study.studyInstanceUid === studyInstanceUid;
|
||||
});
|
||||
|
||||
if (!study) {
|
||||
return;
|
||||
}
|
||||
|
||||
return study.displaySets.find(displaySet => {
|
||||
return displaySet.displaySetInstanceUid === displaySetInstanceUid;
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Add beforeUnload event handler to check for unsaved changes
|
||||
//window.addEventListener('beforeunload', unloadHandlers.beforeUnload);
|
||||
|
||||
// Get all the display sets for the viewer studies
|
||||
const displaySets = this.getDisplaySetsWithImages(this.props.studies);
|
||||
|
||||
this.setState({
|
||||
viewportData: displaySets
|
||||
});
|
||||
}
|
||||
|
||||
setViewportData = ({viewportIndex, item}) => {
|
||||
// TODO: Replace this with mapDispatchToProps call
|
||||
// if we decide to put viewport info into redux
|
||||
|
||||
// Note: Use Slice because React does a shallow equality check. Mutating the array
|
||||
// would not trigger a re-render. We have to create a copy.
|
||||
debugger;
|
||||
console.warn(item);
|
||||
const updatedViewportData = this.state.viewportData.slice(0);
|
||||
|
||||
const displaySet = this.findDisplaySet(this.props.studies, item.studyInstanceUid, item.displaySetInstanceUid);
|
||||
|
||||
console.warn(displaySet);
|
||||
|
||||
updatedViewportData[viewportIndex] = Object.assign({}, displaySet);
|
||||
|
||||
this.setState({
|
||||
viewportData: updatedViewportData
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="ViewerMain">
|
||||
<ConnectedLayoutManager
|
||||
studies={this.props.studies}
|
||||
viewportData={this.state.viewportData}
|
||||
setViewportData={this.setViewportData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// Remove beforeUnload event handler...
|
||||
//window.removeEventListener('beforeunload', unloadHandlers.beforeUnload);
|
||||
|
||||
// Destroy the synchronizer used to update reference lines
|
||||
OHIF.viewer.updateImageSynchronizer.destroy();
|
||||
|
||||
// TODO: Instruct all plugins to clean up themselves
|
||||
//
|
||||
// Clear references to all stacks in the StackManager
|
||||
//StackManager.clearStacks();
|
||||
|
||||
// @TypeSafeStudies
|
||||
// Clears OHIF.viewer.Studies collection
|
||||
//OHIF.viewer.Studies.removeAll();
|
||||
|
||||
// @TypeSafeStudies
|
||||
// Clears OHIF.viewer.StudyMetadataList collection
|
||||
//OHIF.viewer.StudyMetadataList.removeAll();
|
||||
}
|
||||
}
|
||||
|
||||
export default ViewerMain;
|
||||
@ -1,11 +1,11 @@
|
||||
import React, {Component} from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import OHIF from 'ohif-core';
|
||||
import Viewer from "./Viewer/Viewer.js";
|
||||
import Viewer from "./Viewer.js";
|
||||
|
||||
const { createDisplaySets } = OHIF.utils;
|
||||
|
||||
class ViewerFromStudyData extends Component {
|
||||
class ViewerRetrieveStudyData extends Component {
|
||||
static propTypes = {
|
||||
studyInstanceUids: PropTypes.array.isRequired,
|
||||
seriesInstanceUids: PropTypes.array,
|
||||
@ -53,4 +53,4 @@ class ViewerFromStudyData extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default ViewerFromStudyData;
|
||||
export default ViewerRetrieveStudyData;
|
||||
@ -5,17 +5,19 @@ import { Provider } from 'react-redux';
|
||||
import { createStore, combineReducers } from 'redux';
|
||||
import { loadUser, reducer as oidcReducer, OidcProvider} from 'redux-oidc';
|
||||
import OHIF from 'ohif-core';
|
||||
|
||||
import OHIFCornerstoneViewportPlugin from "./connectedComponents/OHIFCornerstoneViewportPlugin/OHIFCornerstoneViewportPlugin.js";
|
||||
import './config';
|
||||
import ui from './redux/ui.js';
|
||||
import App from './App.js';
|
||||
import ui from './redux/ui.js'
|
||||
import userManager from './userManager.js';
|
||||
import Icons from "./images/icons.svg"
|
||||
import ConnectedExampleViewportPlugin from './components/ConnectedExampleViewportPlugin.js';
|
||||
|
||||
const reducers = OHIF.redux.reducers;
|
||||
reducers.ui = ui;
|
||||
reducers.oidc = oidcReducer;
|
||||
|
||||
const Icons = '/icons.svg';
|
||||
|
||||
const combined = combineReducers(reducers)
|
||||
|
||||
const store = createStore(combined);
|
||||
@ -87,11 +89,26 @@ const defaultButtons = [
|
||||
},
|
||||
];
|
||||
|
||||
store.dispatch({
|
||||
type: 'SET_AVAILABLE_BUTTONS',
|
||||
buttons: defaultButtons
|
||||
const buttonsAction = OHIF.redux.actions.setAvailableButtons(defaultButtons);
|
||||
|
||||
store.dispatch(buttonsAction);
|
||||
|
||||
// Uncomment this and comment the Cornerstone version to see how the
|
||||
// example plugin works
|
||||
/*const pluginAction = OHIF.redux.actions.addPlugin({
|
||||
id: 'example',
|
||||
type: 'viewport',
|
||||
component: ConnectedExampleViewportPlugin
|
||||
});*/
|
||||
|
||||
const pluginAction = OHIF.redux.actions.addPlugin({
|
||||
id: 'cornerstone',
|
||||
type: 'viewport',
|
||||
component: OHIFCornerstoneViewportPlugin
|
||||
});
|
||||
|
||||
store.dispatch(pluginAction);
|
||||
|
||||
const servers = {
|
||||
dicomWeb: [
|
||||
{
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import ViewerFromStudyData from "./ViewerFromStudyData.js";
|
||||
import ViewerRetrieveStudyData from "../connectedComponents/ViewerRetrieveStudyData.js";
|
||||
|
||||
function IHEInvokeImageDisplay({ match }) {
|
||||
const requestType = match.params.query.requestType;
|
||||
@ -26,7 +26,7 @@ function IHEInvokeImageDisplay({ match }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ViewerFromStudyData studyInstanceUids={studyInstanceUids}/>
|
||||
<ViewerRetrieveStudyData studyInstanceUids={studyInstanceUids}/>
|
||||
);
|
||||
}
|
||||
|
||||
104
OHIFViewer-react/src/routes/StandaloneRouting.js
Normal file
104
OHIFViewer-react/src/routes/StandaloneRouting.js
Normal file
@ -0,0 +1,104 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import qs from 'query-string'
|
||||
import Viewer from "../connectedComponents/Viewer";
|
||||
import OHIF from 'ohif-core';
|
||||
|
||||
const { createDisplaySets } = OHIF.utils;
|
||||
|
||||
class StandaloneRouting extends Component {
|
||||
state = {
|
||||
studies: null,
|
||||
error: null
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
location: PropTypes.object,
|
||||
store: PropTypes.object
|
||||
};
|
||||
|
||||
static parseQueryAndFetchStudies(query) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = query.url;
|
||||
|
||||
if (!url) {
|
||||
reject(new Error('No URL was specified. Use ?url=$yourURL'));
|
||||
}
|
||||
|
||||
// Define a request to the server to retrieve the study data
|
||||
// as JSON, given a URL that was in the Route
|
||||
const oReq = new XMLHttpRequest();
|
||||
|
||||
// Add event listeners for request failure
|
||||
oReq.addEventListener('error', (error) => {
|
||||
OHIF.log.warn('An error occurred while retrieving the JSON data');
|
||||
reject(error)
|
||||
});
|
||||
|
||||
// When the JSON has been returned, parse it into a JavaScript Object
|
||||
// and render the OHIF Viewer with this data
|
||||
oReq.addEventListener('load', () => {
|
||||
// Parse the response content
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseText
|
||||
if (!oReq.responseText) {
|
||||
OHIF.log.warn('Response was undefined');
|
||||
reject(new Error('Response was undefined'));
|
||||
}
|
||||
|
||||
OHIF.log.info(JSON.stringify(oReq.responseText, null, 2));
|
||||
|
||||
const data = JSON.parse(oReq.responseText);
|
||||
if (data.servers && query.studyInstanceUids) {
|
||||
const server = data.servers.dicomWeb[0];
|
||||
server.type = 'dicomWeb';
|
||||
|
||||
const studyInstanceUids = query.studyInstanceUids.split(';');
|
||||
const seriesInstanceUids = [];
|
||||
|
||||
OHIF.studies.retrieveStudiesMetadata(server, studyInstanceUids, seriesInstanceUids).then(studies => {
|
||||
resolve(studies);
|
||||
}, error => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
resolve(data.studies);
|
||||
}
|
||||
});
|
||||
|
||||
// Open the Request to the server for the JSON data
|
||||
// In this case we have a server-side route called /api/
|
||||
// which responds to GET requests with the study data
|
||||
OHIF.log.info(`Sending Request to: ${url}`);
|
||||
oReq.open('GET', url);
|
||||
oReq.setRequestHeader('Accept', 'application/json')
|
||||
|
||||
// Fire the request to the server
|
||||
oReq.send();
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const query = qs.parse(this.props.location.search);
|
||||
StandaloneRouting.parseQueryAndFetchStudies(query).then(studies => {
|
||||
const updatedStudies = createDisplaySets(studies);
|
||||
|
||||
this.setState({ studies: updatedStudies });
|
||||
}, error => {
|
||||
this.setState({ error });
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
return (<div>Error: {JSON.stringify(this.state.error)}</div>);
|
||||
} else if (!this.state.studies) {
|
||||
return (<div>Loading...</div>);
|
||||
}
|
||||
|
||||
return (
|
||||
<Viewer studies={this.state.studies}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default StandaloneRouting;
|
||||
@ -1,8 +1,6 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import ConnectedViewerFromStudyData from "./ConnectedViewerFromStudyData";
|
||||
|
||||
// TODO: Move to react-viewerbase
|
||||
import ConnectedViewerRetrieveStudyData from "../connectedComponents/ConnectedViewerRetrieveStudyData";
|
||||
|
||||
function ViewerRouting({ match }) {
|
||||
const { studyInstanceUids, seriesInstanceUids } = match.params;
|
||||
@ -18,7 +16,7 @@ function ViewerRouting({ match }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ConnectedViewerFromStudyData studyInstanceUids={studyUIDs} seriesInstanceUids={seriesUIDs}/>
|
||||
<ConnectedViewerRetrieveStudyData studyInstanceUids={studyUIDs} seriesInstanceUids={seriesUIDs}/>
|
||||
);
|
||||
}
|
||||
|
||||
17
OHIFViewer-react/src/routes/index.js
Normal file
17
OHIFViewer-react/src/routes/index.js
Normal file
@ -0,0 +1,17 @@
|
||||
import IHEInvokeImageDisplay from './IHEInvokeImageDisplay.js';
|
||||
import StandaloneRouting from './StandaloneRouting.js';
|
||||
import ViewerRouting from './ViewerRouting.js';
|
||||
|
||||
export {
|
||||
IHEInvokeImageDisplay,
|
||||
ViewerRouting,
|
||||
StandaloneRouting
|
||||
};
|
||||
|
||||
const routes = {
|
||||
IHEInvokeImageDisplay,
|
||||
ViewerRouting,
|
||||
StandaloneRouting
|
||||
};
|
||||
|
||||
export default routes;
|
||||
@ -3,7 +3,7 @@ import PropTypes from "prop-types";
|
||||
import OHIF from 'ohif-core';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { StudyList } from "react-viewerbase";
|
||||
import Header from "./Header";
|
||||
import Header from "../components/Header";
|
||||
|
||||
class StudyListWithData extends Component {
|
||||
state = {
|
||||
File diff suppressed because it is too large
Load Diff
1
Packages-react/ohif-core
Submodule
1
Packages-react/ohif-core
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 521cd30ac0525f1a9882969fdcaa54e2bf75a825
|
||||
15
Packages-react/ohif-viewer/.babelrc
Normal file
15
Packages-react/ohif-viewer/.babelrc
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", {
|
||||
"targets": {
|
||||
"ie": "11"
|
||||
}
|
||||
}],
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-transform-regenerator",
|
||||
"@babel/plugin-transform-runtime"
|
||||
]
|
||||
}
|
||||
21
Packages-react/ohif-viewer/LICENSE
Normal file
21
Packages-react/ohif-viewer/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Open Health Imaging Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
43
Packages-react/ohif-viewer/README.md
Normal file
43
Packages-react/ohif-viewer/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
# react-viewerbase
|
||||
|
||||
> A set of React components for Medical Imaging Viewers.
|
||||
|
||||
*Status: Under development*
|
||||
|
||||
[](https://www.npmjs.com/package/react-viewerbase)
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install --save react-viewerbase
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
import React, { Component } from 'react'
|
||||
|
||||
import CornerstoneViewport from 'react-viewerbase'
|
||||
|
||||
class Example extends Component {
|
||||
render () {
|
||||
return (
|
||||
<CornerstoneViewport />
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## To run locally
|
||||
1. Clone this repository and build the library
|
||||
1. `cd react-viewerbase`
|
||||
1. `yarn install`
|
||||
1. `yarn start`
|
||||
1. Open a new terminal (or tab) and build the example application.
|
||||
1. `cd example`
|
||||
1. `yarn install`
|
||||
1. `yarn start`
|
||||
|
||||
## License
|
||||
|
||||
MIT © [OHIF](https://github.com/OHIF)
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
35041
Packages-react/ohif-viewer/dist/index.es.js
vendored
Normal file
35041
Packages-react/ohif-viewer/dist/index.es.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
Packages-react/ohif-viewer/dist/index.es.js.map
vendored
Normal file
1
Packages-react/ohif-viewer/dist/index.es.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
35048
Packages-react/ohif-viewer/dist/index.js
vendored
Normal file
35048
Packages-react/ohif-viewer/dist/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
Packages-react/ohif-viewer/dist/index.js.map
vendored
Normal file
1
Packages-react/ohif-viewer/dist/index.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
35051
Packages-react/ohif-viewer/dist/index.umd.js
vendored
Normal file
35051
Packages-react/ohif-viewer/dist/index.umd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
Packages-react/ohif-viewer/dist/index.umd.js.map
vendored
Normal file
1
Packages-react/ohif-viewer/dist/index.umd.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
3
Packages-react/ohif-viewer/example/.gitignore
vendored
Normal file
3
Packages-react/ohif-viewer/example/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
yarn.lock
|
||||
yarn-error.log
|
||||
2164
Packages-react/ohif-viewer/example/README.md
Normal file
2164
Packages-react/ohif-viewer/example/README.md
Normal file
File diff suppressed because it is too large
Load Diff
3
Packages-react/ohif-viewer/example/cornerstoneWADOImageLoader.min.js
vendored
Normal file
3
Packages-react/ohif-viewer/example/cornerstoneWADOImageLoader.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
Packages-react/ohif-viewer/example/cornerstoneWADOImageLoaderCodecs.min.js
vendored
Normal file
4
Packages-react/ohif-viewer/example/cornerstoneWADOImageLoaderCodecs.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
Packages-react/ohif-viewer/example/cornerstoneWADOImageLoaderWebWorker.min.js
vendored
Normal file
3
Packages-react/ohif-viewer/example/cornerstoneWADOImageLoaderWebWorker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
237
Packages-react/ohif-viewer/example/icons.svg
Normal file
237
Packages-react/ohif-viewer/example/icons.svg
Normal file
@ -0,0 +1,237 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<symbol id="icon-hud" viewBox="0 0 20 22">
|
||||
<title>HUD</title>
|
||||
<g id="icon-hud-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path id="icon-hud-dashes" d="m14.5,2.75 2,0 a2.5 2.5 0 0 1 2.5 2.5 l0,13.25 a2.5 2.5 0 0 1 -2.5 2.5 l-11.5,0 a2.5 2.5 0 0 1 -2.5 -2.5 l0,-1" stroke-dasharray="1,3" />
|
||||
<path id="icon-hud-box" d="m0.5,2 0,14 a1.5 1.5 0 0 0 1.5 1.5 l12,0 a1.5 1.5 0 0 0 1.5 -1.5 l0,-14" />
|
||||
<path id="icon-hud-top" d="m2,2 12,0" stroke-width="4" />
|
||||
<path id="icon-hud-paralel" d="m4.4,7.5 7.2,0 m0,3 -7.2,0 m0,3 7.2,0" stroke-linecap="square" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-measurements-additional" viewBox="0 0 16 16">
|
||||
<title>Additional Measurements</title>
|
||||
<g id="icon-measurements-additional-group" stroke="none">
|
||||
<path id="icon-measurements-additional-boxes" d="M11,2 l1.85,-1.85 a1 1 0 0 1 1,0 l2,2 a1 1 0 0 1 0,1 l-1.85,1.85Z M10,3 l-10,10 0,3 3,0 10,-10Z M16,16 l0,-2 -9,0 -2,2Z" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-measurements-lesions" viewBox="0 0 18 10">
|
||||
<title>Lesions</title>
|
||||
<g id="icon-measurements-lesions-group" stroke="none">
|
||||
<path id="icon-measurements-lesions-boxes" d="M0,0 2,0 2,2 0,2Z M4,0 18,0 18,2 4,2Z M0,4 2,4 2,6 0,6Z M4,4 18,4 18,6 4,6Z M0,8 2,8 2,10 0,10Z M4,8 18,8 18,10 4,10Z" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-settings" viewBox="0 0 15 15">
|
||||
<title>Settings</title>
|
||||
<path d="M12.85 8.25C12.85 8.03 12.93 7.8 12.93 7.5 12.93 7.2 12.93 6.98 12.85 6.75L14.43 5.48C14.58 5.33 14.58 5.18 14.5 5.03L13 2.4C12.93 2.33 12.78 2.25 12.55 2.33L10.68 3.08C10.3 2.78 9.85 2.55 9.4 2.33L9.1 0.38C9.18 0.15 8.95 0 8.8 0L5.8 0C5.65 0 5.43 0.15 5.43 0.3L5.13 2.33C4.68 2.48 4.3 2.78 3.85 3.08L2.05 2.33C1.83 2.25 1.68 2.33 1.53 2.48L0.03 5.1C-0.05 5.18 0.03 5.4 0.18 5.55L1.75 6.75C1.75 6.98 1.68 7.2 1.68 7.5 1.68 7.8 1.68 8.03 1.75 8.25L0.18 9.53C0.03 9.68 0.03 9.82 0.1 9.98L1.6 12.6C1.68 12.68 1.83 12.75 2.05 12.68L3.93 11.93C4.3 12.23 4.75 12.45 5.2 12.68L5.5 14.63C5.5 14.78 5.65 14.93 5.88 14.93L8.88 14.93C9.03 14.93 9.25 14.78 9.25 14.63L9.55 12.68C10 12.45 10.45 12.23 10.83 11.93L12.7 12.68C12.85 12.75 13.08 12.68 13.15 12.53L14.65 9.9C14.73 9.75 14.73 9.53 14.58 9.45L12.85 8.25 12.85 8.25ZM7.3 10.13C5.88 10.13 4.68 8.93 4.68 7.5 4.68 6.08 5.88 4.88 7.3 4.88 8.73 4.88 9.93 6.08 9.93 7.5 9.93 8.93 8.73 10.13 7.3 10.13L7.3 10.13Z" id="Shape"/>
|
||||
</symbol>
|
||||
<symbol id="icon-status-complete" viewBox="0 0 27 27">
|
||||
<title>Complete</title>
|
||||
<g id="icon-status-complete-group" stroke="none">
|
||||
<path id="icon-status-complete-circle" d="M13.5,0 a13.5 13.5 0 0 1 0 27 a13.5 13.5 0 0 1 0 -27 M23,8 l-2,-2 -10,10, -5,-5 -2,2 7,7Z" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-status-lock" viewBox="0 0 16 21">
|
||||
<title>Locked</title>
|
||||
<path d="M14 7L13 7 13 5C13 2.2 10.8 0 8 0 5.2 0 3 2.2 3 5L3 7 2 7C0.9 7 0 7.9 0 9L0 19C0 20.1 0.9 21 2 21L14 21C15.1 21 16 20.1 16 19L16 9C16 7.9 15.1 7 14 7L14 7ZM11.1 7L4.9 7 4.9 5C4.9 3.3 6.3 1.9 8 1.9 9.7 1.9 11.1 3.3 11.1 5L11.1 7 11.1 7Z"/>
|
||||
</symbol>
|
||||
<symbol id="icon-studies" viewBox="0 0 15 13">
|
||||
<title>Studies</title>
|
||||
<g id="icon-studies-group" stroke="none">
|
||||
<path id="icon-studies-boxes" d="M0,0 7,0 7,6 0,6Z M8,0 15,0 15,6 8,6Z M0,7 7,7 7,13 0,13Z M8,7 15,7 15,13 8,13Z" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-levels" viewBox="0 0 18 18">
|
||||
<title>Window / Level</title>
|
||||
<g id="icon-tools-levels-group">
|
||||
<path id="icon-tools-levels-path" d="M14.5,3.5 a1 1 0 0 1 -11,11 Z" stroke="none" opacity="0.8" />
|
||||
<circle id="icon-tools-levels-circle" cx="9" cy="9" r="8" fill="none" stroke-width="2" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-link" viewBox="0 0 23 15">
|
||||
<title>Link</title>
|
||||
<g id="icon-tools-link-group" stroke="none">
|
||||
<circle id="icon-tools-link-circle-l" opacity="0.8" cx="7.5" cy="7.5" r="7.5" />
|
||||
<circle id="icon-tools-link-circle-r" opacity="0.6" cx="15.5" cy="7.5" r="7.5" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-measure-non-target" viewBox="0 0 24 24">
|
||||
<title>Non-Target Measurement</title>
|
||||
<g id="icon-tools-measure-non-target-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle id="icon-tools-measure-non-target-circle" cx="6.5" cy="6.5" r="6" />
|
||||
<path id="icon-tools-measure-non-target-plus" d="M6.5,3 l0,7 M3,6.5 l7,0"></path>
|
||||
<path id="icon-tools-measure-non-target-arrow" d="M23,7 l-15,15 M7,17 l0,6 6,0" stroke-width="2" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-measure-target" viewBox="0 0 26 26">
|
||||
<title>Target Measurement</title>
|
||||
<g id="icon-tools-measure-target-group" stroke-width="0">
|
||||
<path id="icon-tools-measure-target-ruler" d="m9.5,26 2,-2 -1.5,-1.5 0.5,-0.5 1.5,1.5 1.5,-1.5 -2.5,-2.5 0.5,-0.5 2.5,2.5 1.5,-1.5 -1.5,-1.5 0.5,-0.5 1.5,1.5 1.5,-1.5 -2.5,-2.5 0.5,-0.5 2.5,2.5 1.5,-1.5 -1.5,-1.5 0.5,-0.5 1.5,1.5 1.5,-1.5 -2.5,-2.5 0.5,-0.5 2.5,2.5 1.5,-1.5 -1.5,-1.5 0.5,-0.5 1.5,1.5 2,-2 -4,-4 a2 2 0 0 0 -2.5 0 l-14 14 a2 2 0 0 0 0 2.5 l4,4 Z" />
|
||||
<path id="icon-tools-measure-target-plus" d="m6,0 a6 6 0 0 1 0 12 a6 6 0 0 1 0 -12 m0.5,3 a0.5 0.5 0 0 0 -1 0 l0,2.5 -2.5,0 a0.5 0.5 0 0 0 0 1 l2.5,0 0,2.5 a0.5 0.5 0 0 0 1 0 l0,-2.5 2.5,0 a0.5 0.5 0 0 0 0 -1 l-2.5,0 z" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-measure-target-cr" viewBox="0 0 24 24">
|
||||
<title>Target CR Measurement</title>
|
||||
<g id="icon-tools-measure-target-cr-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<text id="icon-tools-measure-target-cr-text" fill="#000000" x="0" y="8" style="font-size:10px; font-family:sans-serif">CR</text>
|
||||
<path id="icon-tools-measure-target-cr-arrow" d="M23,7 l-15,15 M7,17 l0,6 6,0" stroke-width="2" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-measure-target-un" viewBox="0 0 24 24">
|
||||
<title>Target UN Measurement</title>
|
||||
<g id="icon-tools-measure-target-un-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<text id="icon-tools-measure-target-un-text" fill="#000000" x="0" y="8" style="font-size:10px; font-family:sans-serif">UN</text>
|
||||
<path id="icon-tools-measure-target-un-arrow" d="M23,7 l-15,15 M7,17 l0,6 6,0" stroke-width="2" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-measure-temp" viewBox="0 0 24 24">
|
||||
<title>Temporary Measurement</title>
|
||||
<g id="icon-tools-measure-temp-group" fill="none" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle id="icon-tools-measure-temp-circle" cx="6.5" cy="6.5" r="6" />
|
||||
<path id="icon-tools-measure-temp-plus" d="M6.5,3 l0,7 M3,6.5 l7,0"></path>
|
||||
<path id="icon-tools-measure-temp-dashes" d="m22.5,6 -16.5,16.5" stroke-width="3" stroke-dasharray="0.6666,5" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-more" viewBox="0 0 18 18">
|
||||
<title>More</title>
|
||||
<g id="icon-tools-more-group" stroke="none">
|
||||
<path id="icon-tools-more-circle" d="M9,0 a9 9 0 0 1 0 18 a9 9 0 0 1 0 -18 M4.5,8 a1.5 1.5 0 0 0 0 3 a1.5 1.5 0 0 0 0 -3 M9,8 a1.5 1.5 0 0 0 0 3 a1.5 1.5 0 0 0 0 -3 M13.5,8 a1.5 1.5 0 0 0 0 3 a1.5 1.5 0 0 0 0 -3" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-pan" viewBox="0 0 18 18">
|
||||
<title>Pan</title>
|
||||
<g id="icon-tools-pan-group" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path id="icon-tools-pan-line-v" d="M9,1 L9,17"></path>
|
||||
<path id="icon-tools-pan-line-h" d="M1,9 L17,9"></path>
|
||||
<polyline id="icon-tools-pan-caret-t" points="7 3 9 1 11 3"></polyline>
|
||||
<polyline id="icon-tools-pan-caret-r" points="15 11 17 9 15 7"></polyline>
|
||||
<polyline id="icon-tools-pan-caret-b" points="11 15 9 17 7 15"></polyline>
|
||||
<polyline id="icon-tools-pan-caret-l" points="3 7 1 9 3 11"></polyline>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-zoom" viewBox="0 0 17 17">
|
||||
<title>Zoom</title>
|
||||
<g id="icon-tools-zoom-group" fill="none" stroke-width="2" stroke-linecap="round">
|
||||
<path id="icon-tools-zoom-path" d="m11.5,11.5 4.5,4.5" />
|
||||
<circle id="icon-tools-zoom-circle" cx="7" cy="7" r="6" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-invert" viewBox="0 0 24 28">
|
||||
<title>Invert</title>
|
||||
<path d="M12 22.5v-17c-4.688 0-8.5 3.813-8.5 8.5s3.813 8.5 8.5 8.5zM24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12 12 5.375 12 12z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-stack-scroll" viewBox="0 0 24 28">
|
||||
<title>Stack Scroll</title>
|
||||
<path d="M24 21v2c0 0.547-0.453 1-1 1h-22c-0.547 0-1-0.453-1-1v-2c0-0.547 0.453-1 1-1h22c0.547 0 1 0.453 1 1zM24 13v2c0 0.547-0.453 1-1 1h-22c-0.547 0-1-0.453-1-1v-2c0-0.547 0.453-1 1-1h22c0.547 0 1 0.453 1 1zM24 5v2c0 0.547-0.453 1-1 1h-22c-0.547 0-1-0.453-1-1v-2c0-0.547 0.453-1 1-1h22c0.547 0 1 0.453 1 1z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-elliptical-roi" viewBox="0 0 24 28">
|
||||
<title>Elliptical ROI</title>
|
||||
<path d="M12 5.5c-4.688 0-8.5 3.813-8.5 8.5s3.813 8.5 8.5 8.5 8.5-3.813 8.5-8.5-3.813-8.5-8.5-8.5zM24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12v0c6.625 0 12 5.375 12 12z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-magnify" viewBox="0 0 24 28">
|
||||
<title>Magnify</title>
|
||||
<path d="M24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12 12 5.375 12 12z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-reset" viewBox="0 0 24 28">
|
||||
<title>Reset</title>
|
||||
<path d="M24 14c0 6.609-5.391 12-12 12-3.578 0-6.953-1.578-9.234-4.328-0.156-0.203-0.141-0.5 0.031-0.672l2.141-2.156c0.109-0.094 0.25-0.141 0.391-0.141 0.141 0.016 0.281 0.078 0.359 0.187 1.531 1.984 3.828 3.109 6.312 3.109 4.406 0 8-3.594 8-8s-3.594-8-8-8c-2.047 0-3.984 0.781-5.437 2.141l2.141 2.156c0.297 0.281 0.375 0.719 0.219 1.078-0.156 0.375-0.516 0.625-0.922 0.625h-7c-0.547 0-1-0.453-1-1v-7c0-0.406 0.25-0.766 0.625-0.922 0.359-0.156 0.797-0.078 1.078 0.219l2.031 2.016c2.203-2.078 5.187-3.313 8.266-3.313 6.609 0 12 5.391 12 12z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-rotate" viewBox="0 0 24 28">
|
||||
<title>Rotate</title>
|
||||
<path d="M24 4v7c0 0.547-0.453 1-1 1h-7c-0.406 0-0.766-0.25-0.922-0.625-0.156-0.359-0.078-0.797 0.219-1.078l2.156-2.156c-1.469-1.359-3.406-2.141-5.453-2.141-4.406 0-8 3.594-8 8s3.594 8 8 8c2.484 0 4.781-1.125 6.312-3.109 0.078-0.109 0.219-0.172 0.359-0.187 0.141 0 0.281 0.047 0.391 0.141l2.141 2.156c0.187 0.172 0.187 0.469 0.031 0.672-2.281 2.75-5.656 4.328-9.234 4.328-6.609 0-12-5.391-12-12s5.391-12 12-12c3.078 0 6.062 1.234 8.266 3.313l2.031-2.016c0.281-0.297 0.719-0.375 1.094-0.219 0.359 0.156 0.609 0.516 0.609 0.922z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-rotate-right" viewBox="0 0 24 24">
|
||||
<title>Rotate Right</title>
|
||||
<path d="M16.875 15.469c0.563-0.75 0.891-1.594 1.031-2.484h2.016c-0.188 1.406-0.703 2.719-1.594 3.891zM12.984 17.906c0.891-0.141 1.734-0.469 2.484-1.031l1.453 1.453c-1.172 0.891-2.531 1.406-3.938 1.594v-2.016zM19.922 11.016h-2.016c-0.141-0.891-0.469-1.734-1.031-2.484l1.453-1.406c0.891 1.172 1.406 2.484 1.594 3.891zM15.563 5.531l-4.547 4.453v-3.891c-2.859 0.469-5.016 2.953-5.016 5.906s2.156 5.438 5.016 5.906v2.016c-3.938-0.469-7.031-3.844-7.031-7.922s3.094-7.453 7.031-7.922v-3.094z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-cineplay-toggle" viewBox="0 0 28 28">
|
||||
<title>Cineplay Toggle</title>
|
||||
<path d="M11.109 17.625l7.562-3.906-7.562-3.953v7.859zM14 4.156c5.891 0 9.797 0.281 9.797 0.281 0.547 0.063 1.75 0.063 2.812 1.188 0 0 0.859 0.844 1.109 2.781 0.297 2.266 0.281 4.531 0.281 4.531v2.125s0.016 2.266-0.281 4.531c-0.25 1.922-1.109 2.781-1.109 2.781-1.062 1.109-2.266 1.109-2.812 1.172 0 0-3.906 0.297-9.797 0.297v0c-7.281-0.063-9.516-0.281-9.516-0.281-0.625-0.109-2.031-0.078-3.094-1.188 0 0-0.859-0.859-1.109-2.781-0.297-2.266-0.281-4.531-0.281-4.531v-2.125s-0.016-2.266 0.281-4.531c0.25-1.937 1.109-2.781 1.109-2.781 1.062-1.125 2.266-1.125 2.812-1.188 0 0 3.906-0.281 9.797-0.281v0z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-flip-vertical" viewBox="0 0 6 28">
|
||||
<title>Vertical</title>
|
||||
<path d="M6 19.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5zM6 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5zM6 3.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-tools-flip-horizontal" viewBox="0 0 22 28">
|
||||
<title>Horizontal</title>
|
||||
<path d="M6 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5zM14 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5zM22 11.5v3c0 0.828-0.672 1.5-1.5 1.5h-3c-0.828 0-1.5-0.672-1.5-1.5v-3c0-0.828 0.672-1.5 1.5-1.5h3c0.828 0 1.5 0.672 1.5 1.5z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-trial-info" viewBox="0 0 20 20">
|
||||
<title>Trial Information</title>
|
||||
<g id="icon-trial-info-group" fill="none" stroke-width="2">
|
||||
<path id="icon-trial-info-circle" d="M10,1 a9 9 0 0 1 0 18 a9 9 0 0 1 0 -18 M10,5 l0,2 m0,2 0,6" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-ui-accordion-expand" viewBox="0 0 12 8">
|
||||
<title>Expand</title>
|
||||
<polygon transform="translate(6.000000, 3.700000) rotate(-270.000000) translate(-6.000000, -3.700000) " points="3.7 -2.3 2.3 -0.9 6.9 3.7 2.3 8.3 3.7 9.7 9.7 3.7"/>
|
||||
</symbol>
|
||||
<symbol id="icon-ui-add" viewBox="0 0 12 13">
|
||||
<title>Add</title>
|
||||
<path d="M6.5 7L6.5 12C6.5 12.28 6.28 12.5 6 12.5 5.72 12.5 5.5 12.28 5.5 12L5.5 7 0.5 7C0.22 7 0 6.78 0 6.5 0 6.22 0.22 6 0.5 6L5.5 6 5.5 1C5.5 0.72 5.72 0.5 6 0.5 6.28 0.5 6.5 0.72 6.5 1L6.5 6 11.5 6C11.78 6 12 6.22 12 6.5 12 6.78 11.78 7 11.5 7L6.5 7Z" id="Combined-Shape"/>
|
||||
</symbol>
|
||||
<symbol id="icon-ui-close" viewBox="0 0 14 14">
|
||||
<title>Close</title>
|
||||
<g id="icon-ui-close-group" stroke-width="1.75">
|
||||
<path id="icon-ui-close-path" d="M1,1 13,13 M1,13 13,1" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-create-comment" viewBox="0 0 37 34">
|
||||
<title>Comment</title>
|
||||
<path fill="#94a8b3" d="M22.32 14L35 14C36.1 14 37 14.9 37 16L37 28C37 29.1 36.1 30 35 30L21 30 17 34 17 20.39C19.45 18.97 21.36 16.71 22.32 14Z" id="Combined-Shape"/>
|
||||
<path d="M10 0C4.5 0 0 4.5 0 10 0 15.5 4.5 20 10 20 15.5 20 20 15.5 20 10 20 4.5 15.5 0 10 0L10 0ZM15 11L11 11 11 15 9 15 9 11 5 11 5 9 9 9 9 5 11 5 11 9 15 9 15 11 15 11Z" id="Shape"/>
|
||||
</symbol>
|
||||
<symbol id="icon-create-screen-capture" viewBox="0 0 37 35">
|
||||
<title>Capture Screen</title>
|
||||
<g id="camera-alt" transform="translate(17.000000, 17.000000)" fill="#94a8b3">
|
||||
<path d="M10 13C11.66 13 13 11.66 13 10 13 8.34 11.66 7 10 7 8.34 7 7 8.34 7 10 7 11.66 8.34 13 10 13Z" id="Oval"/>
|
||||
<path d="M7 0L5.2 2 2 2C0.9 2 0 2.9 0 4L0 16C0 17.1 0.9 18 2 18L18 18C19.1 18 20 17.1 20 16L20 4C20 2.9 19.1 2 18 2L14.8 2 13 0 7 0 7 0ZM10 15C7.2 15 5 12.8 5 10 5 7.2 7.2 5 10 5 12.8 5 15 7.2 15 10 15 12.8 12.8 15 10 15L10 15Z" id="Shape"/>
|
||||
</g>
|
||||
<path d="M10 0C4.5 0 0 4.5 0 10 0 15.5 4.5 20 10 20 15.5 20 20 15.5 20 10 20 4.5 15.5 0 10 0L10 0ZM15 11L11 11 11 15 9 15 9 11 5 11 5 9 9 9 9 5 11 5 11 9 15 9 15 11 15 11Z" id="Shape"/>
|
||||
</symbol>
|
||||
<symbol id="icon-ui-warning" viewBox="0 0 22 20">
|
||||
<title>Warning</title>
|
||||
<g id="icon-ui-warning-group" stroke="none">
|
||||
<path d="m11,0 11,20 -22,0Z m1,7 -2,0 0,6 2,0Z m0,8 -2,0 0,2 2,0Z" />
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-ohif-logo" viewBox="0 0 47 47">
|
||||
<title>Open Health Imaging Foundation</title>
|
||||
<g id="icon-ohif-logo" stroke="#fff" stroke-width="2.5" stroke-miterlimit="10">
|
||||
<rect x="1.25" y="1.25" width="18.8" height="18.8" rx="1" ry="1"/>
|
||||
<rect x="26" y="1.25" width="18.8" height="18.8" rx="1" ry="1"/>
|
||||
<rect x="1.25" y="26" width="18.8" height="18.8" rx="1" ry="1"/>
|
||||
<rect x="26" y="26" width="18.8" height="18.8" rx="1" ry="1"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="icon-viewport-link" viewBox="0 0 32 32">
|
||||
<title>Viewport Link</title>
|
||||
<g id="icon-viewport-link-group" stroke="none">
|
||||
<path d="m5.364 28.917c1.047 1.046 2.422 1.57 3.797 1.57 1.376 0 2.751-0.523 3.798-1.572l7.552-7.554c1.016-1.014 1.574-2.361 1.574-3.797 -0.001-1.432-0.558-2.781-1.574-3.796l-2.297-2.298 -2.009 2.009 2.297 2.298c0.478 0.478 0.741 1.113 0.741 1.788 0 0.674-0.264 1.309-0.741 1.786l-7.553 7.555c-0.987 0.985-2.59 0.985-3.576 0l-2.297-2.298c-0.987-0.985-0.987-2.589 0-3.576l3.775-3.776 -2.009-2.009 -3.776 3.776c-2.094 2.096-2.092 5.502 0 7.595l2.299 2.3zM26.695 2.992"/>
|
||||
<path d="m26.695 2.992c-1.014-1.016-2.362-1.575-3.797-1.575 -0.001 0-0.001 0-0.002 0 -1.435 0-2.784 0.56-3.798 1.573l-7.551 7.553c-1.017 1.016-1.576 2.363-1.576 3.799 0 1.434 0.558 2.784 1.574 3.797l2.297 2.297 2.01-2.009 -2.298-2.297c-0.477-0.477-0.741-1.113-0.741-1.788 0-0.676 0.265-1.311 0.742-1.788l7.553-7.555c0.477-0.477 1.111-0.74 1.789-0.74 0 0 0 0 0.001 0 0.674 0 1.309 0.264 1.786 0.74l2.297 2.299c0.477 0.477 0.74 1.111 0.74 1.788 0 0.674-0.264 1.311-0.74 1.788l-3.776 3.777 2.009 2.009 3.776-3.777c1.014-1.013 1.573-2.363 1.574-3.797 0-1.435-0.56-2.784-1.574-3.797l-2.295-2.296z"/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id="theme" viewBox="0 0 24 24">
|
||||
<title>Theme</title>
|
||||
<path d="M17.484 12c0.844 0 1.5-0.656 1.5-1.5s-0.656-1.5-1.5-1.5-1.5 0.656-1.5 1.5 0.656 1.5 1.5 1.5zM14.484 8.016c0.844 0 1.5-0.656 1.5-1.5s-0.656-1.5-1.5-1.5-1.5 0.656-1.5 1.5 0.656 1.5 1.5 1.5zM9.516 8.016c0.844 0 1.5-0.656 1.5-1.5s-0.656-1.5-1.5-1.5-1.5 0.656-1.5 1.5 0.656 1.5 1.5 1.5zM6.516 12c0.844 0 1.5-0.656 1.5-1.5s-0.656-1.5-1.5-1.5-1.5 0.656-1.5 1.5 0.656 1.5 1.5 1.5zM12 3c4.969 0 9 3.609 9 8.016 0 2.766-2.25 4.969-5.016 4.969h-1.734c-0.844 0-1.5 0.656-1.5 1.5 0 0.375 0.141 0.703 0.375 0.984s0.375 0.656 0.375 1.031c0 0.844-0.656 1.5-1.5 1.5-4.969 0-9-4.031-9-9s4.031-9 9-9z"></path>
|
||||
</symbol>
|
||||
<symbol id="log" viewBox="0 0 24 24">
|
||||
<title>Log</title>
|
||||
<path d="M9.516 14.016c2.484 0 4.5-2.016 4.5-4.5s-2.016-4.5-4.5-4.5-4.5 2.016-4.5 4.5 2.016 4.5 4.5 4.5zM15.516 14.016l4.969 4.969-1.5 1.5-4.969-4.969v-0.797l-0.281-0.281c-1.125 0.984-2.625 1.547-4.219 1.547-3.609 0-6.516-2.859-6.516-6.469s2.906-6.516 6.516-6.516 6.469 2.906 6.469 6.516c0 1.594-0.563 3.094-1.547 4.219l0.281 0.281h0.797z"></path>
|
||||
</symbol>
|
||||
<symbol id="server" viewBox="0 0 24 28">
|
||||
<title>Server</title>
|
||||
<path d="M12 12c4.703 0 9.422-0.844 12-2.656v2.656c0 2.203-5.375 4-12 4s-12-1.797-12-4v-2.656c2.578 1.813 7.297 2.656 12 2.656zM12 24c4.703 0 9.422-0.844 12-2.656v2.656c0 2.203-5.375 4-12 4s-12-1.797-12-4v-2.656c2.578 1.813 7.297 2.656 12 2.656zM12 18c4.703 0 9.422-0.844 12-2.656v2.656c0 2.203-5.375 4-12 4s-12-1.797-12-4v-2.656c2.578 1.813 7.297 2.656 12 2.656zM12 0c6.625 0 12 1.797 12 4v2c0 2.203-5.375 4-12 4s-12-1.797-12-4v-2c0-2.203 5.375-4 12-4z"></path>
|
||||
</symbol>
|
||||
<symbol id="study-list" viewBox="0 0 24 24">
|
||||
<title>Study List</title>
|
||||
<path d="M9 5.016h12v3.984h-12v-3.984zM9 18.984v-3.984h12v3.984h-12zM9 14.016v-4.031h12v4.031h-12zM3.984 9v-3.984h4.031v3.984h-4.031zM3.984 18.984v-3.984h4.031v3.984h-4.031zM3.984 14.016v-4.031h4.031v4.031h-4.031z"></path>
|
||||
</symbol>
|
||||
<symbol id="logout" viewBox="0 0 24 28">
|
||||
<title>Logout</title>
|
||||
<path d="M24 14c0 6.609-5.391 12-12 12s-12-5.391-12-12c0-3.797 1.75-7.297 4.797-9.578 0.891-0.672 2.141-0.5 2.797 0.391 0.672 0.875 0.484 2.141-0.391 2.797-2.031 1.531-3.203 3.859-3.203 6.391 0 4.406 3.594 8 8 8s8-3.594 8-8c0-2.531-1.172-4.859-3.203-6.391-0.875-0.656-1.062-1.922-0.391-2.797 0.656-0.891 1.922-1.062 2.797-0.391 3.047 2.281 4.797 5.781 4.797 9.578zM14 2v10c0 1.094-0.906 2-2 2s-2-0.906-2-2v-10c0-1.094 0.906-2 2-2s2 0.906 2 2z"></path>
|
||||
</symbol>
|
||||
<symbol id="password" viewBox="0 0 18 28">
|
||||
<title>Password</title>
|
||||
<path d="M5 12h8v-3c0-2.203-1.797-4-4-4s-4 1.797-4 4v3zM18 13.5v9c0 0.828-0.672 1.5-1.5 1.5h-15c-0.828 0-1.5-0.672-1.5-1.5v-9c0-0.828 0.672-1.5 1.5-1.5h0.5v-3c0-3.844 3.156-7 7-7s7 3.156 7 7v3h0.5c0.828 0 1.5 0.672 1.5 1.5z"></path>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 20 KiB |
5
Packages-react/ohif-viewer/example/index.css
Normal file
5
Packages-react/ohif-viewer/example/index.css
Normal file
@ -0,0 +1,5 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
103
Packages-react/ohif-viewer/example/index.html
Normal file
103
Packages-react/ohif-viewer/example/index.html
Normal file
@ -0,0 +1,103 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<meta name="description" content="Open Health Imaging Foundation DICOM Viewer">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1,maximum-scale=1,user-scalable=no">
|
||||
<link rel="shortcut icon" type="image/png" href="/images/logo.png" sizes="16x16 32x32 64x64">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta http-equiv="cleartype" content="on">
|
||||
<meta name="MobileOptimized" content="320">
|
||||
<meta name="HandheldFriendly" content="True">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<link rel="manifest" href="./manifest.json">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
|
||||
<!-- Latest compiled and minified CSS -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
|
||||
<!-- WEB FONTS -->
|
||||
<link href="https://fonts.googleapis.com/css?family=Sanchez" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
|
||||
crossorigin="anonymous">
|
||||
|
||||
<title>OHIF Standalone Viewer</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
|
||||
<div id="root"></div>
|
||||
|
||||
<!-- Load React. -->
|
||||
<!-- Note: when deploying, replace "development.js" with "production.min.js". -->
|
||||
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
|
||||
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
|
||||
<!--<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
|
||||
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>!-->
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
var process = {
|
||||
"env": {
|
||||
"NODE_ENV": "production"
|
||||
}
|
||||
};
|
||||
|
||||
window.process = process;
|
||||
</script>
|
||||
<script src="node_modules/ohif-standalone-viewer/dist/index.umd.js" crossorigin></script>
|
||||
|
||||
<!-- Load our React component. -->
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
// TODO: Add viewer config here
|
||||
var props = {};
|
||||
|
||||
/*
|
||||
|
||||
OAuth settings
|
||||
DICOMWeb servers
|
||||
UI settings
|
||||
Plugins
|
||||
- Custom tools / buttons
|
||||
- Custom Sidebar module thing
|
||||
- Custom Viewports
|
||||
- Custom Sop Class Interpreters
|
||||
|
||||
|
||||
base url path
|
||||
|
||||
"ROOT_URL": "http://localhost:3000",
|
||||
"PUBLIC_SETTINGS": {
|
||||
"verifyEmail": false,
|
||||
"ui": {
|
||||
"studyListFunctionsEnabled": true,
|
||||
"leftSidebarOpen": false,
|
||||
"displaySetNavigationLoopOverSeries": false,
|
||||
"displaySetNavigationMultipleViewports": true,
|
||||
"autoPositionMeasurementsTextCallOuts": "TRLB"
|
||||
},
|
||||
"prefetch": {
|
||||
"order": "topdown",
|
||||
"displaySetCount": 1
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: Why do we need to do this? Something is wrong with our rollup settings
|
||||
var Viewer = window.OHIFStandaloneViewer.App;
|
||||
var app = React.createElement(Viewer, props, null);
|
||||
|
||||
ReactDOM.render(app, document.getElementById('root'));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
8
Packages-react/ohif-viewer/example/manifest.json
Normal file
8
Packages-react/ohif-viewer/example/manifest.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"short_name": "ohif-standalone-viewer-example",
|
||||
"name": "ohif-standalone-viewer-example",
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
13
Packages-react/ohif-viewer/example/package.json
Normal file
13
Packages-react/ohif-viewer/example/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "ohif-viewer-example",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"ohif-viewer": "link:../",
|
||||
"serve": "^10.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "serve ."
|
||||
}
|
||||
}
|
||||
3
Packages-react/ohif-viewer/generateStaticSite.sh
Executable file
3
Packages-react/ohif-viewer/generateStaticSite.sh
Executable file
@ -0,0 +1,3 @@
|
||||
yarn run build
|
||||
cd example
|
||||
yarn install
|
||||
111
Packages-react/ohif-viewer/package.json
Normal file
111
Packages-react/ohif-viewer/package.json
Normal file
@ -0,0 +1,111 @@
|
||||
{
|
||||
"name": "ohif-viewer",
|
||||
"version": "0.0.1",
|
||||
"description": "OHIF Viewer",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
"repository": "OHIF/Viewers",
|
||||
"main": "dist/index.js",
|
||||
"browser": "dist/index.umd.js",
|
||||
"module": "dist/index.es.js",
|
||||
"jsnext:main": "dist/index.es.js",
|
||||
"homepage": "http://localhost:3000",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "cross-env CI=1 react-scripts test --env=jsdom",
|
||||
"test:watch": "react-scripts test --env=jsdom",
|
||||
"build": "rollup -c",
|
||||
"start": "rollup -c -w",
|
||||
"lint": "eslint -c .eslintrc --fix src && prettier --single-quote --write src/**/*.{js,jsx,json,css}",
|
||||
"prepare": "npm run build",
|
||||
"predeploy": "cd example && npm install && npm run build",
|
||||
"deploy": "gh-pages -d example/build",
|
||||
"generateStaticSite": "./generateStaticSite.sh"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.0 || ^16.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/plugin-external-helpers": "^7.2.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.2.3",
|
||||
"@babel/plugin-transform-runtime": "^7.2.0",
|
||||
"@babel/preset-env": "^7.2.3",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@svgr/rollup": "^4.1.0",
|
||||
"babel-eslint": "^9.0.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"eslint": "5.6.0",
|
||||
"eslint-plugin-import": "^2.14.0",
|
||||
"eslint-plugin-node": "^8.0.0",
|
||||
"eslint-plugin-promise": "^4.0.1",
|
||||
"eslint-plugin-react": "^7.11.1",
|
||||
"gh-pages": "^2.0.1",
|
||||
"husky": "^1.3.1",
|
||||
"lint-staged": "^8.1.0",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"prettier": "^1.15.3",
|
||||
"react-scripts": "^2.1.2",
|
||||
"rollup": "^0.68.2",
|
||||
"rollup-plugin-babel": "^4.2.0",
|
||||
"rollup-plugin-commonjs": "^9.2.0",
|
||||
"rollup-plugin-json": "^3.1.0",
|
||||
"rollup-plugin-node-builtins": "^2.1.2",
|
||||
"rollup-plugin-node-resolve": "^4.0.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.0",
|
||||
"rollup-plugin-postcss": "^1.6.3",
|
||||
"rollup-plugin-url": "^2.1.0",
|
||||
"stylelint": "^9.9.0",
|
||||
"stylelint-config-recommended": "^2.1.0",
|
||||
"stylus": "^0.54.5"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,jsx,json,css}": [
|
||||
"prettier --single-quote --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"classnames": "^2.2.6",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "3.0.0-b.1512",
|
||||
"cornerstone-wado-image-loader": "^2.2.3",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"hammerjs": "^2.0.8",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"ohif-core": "^0.1.6",
|
||||
"oidc-client": "^1.6.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-bootstrap-modal": "^4.2.0",
|
||||
"react-cornerstone-viewport": "^0.1.8",
|
||||
"react-dnd": "^7.0.2",
|
||||
"react-dnd-html5-backend": "^7.0.2",
|
||||
"react-redux": "^6.0.0",
|
||||
"react-router": "^4.3.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-viewerbase": "^0.2.1",
|
||||
"redux": "^4.0.1",
|
||||
"redux-oidc": "^3.1.0"
|
||||
}
|
||||
}
|
||||
66
Packages-react/ohif-viewer/rollup.config.js
Normal file
66
Packages-react/ohif-viewer/rollup.config.js
Normal file
@ -0,0 +1,66 @@
|
||||
import babel from 'rollup-plugin-babel'
|
||||
import commonjs from 'rollup-plugin-commonjs'
|
||||
import external from 'rollup-plugin-peer-deps-external'
|
||||
import postcss from 'rollup-plugin-postcss'
|
||||
import resolve from 'rollup-plugin-node-resolve'
|
||||
import json from 'rollup-plugin-json'
|
||||
import url from 'rollup-plugin-url'
|
||||
import svgr from '@svgr/rollup'
|
||||
import pkg from './package.json'
|
||||
// Deal with https://github.com/rollup/rollup-plugin-commonjs/issues/297
|
||||
import builtins from 'rollup-plugin-node-builtins';
|
||||
|
||||
export default {
|
||||
external: ['react', 'react-dom'],
|
||||
input: 'src/index.js',
|
||||
output: [
|
||||
{
|
||||
file: pkg.main,
|
||||
format: 'cjs',
|
||||
exports: 'named',
|
||||
sourcemap: true
|
||||
},
|
||||
{
|
||||
file: pkg.browser,
|
||||
format: 'umd',
|
||||
sourcemap: true,
|
||||
exports: 'named',
|
||||
name: 'OHIFStandaloneViewer',
|
||||
globals: {
|
||||
'react': 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'cornerstone-core': 'cornerstone',
|
||||
'cornerstone-tools': 'cornerstoneTools',
|
||||
'cornerstone-math': 'cornerstoneMath',
|
||||
'dicom-parser': 'dicomParser'
|
||||
},
|
||||
},
|
||||
{
|
||||
file: pkg.module,
|
||||
format: 'es',
|
||||
exports: 'named',
|
||||
sourcemap: true,
|
||||
}
|
||||
],
|
||||
plugins: [
|
||||
builtins(),
|
||||
external(),
|
||||
postcss({
|
||||
modules: false
|
||||
}),
|
||||
url(),
|
||||
svgr(),
|
||||
json(),
|
||||
resolve(),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
runtimeHelpers: true
|
||||
}),
|
||||
commonjs({
|
||||
include: 'node_modules/**',
|
||||
namedExports: {
|
||||
'node_modules/react-is/index.js': ['isValidElementType']
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
152
Packages-react/ohif-viewer/src/App.js
Normal file
152
Packages-react/ohif-viewer/src/App.js
Normal file
@ -0,0 +1,152 @@
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { BrowserRouter } 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 OHIFCornerstoneViewportPlugin from './connectedComponents/OHIFCornerstoneViewportPlugin/OHIFCornerstoneViewportPlugin.js';
|
||||
//import { loadUser, reducer as oidcReducer, OidcProvider} from 'redux-oidc';
|
||||
//import userManager from './userManager.js';
|
||||
//import Icons from "./images/icons.svg"
|
||||
|
||||
const Icons = 'icons.svg';
|
||||
|
||||
const reducers = OHIF.redux.reducers;
|
||||
reducers.ui = ui;
|
||||
//reducers.oidc = oidcReducer;
|
||||
|
||||
const combined = combineReducers(reducers)
|
||||
|
||||
const store = createStore(combined);
|
||||
//loadUser(store, userManager);
|
||||
|
||||
const defaultButtons = [
|
||||
{
|
||||
command: 'StackScroll',
|
||||
type: 'tool',
|
||||
text: 'Stack Scroll',
|
||||
svgUrl: `${Icons}#icon-tools-stack-scroll`,
|
||||
active: false
|
||||
},
|
||||
{
|
||||
command: 'Zoom',
|
||||
type: 'tool',
|
||||
text: 'Zoom',
|
||||
svgUrl: `${Icons}#icon-tools-zoom`,
|
||||
active: false
|
||||
},
|
||||
{
|
||||
command: 'Wwwc',
|
||||
type: 'tool',
|
||||
text: 'Levels',
|
||||
svgUrl: `${Icons}#icon-tools-levels`,
|
||||
active: true
|
||||
},
|
||||
{
|
||||
command: 'Pan',
|
||||
type: 'tool',
|
||||
text: 'Pan',
|
||||
svgUrl: `${Icons}#icon-tools-pan`,
|
||||
active: false
|
||||
},
|
||||
{
|
||||
command: 'Length',
|
||||
type: 'tool',
|
||||
text: 'Length',
|
||||
svgUrl: `${Icons}#icon-tools-measure-temp`,
|
||||
active: false
|
||||
},
|
||||
/*{
|
||||
command: 'Annotate',
|
||||
type: 'tool',
|
||||
text: 'Annotate',
|
||||
svgUrl: `${Icons}#icon-tools-measure-non-target`,
|
||||
active: false
|
||||
},*/
|
||||
{
|
||||
command: 'Angle',
|
||||
type: 'tool',
|
||||
text: 'Angle',
|
||||
iconClasses: 'fa fa-angle-left',
|
||||
active: false
|
||||
},
|
||||
{
|
||||
command: 'Bidirectional',
|
||||
type: 'tool',
|
||||
text: 'Bidirectional',
|
||||
svgUrl: `${Icons}#icon-tools-measure-target`,
|
||||
active: false
|
||||
},
|
||||
{
|
||||
command: 'reset',
|
||||
type: 'command',
|
||||
text: 'Reset',
|
||||
svgUrl: `${Icons}#icon-tools-reset`,
|
||||
active: false
|
||||
},
|
||||
];
|
||||
|
||||
const buttonsAction = OHIF.redux.actions.setAvailableButtons(defaultButtons);
|
||||
|
||||
store.dispatch(buttonsAction);
|
||||
|
||||
const pluginAction = OHIF.redux.actions.addPlugin({
|
||||
id: 'cornerstone',
|
||||
type: 'viewport',
|
||||
component: OHIFCornerstoneViewportPlugin
|
||||
});
|
||||
|
||||
store.dispatch(pluginAction);
|
||||
|
||||
const servers = {
|
||||
dicomWeb: [
|
||||
{
|
||||
"name": "DCM4CHEE",
|
||||
//"wadoUriRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/wado",
|
||||
//"qidoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
|
||||
//"wadoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
|
||||
//"wadoUriRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/wado",
|
||||
//"qidoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
|
||||
//"wadoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
|
||||
"wadoUriRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/wado",
|
||||
"qidoRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/rs",
|
||||
"wadoRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/rs",
|
||||
"qidoSupportsIncludeField": true,
|
||||
"imageRendering": "wadors",
|
||||
"thumbnailRendering": "wadors",
|
||||
"requestOptions": {
|
||||
"requestFromBrowser": true,
|
||||
"logRequests": true,
|
||||
"logResponses": false,
|
||||
"logTiming": true,
|
||||
//"auth": "admin:admin"
|
||||
//"auth": "cloud:healthcare"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
OHIF.utils.addServers(servers, store);
|
||||
|
||||
// TODO[react] Use a provider when the whole tree is React
|
||||
window.store = store;
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
{/*<OidcProvider store={store} userManager={userManager}>*/}
|
||||
<BrowserRouter basename={props.routerBasename}>
|
||||
<OHIFStandaloneViewer/>
|
||||
</BrowserRouter>
|
||||
{/*</OidcProvider>*/}
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
17
Packages-react/ohif-viewer/src/OHIFStandaloneViewer.css
Normal file
17
Packages-react/ohif-viewer/src/OHIFStandaloneViewer.css
Normal file
@ -0,0 +1,17 @@
|
||||
html,
|
||||
body,
|
||||
#root {
|
||||
background-color: black;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: Roboto,OpenSans,HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;
|
||||
}
|
||||
106
Packages-react/ohif-viewer/src/OHIFStandaloneViewer.js
Normal file
106
Packages-react/ohif-viewer/src/OHIFStandaloneViewer.js
Normal file
@ -0,0 +1,106 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withRouter } from 'react-router';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { ViewerbaseDragDropContext } from 'react-viewerbase';
|
||||
import ViewerRouting from "./routes/ViewerRouting.js";
|
||||
import StandaloneRouting from './routes/StandaloneRouting.js';
|
||||
import IHEInvokeImageDisplay from './routes/IHEInvokeImageDisplay.js';
|
||||
//import CallbackPage from './CallbackPage.js';
|
||||
//import userManager from "./userManager.js";
|
||||
import './OHIFStandaloneViewer.css';
|
||||
import './variables.css';
|
||||
import './theme-tide.css';
|
||||
|
||||
const reload = () => window.location.reload();
|
||||
|
||||
/*function LoadingUser() {
|
||||
return (<div>Loading user...</div>);
|
||||
}*/
|
||||
|
||||
class App 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unlisten();
|
||||
}
|
||||
|
||||
render() {
|
||||
const user = this.props.user;
|
||||
|
||||
/*if (!user || user.expired) {
|
||||
// TODO: redirect to OAuth page if necessary
|
||||
return <Switch>
|
||||
<Route path="/callback" component={CallbackPage} />
|
||||
<Route exact path='/login' component={() => {
|
||||
userManager.signinRedirect();
|
||||
}}/>
|
||||
<Route component={LoadingUser}/>
|
||||
</Switch>;
|
||||
}*/
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path="/"
|
||||
component={StandaloneRouting}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/viewer"
|
||||
component={StandaloneRouting}
|
||||
/>
|
||||
<Route
|
||||
path="/viewer/:studyInstanceUids"
|
||||
component={ViewerRouting}
|
||||
/>
|
||||
<Route
|
||||
path="/study/:studyInstanceUid/series/:seriesInstanceUids"
|
||||
component={ViewerRouting}
|
||||
/>
|
||||
<Route
|
||||
path="/IHEInvokeImageDisplay"
|
||||
component={IHEInvokeImageDisplay}
|
||||
/>
|
||||
|
||||
<Route path="/silent-refresh.html" onEnter={reload} />
|
||||
<Route path="/logout-redirect.html" onEnter={reload} />
|
||||
{/*<Route exact path='/login' component={() => {
|
||||
userManager.signinRedirect();
|
||||
}}
|
||||
/>*/
|
||||
/*<Route path="/callback" component={CallbackPage} />*/}
|
||||
<Route render={() =>
|
||||
<div> Sorry, this page does not exist. </div>}
|
||||
/>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {};
|
||||
/*return {
|
||||
user: state.oidc.user,
|
||||
};*/
|
||||
};
|
||||
|
||||
const ConnectedApp = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(App);
|
||||
|
||||
export default ViewerbaseDragDropContext(withRouter(ConnectedApp));
|
||||
@ -0,0 +1,20 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ExampleViewportPlugin from './ExampleViewportPlugin';
|
||||
|
||||
// We don't really need Redux to do this, this is just an example
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { displaySet } = ownProps.viewportData;
|
||||
const { studyInstanceUid, seriesInstanceUid } = displaySet;
|
||||
|
||||
return {
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedExampleViewportPlugin = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(ExampleViewportPlugin);
|
||||
|
||||
export default ConnectedExampleViewportPlugin;
|
||||
@ -0,0 +1,24 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class ExampleViewportPlugin extends Component {
|
||||
static propTypes = {
|
||||
studyInstanceUid: PropTypes.string.isRequired,
|
||||
seriesInstanceUid: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
studyInstanceUid: '',
|
||||
seriesInstanceUid: ''
|
||||
};
|
||||
|
||||
render() {
|
||||
return (<div className="ExampleViewportPlugin">
|
||||
<p>StudyInstanceUid: {this.props.studyInstanceUid}</p>
|
||||
<p>SeriesInstanceUid: {this.props.seriesInstanceUid}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ExampleViewportPlugin;
|
||||
108
Packages-react/ohif-viewer/src/components/Header/Header.css
Normal file
108
Packages-react/ohif-viewer/src/components/Header/Header.css
Normal file
@ -0,0 +1,108 @@
|
||||
.entry-header {
|
||||
padding: 10px 10px 0;
|
||||
height: var(--top-bar-height);
|
||||
}
|
||||
|
||||
.entry-header.header-big {
|
||||
background: rgba(21,25,30,0.7);
|
||||
padding: 10px var(--study-list-padding);
|
||||
height: auto;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-left-box {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-viewerLink {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.header-brand {
|
||||
height: 30px;
|
||||
text-decoration: none;
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.entry-header.header-big .header-brand {
|
||||
height: auto;
|
||||
padding: 25px 0;
|
||||
}
|
||||
|
||||
|
||||
.header-logo-image {
|
||||
fill: transparent;
|
||||
height: 100%;
|
||||
margin: 0 8px 0 0;
|
||||
width: 30px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.entry-header.header-big .header-logo-image {
|
||||
margin: 0 20px 0 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.header-logo-text {
|
||||
display: inline-block;
|
||||
font-family: var(--logo-font-family);
|
||||
font-size: 14px;
|
||||
font-weight: var(--logo-font-weight);
|
||||
color: var(--text-primary-color);
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.entry-header.header-big .header-logo-text {
|
||||
font-size: 30px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.header-btn {
|
||||
color: var(--text-secondary-color);
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.header-btn:hover {
|
||||
color: var(--hover-color);
|
||||
}
|
||||
|
||||
.header-btn:active {
|
||||
color: var(--active-color);
|
||||
}
|
||||
|
||||
.header-studyListLinkSection {
|
||||
border-left: var(--ui-border-thickness) solid var(--ui-border-color);
|
||||
margin: 3px 0 0 10px;
|
||||
padding: 0 0 0 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.header-menu {
|
||||
color: var(--text-primary-color);
|
||||
float: right;
|
||||
padding: 4px 0;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
text-decoration: none;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.entry-header.header-big .header-menu {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.header-menu .research-use {
|
||||
float: left;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #91b9cd;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
64
Packages-react/ohif-viewer/src/components/Header/Header.js
Normal file
64
Packages-react/ohif-viewer/src/components/Header/Header.js
Normal file
@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { Dropdown } from "react-viewerbase";
|
||||
import './Header.css';
|
||||
import list from './HeaderMenuList.json';
|
||||
//import Icons from "../../images/icons.svg";
|
||||
|
||||
const Icons = '/icons.svg';
|
||||
|
||||
function Header({ home, location }) {
|
||||
const { state } = location
|
||||
|
||||
return (
|
||||
<div className={`entry-header ${home ? 'header-big' : ''}`}>
|
||||
<div className='header-left-box'>
|
||||
{
|
||||
state && state.studyLink &&
|
||||
<Link to={state.studyLink} className="header-btn header-viewerLink">
|
||||
Back to Viewer
|
||||
</Link>
|
||||
}
|
||||
|
||||
<a target="_blank" rel="noopener noreferrer" className="header-brand" href="http://ohif.org">
|
||||
<svg className="header-logo-image">
|
||||
<use xlinkHref={`${Icons}#icon-ohif-logo`} />
|
||||
</svg>
|
||||
<div className="header-logo-text">Open Health Imaging Foundation</div>
|
||||
</a>
|
||||
|
||||
{!home &&
|
||||
<Link className='header-btn header-studyListLinkSection' to={{
|
||||
pathname: "/",
|
||||
state: { studyLink: location.pathname }
|
||||
}}>Study list</Link>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
<div className="header-menu">
|
||||
<span className="research-use">
|
||||
INVESTIGATIONAL USE ONLY
|
||||
</span>
|
||||
<Dropdown
|
||||
title='Options'
|
||||
list={list}
|
||||
align='right'
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
home: PropTypes.bool.isRequired,
|
||||
location: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
Header.defaultProps = {
|
||||
home: true
|
||||
};
|
||||
|
||||
export default withRouter(Header)
|
||||
@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"title": "Preferences",
|
||||
"icon": "fa fa-user",
|
||||
"link": "http://www.google.com"
|
||||
},
|
||||
{
|
||||
"title": "About",
|
||||
"icon": "fa fa-info",
|
||||
"link": "http://ohif.org"
|
||||
}
|
||||
]
|
||||
@ -0,0 +1,3 @@
|
||||
import Header from './Header'
|
||||
|
||||
export default Header
|
||||
@ -0,0 +1,43 @@
|
||||
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,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// Destroy stack loading listeners when we close the viewer
|
||||
this.studyLoadingListener.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default StudyLoadingMonitor;
|
||||
30
Packages-react/ohif-viewer/src/components/StudyPrefetcher.js
Normal file
30
Packages-react/ohif-viewer/src/components/StudyPrefetcher.js
Normal file
@ -0,0 +1,30 @@
|
||||
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,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const { studies } = this.props;
|
||||
|
||||
this.studyPrefetcher = StudyPrefetcher.getInstance();
|
||||
this.studyPrefetcher.setStudies(studies);
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// Stop prefetching when we close the viewer
|
||||
this.studyPrefetcher.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default StudyPrefetcherComponent;
|
||||
98
Packages-react/ohif-viewer/src/config.js
Normal file
98
Packages-react/ohif-viewer/src/config.js
Normal file
@ -0,0 +1,98 @@
|
||||
import dicomParser from 'dicom-parser';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||
import cornerstoneMath from 'cornerstone-math';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import Hammer from 'hammerjs';
|
||||
import OHIF from 'ohif-core';
|
||||
//import sha from './sha.js';
|
||||
//import version from './version.js';
|
||||
import { homepage } from '../package.json';
|
||||
|
||||
window.info = {
|
||||
//sha,
|
||||
//version,
|
||||
homepage
|
||||
};
|
||||
|
||||
// For debugging
|
||||
window.cornerstone = cornerstone;
|
||||
window.cornerstoneTools = cornerstoneTools;
|
||||
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
|
||||
|
||||
cornerstoneTools.external.cornerstone = cornerstone;
|
||||
cornerstoneTools.external.Hammer = Hammer;
|
||||
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
|
||||
cornerstoneTools.init();
|
||||
|
||||
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
|
||||
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
|
||||
|
||||
OHIF.external.cornerstone = cornerstone;
|
||||
OHIF.external.cornerstoneTools = cornerstoneTools;
|
||||
OHIF.external.cornerstoneMath = cornerstoneMath;
|
||||
OHIF.external.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
|
||||
|
||||
// TODO: Is there a better way to guess ROOT_URL?
|
||||
let ROOT_URL = homepage;
|
||||
|
||||
// If the page we are on is not a subset of the expected homepage
|
||||
// provided in the package.json file, we might be doing local development.
|
||||
// In this case, set the base URL to the current location's origin.
|
||||
if (homepage.includes(window.location.origin) === false) {
|
||||
ROOT_URL = window.location.origin;
|
||||
}
|
||||
|
||||
const config = {
|
||||
maxWebWorkers: Math.max(navigator.hardwareConcurrency - 1, 1),
|
||||
startWebWorkersOnDemand: true,
|
||||
webWorkerPath: ROOT_URL + '/cornerstoneWADOImageLoaderWebWorker.min.js',
|
||||
taskConfiguration: {
|
||||
decodeTask: {
|
||||
loadCodecsOnStartup: true,
|
||||
initializeCodecsOnStartup: false,
|
||||
codecsPath: ROOT_URL + '/cornerstoneWADOImageLoaderCodecs.min.js',
|
||||
usePDFJS: false,
|
||||
strict: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
|
||||
|
||||
|
||||
OHIF.user.getAccessToken = () => {
|
||||
// TODO: Get the Redux store from somewhere else
|
||||
const state = window.store.getState();
|
||||
if (!state.oidc || !state.oidc.user) {
|
||||
return;
|
||||
}
|
||||
|
||||
return state.oidc.user.access_token;
|
||||
}
|
||||
|
||||
cornerstoneWADOImageLoader.configure({
|
||||
beforeSend: function(xhr) {
|
||||
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
|
||||
|
||||
if (headers.Authorization) {
|
||||
xhr.setRequestHeader("Authorization", headers.Authorization);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Set the tool font and font size
|
||||
// context.font = "[style] [variant] [weight] [size]/[line height] [font family]";
|
||||
const fontFamily =
|
||||
'Roboto, OpenSans, HelveticaNeue-Light, Helvetica Neue Light, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif';
|
||||
cornerstoneTools.textStyle.setFont(`16px ${fontFamily}`);
|
||||
|
||||
// Set the tool width
|
||||
cornerstoneTools.toolStyle.setToolWidth(2);
|
||||
// Set color for inactive tools
|
||||
cornerstoneTools.toolColors.setToolColor('rgb(255, 255, 0)');
|
||||
|
||||
// Set color for active tools
|
||||
cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)');
|
||||
|
||||
cornerstoneTools.store.state.touchProximity = 40;
|
||||
@ -0,0 +1,111 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { CineDialog } from 'react-viewerbase';
|
||||
import OHIF from 'ohif-core';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
|
||||
const { setViewportSpecificData } = OHIF.redux.actions;
|
||||
|
||||
// TODO: I'm guessing this function will be used in other connect locations
|
||||
// so we might want to put it somewhere shared
|
||||
function getActiveViewportSpecificData(state) {
|
||||
const { viewportSpecificData, activeViewportIndex } = state.viewports;
|
||||
return viewportSpecificData[activeViewportIndex];
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
// TODO:
|
||||
// - Test if including CineDialog in the toolbarRow will prevent it
|
||||
// from hovering over the rest of the UI when visible.
|
||||
//
|
||||
// - Create custom ToolbarButton which just shows Play state
|
||||
// - Connect this ToolbarButton to Redux
|
||||
const activeViewportSpecificData = getActiveViewportSpecificData(state);
|
||||
|
||||
let stack = {
|
||||
imageIds: [],
|
||||
currentImageIdIndex: 0
|
||||
};
|
||||
if (activeViewportSpecificData && activeViewportSpecificData.stack) {
|
||||
stack = activeViewportSpecificData.stack
|
||||
}
|
||||
|
||||
let cine = {
|
||||
isPlaying: false,
|
||||
cineFrameRate: 24
|
||||
};
|
||||
if (activeViewportSpecificData && activeViewportSpecificData.cine) {
|
||||
cine = activeViewportSpecificData.cine
|
||||
}
|
||||
|
||||
|
||||
// TODO: activeViewportStackData won't currently change anything on
|
||||
// CornerstoneViewport. The updates are too frequent and it's killing
|
||||
// performance. Need to revisit how we can do this.
|
||||
return {
|
||||
activeViewportStackData: stack,
|
||||
activeViewportCineData: cine,
|
||||
activeViewportIndex: state.viewports.activeViewportIndex
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
dispatchSetViewportSpecificData: (viewportIndex, data) => {
|
||||
dispatch(setViewportSpecificData(viewportIndex, data));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
|
||||
const { activeViewportStackData, activeViewportCineData, activeViewportIndex } = propsFromState;
|
||||
|
||||
return {
|
||||
cineFrameRate: activeViewportCineData.cineFrameRate,
|
||||
isPlaying: activeViewportCineData.isPlaying,
|
||||
onPlayPauseChanged: isPlaying => {
|
||||
const cine = cloneDeep(activeViewportCineData);
|
||||
cine.isPlaying = !cine.isPlaying;
|
||||
|
||||
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { cine });
|
||||
},
|
||||
onFrameRateChanged: frameRate => {
|
||||
const cine = cloneDeep(activeViewportCineData);
|
||||
cine.cineFrameRate = frameRate;
|
||||
|
||||
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { cine });
|
||||
},
|
||||
onClickNextButton: () => {
|
||||
const stack = cloneDeep(activeViewportStackData);
|
||||
const largestPossibleIndex = stack.imageIds.length - 1;
|
||||
stack.currentImageIdIndex = Math.min(stack.currentImageIdIndex + 1, largestPossibleIndex)
|
||||
|
||||
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack });
|
||||
},
|
||||
onClickBackButton: () => {
|
||||
const stack = cloneDeep(activeViewportStackData);
|
||||
stack.currentImageIdIndex = Math.max(stack.currentImageIdIndex - 1, 0);
|
||||
|
||||
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack });
|
||||
},
|
||||
onClickSkipToStart: () => {
|
||||
const stack = cloneDeep(activeViewportStackData);
|
||||
stack.currentImageIdIndex = 0;
|
||||
|
||||
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack });
|
||||
},
|
||||
onClickSkipToEnd: () => {
|
||||
const stack = cloneDeep(activeViewportStackData);
|
||||
stack.currentImageIdIndex = stack.imageIds.length;
|
||||
|
||||
propsFromDispatch.dispatchSetViewportSpecificData(activeViewportIndex, { stack });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedCineDialog = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
mergeProps
|
||||
)(CineDialog);
|
||||
|
||||
export default ConnectedCineDialog;
|
||||
@ -0,0 +1,16 @@
|
||||
import { connect } from 'react-redux';
|
||||
import FlexboxLayout from './FlexboxLayout';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
leftSidebarOpen: state.ui.leftSidebarOpen,
|
||||
rightSidebarOpen: state.ui.rightSidebarOpen,
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedFlexboxLayout = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(FlexboxLayout);
|
||||
|
||||
export default ConnectedFlexboxLayout;
|
||||
@ -0,0 +1,41 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { LayoutButton } from 'react-viewerbase';
|
||||
import OHIF from 'ohif-core'
|
||||
|
||||
const { setLayout } = OHIF.redux.actions;
|
||||
|
||||
const mapStateToProps = state => {
|
||||
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
|
||||
};
|
||||
|
||||
dispatch(setLayout(layout))
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedLayoutButton = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(LayoutButton);
|
||||
|
||||
export default ConnectedLayoutButton;
|
||||
@ -0,0 +1,34 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { LayoutManager } from 'react-viewerbase';
|
||||
import OHIF from 'ohif-core';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const viewportPluginIds = state.plugins.availablePlugins.filter(plugin => plugin.type === OHIF.plugins.PLUGIN_TYPES.VIEWPORT).map(plugin => plugin.id);
|
||||
const availablePlugins = {}
|
||||
viewportPluginIds.forEach(id => {
|
||||
const plugin = OHIF.plugins.availablePlugins.find(plugin => plugin.id === id);
|
||||
if (plugin) {
|
||||
availablePlugins[id] = plugin.component;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO Use something like state.plugins.defaultPlugin[OHIF.plugins.PLUGIN_TYPES.VIEWPORT]
|
||||
let defaultPlugin;
|
||||
if (viewportPluginIds && viewportPluginIds.length) {
|
||||
defaultPlugin = viewportPluginIds[0];
|
||||
}
|
||||
|
||||
return {
|
||||
layout: state.viewports.layout,
|
||||
activeViewportIndex: state.viewports.activeViewportIndex,
|
||||
availablePlugins,
|
||||
defaultPlugin
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedLayoutManager = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(LayoutManager);
|
||||
|
||||
export default ConnectedLayoutManager;
|
||||
@ -0,0 +1,40 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { StudyBrowser } from 'react-viewerbase';
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
|
||||
// TODO
|
||||
// - 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
data.stackPercentComplete = stackPercentComplete;
|
||||
})
|
||||
});
|
||||
|
||||
return {
|
||||
studies: studiesWithLoadingData
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedStudyBrowser = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(StudyBrowser);
|
||||
|
||||
export default ConnectedStudyBrowser;
|
||||
@ -0,0 +1,23 @@
|
||||
import { connect } from 'react-redux';
|
||||
import StudyLoadingMonitor from '../components/StudyLoadingMonitor.js';
|
||||
import OHIF from 'ohif-core'
|
||||
|
||||
const { setStudyLoadingProgress, clearStudyLoadingProgress } = OHIF.redux.actions;
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
setStudyLoadingProgress: (progressId, progressData) => {
|
||||
dispatch(setStudyLoadingProgress(progressId, progressData))
|
||||
},
|
||||
clearStudyLoadingProgress: progressId => {
|
||||
dispatch(clearStudyLoadingProgress(progressId))
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedStudyLoadingMonitor = connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(StudyLoadingMonitor);
|
||||
|
||||
export default ConnectedStudyLoadingMonitor;
|
||||
@ -0,0 +1,29 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ToolbarRow from './ToolbarRow';
|
||||
import { setLeftSidebarOpen, setRightSidebarOpen } from '../redux/actions.js';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
leftSidebarOpen: state.ui.leftSidebarOpen,
|
||||
rightSidebarOpen: state.ui.rightSidebarOpen
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
setLeftSidebarOpen: state => {
|
||||
dispatch(setLeftSidebarOpen(state))
|
||||
},
|
||||
setRightSidebarOpen: state => {
|
||||
dispatch(setRightSidebarOpen(state))
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedToolbarRow = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ToolbarRow);
|
||||
|
||||
export default ConnectedToolbarRow;
|
||||
@ -0,0 +1,29 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { ToolbarSection } from 'react-viewerbase';
|
||||
import OHIF from 'ohif-core'
|
||||
|
||||
const { setToolActive } = OHIF.redux.actions;
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const activeButton = state.tools.buttons.find(tool => tool.active === true);
|
||||
|
||||
return {
|
||||
buttons: state.tools.buttons,
|
||||
activeCommand: activeButton && activeButton.command
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
setToolActive: tool => {
|
||||
dispatch(setToolActive(tool.command))
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedToolbarSection = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ToolbarSection);
|
||||
|
||||
export default ConnectedToolbarSection;
|
||||
@ -0,0 +1,19 @@
|
||||
import { connect } from 'react-redux';
|
||||
import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js';
|
||||
|
||||
const isActive = (a) => a.active === true;
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const activeServer = state.servers.servers.find(isActive);
|
||||
|
||||
return {
|
||||
server: activeServer,
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedViewerRetrieveStudyData = connect(
|
||||
mapStateToProps,
|
||||
null
|
||||
)(ViewerRetrieveStudyData);
|
||||
|
||||
export default ConnectedViewerRetrieveStudyData;
|
||||
@ -0,0 +1,47 @@
|
||||
.FlexboxLayout {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-flow: row nowrap;
|
||||
align-items: stretch;
|
||||
height: calc(100% - var(--toolbar-height));
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar-menu {
|
||||
height: 100%;
|
||||
/* required transformation to make inner fixed elements relative to this one*/
|
||||
transform: scale(1);
|
||||
transition: var(--sidebar-transition);
|
||||
}
|
||||
|
||||
.sidebar-left {
|
||||
border-right: var(--ui-border-thickness) solid var(--ui-border-color);
|
||||
flex: 1;
|
||||
margin-left: calc(var(--left-sidebar-menu-width) * -1);
|
||||
max-width: var(--left-sidebar-menu-width);
|
||||
order: 1
|
||||
}
|
||||
|
||||
.sidebar-left.sidebar-open {
|
||||
margin-left: 0
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
order: 2;
|
||||
overflow: hidden;
|
||||
transition: var(--sidebar-transition);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebar-right {
|
||||
flex: 1;
|
||||
margin-right: calc(var(--right-sidebar-menu-width) * -1);
|
||||
max-width: var(--right-sidebar-menu-width);
|
||||
order: 3;
|
||||
}
|
||||
|
||||
.sidebar-right.sidebar-open {
|
||||
margin-right: 0;
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
|
||||
import ViewerMain from './ViewerMain.js';
|
||||
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();
|
||||
|
||||
this.setState({
|
||||
studiesForBrowser
|
||||
});
|
||||
}
|
||||
|
||||
getStudiesForBrowser = () => {
|
||||
const { studies } = this.props;
|
||||
|
||||
// TODO[react]:
|
||||
// - Add sorting of display sets
|
||||
// - Add useMiddleSeriesInstanceAsThumbnail
|
||||
// - Add showStackLoadingProgressBar option
|
||||
return studies.map((study) => {
|
||||
const { studyInstanceUid } = study;
|
||||
|
||||
const thumbnails = study.displaySets.map((displaySet) => {
|
||||
const { displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames } = displaySet;
|
||||
const imageId = displaySet.images[0].getImageId();
|
||||
|
||||
return {
|
||||
imageId,
|
||||
displaySetInstanceUid,
|
||||
seriesDescription,
|
||||
seriesNumber,
|
||||
instanceNumber,
|
||||
numImageFrames,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
studyInstanceUid,
|
||||
thumbnails
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let mainContentClassName = "main-content"
|
||||
if (this.props.leftSidebarOpen) {
|
||||
mainContentClassName += ' sidebar-left-open';
|
||||
}
|
||||
|
||||
if (this.props.rightSidebarOpen) {
|
||||
mainContentClassName += ' sidebar-right-open';
|
||||
}
|
||||
|
||||
// TODO[react]: Add measurementLightTable
|
||||
return (
|
||||
<div className="FlexboxLayout">
|
||||
<div className={this.props.leftSidebarOpen ? "sidebar-menu sidebar-left sidebar-open" : "sidebar-menu sidebar-left"}>
|
||||
<ConnectedStudyBrowser studies={this.state.studiesForBrowser}/>
|
||||
</div>
|
||||
<div className={mainContentClassName}>
|
||||
<ViewerMain studies={this.props.studies}/>
|
||||
</div>
|
||||
{/* <div className={this.props.rightSidebarOpen ? "sidebar-menu sidebar-right sidebar-open" : "sidebar-menu sidebar-right"}>
|
||||
{{> measurementLightTable (clone this)}}
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FlexboxLayout;
|
||||
@ -0,0 +1,52 @@
|
||||
import { connect } from 'react-redux';
|
||||
import CornerstoneViewport from 'react-cornerstone-viewport';
|
||||
import OHIF from 'ohif-core'
|
||||
import cornerstone from "cornerstone-core";
|
||||
import cornerstoneTools from "cornerstone-tools";
|
||||
|
||||
const { setViewportActive, setViewportSpecificData, clearViewportSpecificData } = OHIF.redux.actions;
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const activeButton = state.tools.buttons.find(tool => tool.active === true);
|
||||
|
||||
// If this is the active viewport, enable prefetching.
|
||||
const { viewportIndex } = ownProps;//.viewportData;
|
||||
const isActive = viewportIndex === state.viewports.activeViewportIndex;
|
||||
const viewportSpecificData = state.viewports.viewportSpecificData[viewportIndex] || {};
|
||||
|
||||
return {
|
||||
layout: state.viewports.layout,
|
||||
isActive,
|
||||
activeTool: activeButton && activeButton.command,
|
||||
enableStackPrefetch: isActive,
|
||||
//stack: viewportSpecificData.stack,
|
||||
cineToolData: viewportSpecificData.cine,
|
||||
cornerstone,
|
||||
cornerstoneTools
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
||||
const { viewportIndex } = ownProps;
|
||||
|
||||
return {
|
||||
setViewportActive: () => {
|
||||
dispatch(setViewportActive(viewportIndex))
|
||||
},
|
||||
|
||||
setViewportSpecificData: (data) => {
|
||||
dispatch(setViewportSpecificData(viewportIndex, data))
|
||||
},
|
||||
|
||||
clearViewportSpecificData: () => {
|
||||
dispatch(clearViewportSpecificData(viewportIndex));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedCornerstoneViewport = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(CornerstoneViewport);
|
||||
|
||||
export default ConnectedCornerstoneViewport;
|
||||
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
function OHIFComponentPlugin(props) {
|
||||
return (<React.Fragment>{props.children}</React.Fragment>);
|
||||
}
|
||||
|
||||
OHIFComponentPlugin.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
init: PropTypes.func.isRequired,
|
||||
destroy: PropTypes.func.isRequired,
|
||||
children: PropTypes.node.isRequired
|
||||
};
|
||||
|
||||
export default OHIFComponentPlugin;
|
||||
@ -0,0 +1,72 @@
|
||||
import React, { Component } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import OHIF from 'ohif-core';
|
||||
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
|
||||
import OHIFComponentPlugin from './OHIFComponentPlugin.js';
|
||||
|
||||
const { StackManager } = OHIF.utils;
|
||||
|
||||
class OHIFCornerstoneViewportPlugin extends Component {
|
||||
static propTypes = {
|
||||
studies: PropTypes.object,
|
||||
displaySet: PropTypes.object,
|
||||
viewportIndex: PropTypes.number,
|
||||
};
|
||||
|
||||
static id = 'CornerstoneViewportPlugin';
|
||||
|
||||
static init() {
|
||||
console.log('CornerstoneViewportPlugin init()');
|
||||
}
|
||||
|
||||
static destroy() {
|
||||
console.log('CornerstoneViewportPlugin destroy()');
|
||||
StackManager.clearStacks();
|
||||
}
|
||||
|
||||
static getCornerstoneStack(studies, studyInstanceUid, displaySetInstanceUid) {
|
||||
// Create shortcut to displaySet
|
||||
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
|
||||
|
||||
const displaySet = study.displaySets.find((set) => {
|
||||
return set.displaySetInstanceUid === displaySetInstanceUid;
|
||||
});
|
||||
|
||||
// Get stack from Stack Manager
|
||||
return StackManager.findOrCreateStack(study, displaySet);
|
||||
}
|
||||
|
||||
static getPluginViewportData = (studies, studyInstanceUid, displaySetInstanceUid) => {
|
||||
const currentStack = OHIFCornerstoneViewportPlugin.getCornerstoneStack(studies, studyInstanceUid, displaySetInstanceUid);
|
||||
|
||||
// Clone the stack here so we don't mutate it later
|
||||
const stack = Object.assign({}, currentStack);
|
||||
stack.currentImageIdIndex = 0;
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { viewportIndex } = this.props;
|
||||
const { studies, displaySet } = this.props.viewportData;
|
||||
const { studyInstanceUid, displaySetInstanceUid } = displaySet;
|
||||
const stack = OHIFCornerstoneViewportPlugin.getPluginViewportData(studies, studyInstanceUid, displaySetInstanceUid);
|
||||
const viewportData = {
|
||||
studyInstanceUid,
|
||||
displaySetInstanceUid,
|
||||
stack
|
||||
};
|
||||
|
||||
const { id, init, destroy } = OHIFCornerstoneViewportPlugin;
|
||||
const pluginProps = { id, init, destroy };
|
||||
|
||||
return (
|
||||
<OHIFComponentPlugin {...pluginProps}>
|
||||
<ConnectedCornerstoneViewport viewportData={viewportData} viewportIndex={viewportIndex}/>
|
||||
</OHIFComponentPlugin>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default OHIFCornerstoneViewportPlugin;
|
||||
@ -0,0 +1,16 @@
|
||||
.ToolbarRow {
|
||||
border-bottom: var(--ui-border-thickness) solid var(--ui-border-color);
|
||||
flex: 0 0 auto;
|
||||
height: var(--toolbar-height);
|
||||
padding-top: 6px;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
position: relative;
|
||||
transition: height 300ms ease;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ToolbarRow.expanded {
|
||||
height: calc(var(--toolbar-height) + var(--toolbar-drawer-height));
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
import React, { Component } from "react";
|
||||
import ConnectedToolbarSection from './ConnectedToolbarSection';
|
||||
import ConnectedLayoutButton from './ConnectedLayoutButton';
|
||||
import ConnectedCineDialog from './ConnectedCineDialog.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import { RoundedButtonGroup } from 'react-viewerbase';
|
||||
import './ToolbarRow.css';
|
||||
//import Icons from "../images/icons.svg"
|
||||
|
||||
const Icons = '/icons.svg';
|
||||
|
||||
class ToolbarRow extends Component {
|
||||
static propTypes = {
|
||||
leftSidebarOpen: PropTypes.bool.isRequired,
|
||||
rightSidebarOpen: PropTypes.bool.isRequired,
|
||||
setLeftSidebarOpen: PropTypes.func,
|
||||
setRightSidebarOpen: PropTypes.func
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
leftSidebarOpen: false,
|
||||
rightSidebarOpen: false
|
||||
};
|
||||
|
||||
onLeftSidebarValueChanged = (value) => {
|
||||
this.props.setLeftSidebarOpen(!!value);
|
||||
}
|
||||
|
||||
render() {
|
||||
const leftSidebarToggle = [{
|
||||
value: 'studies',
|
||||
svgLink: `${Icons}#icon-studies`,
|
||||
svgWidth: 15,
|
||||
svgHeight: 13,
|
||||
bottomLabel: 'Series',
|
||||
}];
|
||||
|
||||
const leftSidebarValue = this.props.leftSidebarOpen ? leftSidebarToggle[0].value : null;
|
||||
|
||||
return (<div className="ToolbarRow">
|
||||
<div className="pull-left m-t-1 p-y-1" style={{ padding: '10px' }}>
|
||||
<RoundedButtonGroup options={leftSidebarToggle} value={leftSidebarValue} onValueChanged={this.onLeftSidebarValueChanged}/>
|
||||
</div>
|
||||
<ConnectedToolbarSection/>
|
||||
<ConnectedLayoutButton/>
|
||||
{/* TODO: Putting cine dialog here for now */}
|
||||
<div style={{ marginLeft: '100px', display: 'inline-block'}}>
|
||||
<ConnectedCineDialog/>
|
||||
</div>
|
||||
<div className="pull-right m-t-1 rm-x-1">
|
||||
{/* > roundedButtonGroup rightSidebarToggleButtonData */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ToolbarRow;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user