Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73a3f07e2b | ||
|
|
4ea239a953 | ||
|
|
625d5d70a7 | ||
|
|
5c3d8b37b6 | ||
|
|
8bbbf3a889 | ||
|
|
d5bf72851a | ||
|
|
2914c83032 | ||
|
|
31e542dfd9 |
No files matched your search
@@ -193,7 +193,8 @@ today, create a GitHub issue!
|
||||
|
||||
The `ExtensionManager` is a class made available to us via the `@ohif/core`
|
||||
project (platform/core). Our application instantiates a single instance of it,
|
||||
and provides a `ServicesManager` and `CommandsManager`.
|
||||
and provides a `ServicesManager` and `CommandsManager` along with the application's
|
||||
configuration through the appConfig key (optional).
|
||||
|
||||
```js
|
||||
const commandsManager = new CommandsManager();
|
||||
@@ -201,6 +202,7 @@ const servicesManager = new ServicesManager();
|
||||
const extensionManager = new ExtensionManager({
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
appConfig
|
||||
});
|
||||
```
|
||||
|
||||
@@ -210,8 +212,8 @@ The `ExtensionManager` only has a few public members:
|
||||
- `registerExtensions` - Registers an array of extensions
|
||||
- `modules` - An object containing registered extensions by `MODULE_TYPE`
|
||||
|
||||
During registration, lifecycle hooks and modules have access to
|
||||
`ExtensionManager`'s `ServicesManager` and `CommandsManager`.
|
||||
During registration, lifecycle hooks and modules have access to the extension's config,
|
||||
the application's config and `ExtensionManager`'s `ServicesManager` and `CommandsManager` instances.
|
||||
|
||||
Our `@ohif/viewer` uses the `modules` member to access registered extensions at
|
||||
appropriate places in our application.
|
||||
|
||||
@@ -14,6 +14,7 @@ export default {
|
||||
preRegistration({
|
||||
servicesManager = {},
|
||||
commandsManager = {},
|
||||
appConfig = {},
|
||||
configuration = {},
|
||||
}) {},
|
||||
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.1.0...@ohif/extension-cornerstone@2.1.1) (2019-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 add WwwcRegionTool to cornerstone tools initialization ([#1302](https://github.com/OHIF/Viewers/issues/1302)) ([d5bf728](https://github.com/OHIF/Viewers/commit/d5bf72851a32dff9fd3fc09332ea5250bc7e6114))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.0.2...@ohif/extension-cornerstone@2.1.0) (2019-12-11)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "2.1.0",
|
||||
"version": "2.1.1",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import { SimpleDialog } from '@ohif/ui';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import csTools from 'cornerstone-tools';
|
||||
import initCornerstoneTools from './initCornerstoneTools.js';
|
||||
import queryString from 'query-string';
|
||||
import { SimpleDialog } from '@ohif/ui';
|
||||
import merge from 'lodash.merge';
|
||||
import queryString from 'query-string';
|
||||
import initCornerstoneTools from './initCornerstoneTools.js';
|
||||
|
||||
function fallbackMetaDataProvider(type, imageId) {
|
||||
if (!imageId.includes('wado?requestType=WADO')) {
|
||||
@@ -81,6 +81,7 @@ export default function init({ servicesManager, configuration }) {
|
||||
csTools.PanTool,
|
||||
csTools.ZoomTool,
|
||||
csTools.WwwcTool,
|
||||
csTools.WwwcRegionTool,
|
||||
csTools.MagnifyTool,
|
||||
csTools.StackScrollTool,
|
||||
csTools.StackScrollMouseWheelTool,
|
||||
@@ -132,7 +133,7 @@ export default function init({ servicesManager, configuration }) {
|
||||
csTools.setToolPassive(toolName); // there may be a better place to determine name; may not be on uninstantiated class
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
|
||||
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
|
||||
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.0...@ohif/extension-dicom-pdf@1.0.1) (2019-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Update ohif pdf extension to optionally use pdfjs ([#1162](https://github.com/OHIF/Viewers/issues/1162)) ([31e542d](https://github.com/OHIF/Viewers/commit/31e542dfd973c3e982ede6d02d79bfe65175e792)), closes [#1049](https://github.com/OHIF/Viewers/issues/1049)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.0.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@0.50.4...@ohif/extension-dicom-pdf@1.0.0) (2019-09-27)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-pdf",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"description": "OHIF extension for Dicom PDF",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,8 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"classnames": "^2.2.6",
|
||||
"lodash.isequal": "^4.5.0"
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"pdfjs-dist": "^2.2.228"
|
||||
},
|
||||
"gitHead": "a5baa9228c0eda0df880136bde4420d78e6f8706"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import { connect } from 'react-redux';
|
||||
import OHIFDicomPDFViewport from './OHIFDicomPDFViewport';
|
||||
|
||||
const { setViewportActive } = OHIF.redux.actions;
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { activeViewportIndex } = state.viewports;
|
||||
return { activeViewportIndex };
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, ownProps) => {
|
||||
const { viewportIndex } = ownProps;
|
||||
|
||||
return {
|
||||
setViewportActive: () => {
|
||||
dispatch(setViewportActive(viewportIndex));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedOHIFDicomPDFViewer = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(OHIFDicomPDFViewport);
|
||||
|
||||
export default ConnectedOHIFDicomPDFViewer;
|
||||
@@ -0,0 +1,62 @@
|
||||
.DicomPDFViewport {
|
||||
--header-height: 50px;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
padding: 0.5em;
|
||||
border-bottom: var(--ui-border-thickness) solid var(--ui-border-color);
|
||||
height: var(--header-height);
|
||||
}
|
||||
|
||||
.DicomPDFViewport #canvas {
|
||||
height: calc(100% - var(--header-height));
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #text-layer {
|
||||
height: 100%;
|
||||
margin-top: var(--header-height);
|
||||
}
|
||||
|
||||
.DicomPDFViewport #pdf-canvas-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.DicomPDFViewport canvas, #text-layer {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #toolbar button {
|
||||
color: currentColor;
|
||||
background-color: transparent;
|
||||
font: inherit;
|
||||
border: var(--ui-border-thickness) solid var(--ui-border-color);
|
||||
border-radius: 3px;
|
||||
padding: 0.25em 0.5em;
|
||||
margin-right: 0.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #text-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0.2;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.DicomPDFViewport #text-layer>span {
|
||||
color: transparent;
|
||||
position: absolute;
|
||||
white-space: pre;
|
||||
cursor: text;
|
||||
transform-origin: 0% 0%;
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { Component, createRef } from 'react';
|
||||
import dicomParser from 'dicom-parser';
|
||||
import PDFJS from 'pdfjs-dist';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import TypedArrayProp from './TypedArrayProp';
|
||||
import './DicomPDFViewport.css';
|
||||
|
||||
import pdfjsBuild from 'pdfjs-dist/build/pdf';
|
||||
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
|
||||
pdfjsBuild.GlobalWorkerOptions.workerSrc = pdfjsWorker;
|
||||
|
||||
// TODO: Should probably use dcmjs for this
|
||||
const SOP_CLASS_UIDS = {
|
||||
@@ -8,16 +16,88 @@ const SOP_CLASS_UIDS = {
|
||||
};
|
||||
|
||||
class DicomPDFViewport extends Component {
|
||||
state = {
|
||||
fileURL: null,
|
||||
error: null,
|
||||
};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
fileURL: null,
|
||||
error: null,
|
||||
currentPageIndex: 1,
|
||||
pdf: null,
|
||||
scale: 1,
|
||||
};
|
||||
|
||||
this.canvas = createRef();
|
||||
this.textLayer = createRef();
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
byteArray: TypedArrayProp.uint8,
|
||||
useNative: PropTypes.bool,
|
||||
viewportData: PropTypes.object,
|
||||
activeViewportIndex: PropTypes.number,
|
||||
setViewportActive: PropTypes.func,
|
||||
viewportIndex: PropTypes.number,
|
||||
};
|
||||
|
||||
renderPDF = (dataSet, byteArray) => {
|
||||
static defaultProps = {
|
||||
useNative: false,
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
const dataSet = this.parseByteArray(this.props.byteArray);
|
||||
const fileURL = this.getPDFFileUrl(dataSet, this.props.byteArray);
|
||||
|
||||
this.setState(state => ({ ...state, fileURL }));
|
||||
|
||||
if (!this.props.useNative) {
|
||||
const pdf = await PDFJS.getDocument(fileURL).promise;
|
||||
this.setState(state => ({ ...state, pdf }), () => this.updatePDFCanvas());
|
||||
}
|
||||
}
|
||||
|
||||
updatePDFCanvas = async () => {
|
||||
const { pdf, scale, currentPageIndex } = this.state;
|
||||
const context = this.canvas.getContext('2d');
|
||||
|
||||
const page = await pdf.getPage(currentPageIndex);
|
||||
let viewport = page.getViewport({ scale });
|
||||
|
||||
this.canvas.height = viewport.height;
|
||||
this.canvas.width = viewport.width;
|
||||
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport,
|
||||
};
|
||||
|
||||
await page.render(renderContext);
|
||||
const textContent = await page.getTextContent();
|
||||
|
||||
this.textLayer.innerHTML = '';
|
||||
this.textLayer.style.height = viewport.height + 'px';
|
||||
this.textLayer.style.width = viewport.width + 'px';
|
||||
|
||||
PDFJS.renderTextLayer({
|
||||
textContent,
|
||||
container: this.textLayer,
|
||||
viewport,
|
||||
textDivs: [],
|
||||
});
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { currentPageIndex, scale } = this.state;
|
||||
const newValidScale = prevState.scale !== scale && scale > 0;
|
||||
const newValidPageNumber =
|
||||
prevState.currentPageIndex !== currentPageIndex && currentPageIndex > 0;
|
||||
|
||||
if (newValidScale || newValidPageNumber) {
|
||||
this.updatePDFCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
getPDFFileUrl = (dataSet, byteArray) => {
|
||||
let pdfByteArray = byteArray;
|
||||
|
||||
if (dataSet) {
|
||||
@@ -36,50 +116,144 @@ class DicomPDFViewport extends Component {
|
||||
const PDF = new Blob([pdfByteArray], { type: 'application/pdf' });
|
||||
const fileURL = URL.createObjectURL(PDF);
|
||||
|
||||
this.setState({
|
||||
fileURL,
|
||||
});
|
||||
return fileURL;
|
||||
};
|
||||
|
||||
onPageChange = async event => {
|
||||
const { currentPageIndex, pdf } = this.state;
|
||||
let newPageIndex = currentPageIndex;
|
||||
|
||||
const action = event.target.getAttribute('data-pager');
|
||||
if (action === 'prev') {
|
||||
if (currentPageIndex === 1) {
|
||||
return;
|
||||
}
|
||||
newPageIndex -= 1;
|
||||
if (currentPageIndex < 0) {
|
||||
newPageIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (action === 'next') {
|
||||
if (currentPageIndex === pdf.numPages - 1) {
|
||||
return;
|
||||
}
|
||||
newPageIndex += 1;
|
||||
if (currentPageIndex > pdf.numPages - 1) {
|
||||
newPageIndex = pdf.numPages - 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState(state => ({ ...state, currentPageIndex: newPageIndex }));
|
||||
};
|
||||
|
||||
onZoomChange = () => {
|
||||
let newZoomValue = this.state.scale;
|
||||
|
||||
const action = event.target.getAttribute('data-pager');
|
||||
|
||||
if (action === '+') {
|
||||
newZoomValue += 0.25;
|
||||
}
|
||||
|
||||
if (action === '-') {
|
||||
newZoomValue -= 0.25;
|
||||
}
|
||||
|
||||
this.setState(state => ({ ...state, scale: newZoomValue }));
|
||||
};
|
||||
|
||||
parseByteArray = byteArray => {
|
||||
const options = {
|
||||
untilTag: '',
|
||||
};
|
||||
const options = { untilTag: '' };
|
||||
|
||||
let dataSet;
|
||||
|
||||
try {
|
||||
dataSet = dicomParser.parseDicom(byteArray, options);
|
||||
} catch (error) {
|
||||
this.setState({
|
||||
error,
|
||||
});
|
||||
this.setState(state => ({ ...state, error }));
|
||||
}
|
||||
|
||||
return dataSet;
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const dataSet = this.parseByteArray(this.props.byteArray);
|
||||
setViewportActiveHandler = () => {
|
||||
const {
|
||||
setViewportActive,
|
||||
viewportIndex,
|
||||
activeViewportIndex,
|
||||
} = this.props;
|
||||
|
||||
this.renderPDF(dataSet, this.props.byteArray);
|
||||
}
|
||||
if (viewportIndex !== activeViewportIndex) {
|
||||
setViewportActive(viewportIndex);
|
||||
}
|
||||
};
|
||||
|
||||
downloadPDFCanvas = () => {
|
||||
const { fileURL } = this.state;
|
||||
const a = document.createElement('a');
|
||||
a.href = fileURL;
|
||||
a.download = fileURL.substr(fileURL.lastIndexOf('/') + 1);
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { fileURL, pdf, error } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={'DicomPDFViewport'}
|
||||
onClick={this.setViewportActiveHandler}
|
||||
onScroll={this.setViewportActiveHandler}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
>
|
||||
{this.state.fileURL && (
|
||||
{!this.props.useNative ? (
|
||||
<>
|
||||
<div id="toolbar">
|
||||
<div id="pager">
|
||||
{pdf && pdf.numPages > 1 && (
|
||||
<>
|
||||
<button data-pager="prev" onClick={this.onPageChange}>
|
||||
{`<`}
|
||||
</button>
|
||||
<button data-pager="next" onClick={this.onPageChange}>
|
||||
{`>`}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
<button data-pager="-" onClick={this.onZoomChange}>
|
||||
{`-`}
|
||||
</button>
|
||||
<button data-pager="+" onClick={this.onZoomChange}>
|
||||
{`+`}
|
||||
</button>
|
||||
<button onClick={this.downloadPDFCanvas}>Download</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="canvas">
|
||||
<div id="pdf-canvas-container">
|
||||
<canvas
|
||||
id="pdf-canvas"
|
||||
ref={canvas => (this.canvas = canvas)}
|
||||
/>
|
||||
<div
|
||||
id="text-layer"
|
||||
ref={textLayer => (this.textLayer = textLayer)}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<object
|
||||
data={this.state.fileURL}
|
||||
aria-label="PDF Viewer"
|
||||
data={fileURL}
|
||||
type="application/pdf"
|
||||
width="100%"
|
||||
height="100%"
|
||||
/>
|
||||
)}
|
||||
{this.state.error && <h2>{JSON.stringify(this.state.error)}</h2>}
|
||||
{error && <h2>{JSON.stringify(error)}</h2>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ class OHIFDicomPDFViewport extends Component {
|
||||
studies: PropTypes.object,
|
||||
displaySet: PropTypes.object,
|
||||
viewportIndex: PropTypes.number,
|
||||
viewportData: PropTypes.object,
|
||||
activeViewportIndex: PropTypes.number,
|
||||
setViewportActive: PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -30,42 +33,36 @@ class OHIFDicomPDFViewport extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
const { displaySet, studies } = this.props.viewportData;
|
||||
const {
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
sopInstanceUid,
|
||||
wadoRoot,
|
||||
wadoUri,
|
||||
authorizationHeaders,
|
||||
} = displaySet;
|
||||
|
||||
DicomLoaderService.findDicomDataPromise(displaySet, studies).then(
|
||||
data => {
|
||||
const byteArray = new Uint8Array(data);
|
||||
this.setState({
|
||||
byteArray: byteArray,
|
||||
});
|
||||
},
|
||||
data => this.setState({ byteArray: new Uint8Array(data) }),
|
||||
error => {
|
||||
this.setState({
|
||||
error,
|
||||
});
|
||||
|
||||
this.setState({ error });
|
||||
throw new Error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
setViewportActive,
|
||||
viewportIndex,
|
||||
activeViewportIndex,
|
||||
} = this.props;
|
||||
const { byteArray, error } = this.state;
|
||||
const { id, init, destroy } = OHIFDicomPDFViewport;
|
||||
const pluginProps = { id, init, destroy };
|
||||
|
||||
return (
|
||||
<OHIFComponentPlugin {...pluginProps}>
|
||||
{this.state.byteArray && (
|
||||
<DicomPDFViewport byteArray={this.state.byteArray} />
|
||||
{byteArray && (
|
||||
<DicomPDFViewport
|
||||
byteArray={byteArray}
|
||||
setViewportActive={setViewportActive}
|
||||
viewportIndex={viewportIndex}
|
||||
activeViewportIndex={activeViewportIndex}
|
||||
/>
|
||||
)}
|
||||
{this.state.error && <h2>{JSON.stringify(this.state.error)}</h2>}
|
||||
{error && <h2>{JSON.stringify(error)}</h2>}
|
||||
</OHIFComponentPlugin>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* We use this component to leverage "Code Splitting"
|
||||
*
|
||||
* Link: https://serverless-stack.com/chapters/code-splitting-in-create-react-app.html
|
||||
*/
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
export default function asyncComponent(importComponent) {
|
||||
class AsyncComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
component: null,
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
// Add dynamically loaded component to state
|
||||
const { default: component } = await importComponent();
|
||||
|
||||
this.setState({
|
||||
component: component,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const C = this.state.component;
|
||||
|
||||
// Render the loaded component, or null
|
||||
return C ? <C {...this.props} /> : null;
|
||||
}
|
||||
}
|
||||
|
||||
return AsyncComponent;
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
import asyncComponent from './asyncComponent.js';
|
||||
import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js';
|
||||
import OHIFDicomPDFViewport from './OHIFDicomPDFViewport.js';
|
||||
|
||||
const ConnectedOHIFDicomPDFViewer = asyncComponent(() =>
|
||||
import(
|
||||
/* webpackChunkName: "ConnectedOHIFDicomPDFViewer" */ './ConnectedOHIFDicomPDFViewer'
|
||||
)
|
||||
);
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'pdf',
|
||||
|
||||
getViewportModule() {
|
||||
return OHIFDicomPDFViewport;
|
||||
return ConnectedOHIFDicomPDFViewer;
|
||||
},
|
||||
getSopClassHandlerModule() {
|
||||
return OHIFDicomPDFSopClassHandler;
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.1.4...@ohif/extension-vtk@1.1.5) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.1.3...@ohif/extension-vtk@1.1.4) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -52,7 +52,7 @@
|
||||
"react-vtkjs-viewport": "^0.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^2.1.1",
|
||||
"@ohif/core": "^2.2.0",
|
||||
"@ohif/ui": "^1.1.4",
|
||||
"cornerstone-tools": "^4.8.0",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.2.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.1.1...@ohif/core@2.2.0) (2019-12-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 Expose extension config to modules ([#1279](https://github.com/OHIF/Viewers/issues/1279)) ([4ea239a](https://github.com/OHIF/Viewers/commit/4ea239a9535ef297e23387c186e537ab273744ea)), closes [#1268](https://github.com/OHIF/Viewers/issues/1268)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.1.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.1.0...@ohif/core@2.1.1) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/core",
|
||||
"version": "2.1.1",
|
||||
"version": "2.2.0",
|
||||
"description": "Generic business logic for web-based medical imaging applications",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -2,13 +2,14 @@ import MODULE_TYPES from './MODULE_TYPES.js';
|
||||
import log from './../log.js';
|
||||
|
||||
export default class ExtensionManager {
|
||||
constructor({ commandsManager, servicesManager }) {
|
||||
constructor({ commandsManager, servicesManager, appConfig = {} }) {
|
||||
this.modules = {};
|
||||
this.registeredExtensionIds = [];
|
||||
this.moduleTypeNames = Object.values(MODULE_TYPES);
|
||||
//
|
||||
this._commandsManager = commandsManager;
|
||||
this._servicesManager = servicesManager;
|
||||
this._appConfig = appConfig;
|
||||
|
||||
this.moduleTypeNames.forEach(moduleType => {
|
||||
this.modules[moduleType] = [];
|
||||
@@ -70,6 +71,7 @@ export default class ExtensionManager {
|
||||
extension.preRegistration({
|
||||
servicesManager: this._servicesManager,
|
||||
commandsManager: this._commandsManager,
|
||||
appConfig: this._appConfig,
|
||||
configuration,
|
||||
});
|
||||
}
|
||||
@@ -79,7 +81,8 @@ export default class ExtensionManager {
|
||||
const extensionModule = this._getExtensionModule(
|
||||
moduleType,
|
||||
extension,
|
||||
extensionId
|
||||
extensionId,
|
||||
configuration
|
||||
);
|
||||
|
||||
if (extensionModule) {
|
||||
@@ -102,7 +105,7 @@ export default class ExtensionManager {
|
||||
* @param {Object} extension
|
||||
* @param {string} extensionId - Used for logging warnings
|
||||
*/
|
||||
_getExtensionModule(moduleType, extension, extensionId) {
|
||||
_getExtensionModule(moduleType, extension, extensionId, configuration) {
|
||||
const getModuleFnName = 'get' + _capitalizeFirstCharacter(moduleType);
|
||||
const getModuleFn = extension[getModuleFnName];
|
||||
|
||||
@@ -114,6 +117,8 @@ export default class ExtensionManager {
|
||||
const extensionModule = getModuleFn({
|
||||
servicesManager: this._servicesManager,
|
||||
commandsManager: this._commandsManager,
|
||||
appConfig: this._appConfig,
|
||||
configuration,
|
||||
});
|
||||
|
||||
if (!extensionModule) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import log from './../log.js';
|
||||
jest.mock('./../log.js');
|
||||
|
||||
describe('ExtensionManager.js', () => {
|
||||
let extensionManager, commandsManager;
|
||||
let extensionManager, commandsManager, servicesManager, appConfig;
|
||||
|
||||
beforeEach(() => {
|
||||
commandsManager = {
|
||||
@@ -14,7 +14,17 @@ describe('ExtensionManager.js', () => {
|
||||
getContext: jest.fn(),
|
||||
registerCommand: jest.fn(),
|
||||
};
|
||||
extensionManager = new ExtensionManager({ commandsManager });
|
||||
servicesManager = {
|
||||
registerService: jest.fn(),
|
||||
};
|
||||
appConfig = {
|
||||
testing: true,
|
||||
};
|
||||
extensionManager = new ExtensionManager({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
appConfig,
|
||||
});
|
||||
log.warn.mockClear();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
@@ -51,7 +61,7 @@ describe('ExtensionManager.js', () => {
|
||||
extensionManager.registerExtensions(fakeExtensions);
|
||||
|
||||
// Assert
|
||||
expect(extensionManager.registerExtension.mock.calls[1]).toContain(
|
||||
expect(extensionManager.registerExtension.mock.calls[1][1]).toEqual(
|
||||
fakeConfiguration
|
||||
);
|
||||
});
|
||||
@@ -67,19 +77,19 @@ describe('ExtensionManager.js', () => {
|
||||
expect(fakeExtension.preRegistration.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('calls preRegistration() passing configuration along with servicesManager and commandsManager instances for extension', () => {
|
||||
const configuration = { config: 'Some configuration' };
|
||||
extensionManager._servicesManager = { services: { TestService: {} } };
|
||||
it('calls preRegistration() passing dependencies and extension configuration to extension', () => {
|
||||
const extensionConfiguration = { config: 'Some configuration' };
|
||||
|
||||
// SUT
|
||||
const fakeExtension = { one: '1', preRegistration: jest.fn() };
|
||||
extensionManager.registerExtension(fakeExtension, configuration);
|
||||
const extension = { one: '1', preRegistration: jest.fn() };
|
||||
extensionManager.registerExtension(extension, extensionConfiguration);
|
||||
|
||||
// Assert
|
||||
expect(fakeExtension.preRegistration.mock.calls[0][0]).toEqual({
|
||||
servicesManager: extensionManager._servicesManager,
|
||||
commandsManager: extensionManager._commandsManager,
|
||||
configuration,
|
||||
expect(extension.preRegistration.mock.calls[0][0]).toEqual({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
appConfig,
|
||||
configuration: extensionConfiguration,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -153,8 +163,8 @@ describe('ExtensionManager.js', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('successfully passes a servicesManager and commandsManager instances to each module', () => {
|
||||
extensionManager._servicesManager = { services: { TestService: {} } };
|
||||
it('successfully passes dependencies to each module along with extension configuration', () => {
|
||||
const extensionConfiguration = { testing: true };
|
||||
|
||||
const extension = {
|
||||
id: 'hello-world',
|
||||
@@ -165,11 +175,17 @@ describe('ExtensionManager.js', () => {
|
||||
getCommandsModule: jest.fn(),
|
||||
};
|
||||
|
||||
extensionManager.registerExtension(extension);
|
||||
extensionManager.registerExtension(extension, extensionConfiguration);
|
||||
|
||||
expect(extension.getViewportModule.mock.calls[0][0]).toEqual({
|
||||
servicesManager: extensionManager._servicesManager,
|
||||
commandsManager: extensionManager._commandsManager,
|
||||
Object.keys(extension).forEach(module => {
|
||||
if (typeof extension[module] === 'function') {
|
||||
expect(extension[module].mock.calls[0][0]).toEqual({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
appConfig,
|
||||
configuration: extensionConfiguration,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -33,7 +33,9 @@ export default class ServicesManager {
|
||||
}
|
||||
|
||||
if (service.create) {
|
||||
this.services[service.name] = service.create({ configuration });
|
||||
this.services[service.name] = service.create({
|
||||
configuration,
|
||||
});
|
||||
} else {
|
||||
log.warn(`Service create factory function not defined. Exiting early.`);
|
||||
return;
|
||||
|
||||
@@ -33,7 +33,7 @@ describe('ServicesManager.js', () => {
|
||||
[{ name: 'UIModalTestService', create: jest.fn() }, fakeConfiguration],
|
||||
]);
|
||||
|
||||
expect(servicesManager.registerService.mock.calls[1]).toContain(
|
||||
expect(servicesManager.registerService.mock.calls[1][1]).toEqual(
|
||||
fakeConfiguration
|
||||
);
|
||||
});
|
||||
@@ -85,7 +85,7 @@ describe('ServicesManager.js', () => {
|
||||
expect(log.warn.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('pass configuration to service create factory function', () => {
|
||||
it('pass dependencies and configuration to service create factory function', () => {
|
||||
const configuration = { config: 'Some configuration' };
|
||||
|
||||
servicesManager.registerService(fakeService, configuration);
|
||||
|
||||
@@ -3,6 +3,44 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.2.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.1.12...@ohif/viewer@3.2.0) (2019-12-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 Expose extension config to modules ([#1279](https://github.com/OHIF/Viewers/issues/1279)) ([4ea239a](https://github.com/OHIF/Viewers/commit/4ea239a9535ef297e23387c186e537ab273744ea)), closes [#1268](https://github.com/OHIF/Viewers/issues/1268)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.12](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.1.11...@ohif/viewer@3.1.12) (2019-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Dismiss all dialogs if leaving viewer route [#1242](https://github.com/OHIF/Viewers/issues/1242) ([#1301](https://github.com/OHIF/Viewers/issues/1301)) ([5c3d8b3](https://github.com/OHIF/Viewers/commit/5c3d8b37b6f723fbd8edcc447c37984e7eee8d40))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.1.10...@ohif/viewer@3.1.11) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.10](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.1.9...@ohif/viewer@3.1.10) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.1.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.1.8...@ohif/viewer@3.1.9) (2019-12-16)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/viewer",
|
||||
"version": "3.1.9",
|
||||
"version": "3.2.0",
|
||||
"description": "OHIF Viewer",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
@@ -45,12 +45,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@ohif/core": "^2.1.1",
|
||||
"@ohif/extension-cornerstone": "^2.1.0",
|
||||
"@ohif/core": "^2.2.0",
|
||||
"@ohif/extension-cornerstone": "^2.1.1",
|
||||
"@ohif/extension-dicom-html": "^1.1.0",
|
||||
"@ohif/extension-dicom-microscopy": "^0.50.6",
|
||||
"@ohif/extension-dicom-pdf": "^1.0.0",
|
||||
"@ohif/extension-vtk": "^1.1.4",
|
||||
"@ohif/extension-dicom-pdf": "^1.0.1",
|
||||
"@ohif/extension-vtk": "^1.1.5",
|
||||
"@ohif/i18n": "^0.52.4",
|
||||
"@ohif/ui": "^1.1.4",
|
||||
"@tanem/react-nprogress": "^1.1.25",
|
||||
|
||||
@@ -64,10 +64,7 @@ const commandsManagerConfig = {
|
||||
const commandsManager = new CommandsManager(commandsManagerConfig);
|
||||
const hotkeysManager = new HotkeysManager(commandsManager);
|
||||
const servicesManager = new ServicesManager();
|
||||
const extensionManager = new ExtensionManager({
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
});
|
||||
let extensionManager;
|
||||
/** ~~~~~~~~~~~~~ End Application Setup */
|
||||
|
||||
// TODO[react] Use a provider when the whole tree is React
|
||||
@@ -128,7 +125,8 @@ class App extends Component {
|
||||
_initServices([UINotificationService, UIModalService, UIDialogService]);
|
||||
_initExtensions(
|
||||
[...defaultExtensions, ...extensions],
|
||||
cornerstoneExtensionConfig
|
||||
cornerstoneExtensionConfig,
|
||||
this._appConfig
|
||||
);
|
||||
|
||||
/*
|
||||
@@ -241,7 +239,13 @@ function _initServices(services) {
|
||||
/**
|
||||
* @param
|
||||
*/
|
||||
function _initExtensions(extensions, cornerstoneExtensionConfig) {
|
||||
function _initExtensions(extensions, cornerstoneExtensionConfig, appConfig) {
|
||||
extensionManager = new ExtensionManager({
|
||||
commandsManager,
|
||||
servicesManager,
|
||||
appConfig,
|
||||
});
|
||||
|
||||
const requiredExtensions = [
|
||||
GenericViewerCommands,
|
||||
[OHIFCornerstoneExtension, cornerstoneExtensionConfig],
|
||||
|
||||
@@ -4,6 +4,7 @@ import classNames from 'classnames';
|
||||
|
||||
import { MODULE_TYPES } from '@ohif/core';
|
||||
import OHIF from '@ohif/core';
|
||||
import { withDialog } from '@ohif/ui';
|
||||
import moment from 'moment';
|
||||
import ConnectedHeader from './ConnectedHeader.js';
|
||||
import ConnectedToolbarRow from './ConnectedToolbarRow.js';
|
||||
@@ -69,6 +70,7 @@ class Viewer extends Component {
|
||||
// window.store.getState().viewports.activeViewportIndex
|
||||
activeViewportIndex: PropTypes.number.isRequired,
|
||||
isStudyLoaded: PropTypes.bool,
|
||||
dialog: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@@ -104,6 +106,12 @@ class Viewer extends Component {
|
||||
thumbnails: [],
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.props.dialog) {
|
||||
this.props.dialog.dismissAll();
|
||||
}
|
||||
}
|
||||
|
||||
retrieveTimepoints = filter => {
|
||||
OHIF.log.info('retrieveTimepoints');
|
||||
|
||||
@@ -297,11 +305,11 @@ class Viewer extends Component {
|
||||
activeIndex={this.props.activeViewportIndex}
|
||||
/>
|
||||
) : (
|
||||
<ConnectedStudyBrowser
|
||||
studies={this.state.thumbnails}
|
||||
studyMetadata={this.props.studies}
|
||||
/>
|
||||
)}
|
||||
<ConnectedStudyBrowser
|
||||
studies={this.state.thumbnails}
|
||||
studyMetadata={this.props.studies}
|
||||
/>
|
||||
)}
|
||||
</SidePanel>
|
||||
|
||||
{/* MAIN */}
|
||||
@@ -324,7 +332,7 @@ class Viewer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default Viewer;
|
||||
export default withDialog(Viewer);
|
||||
|
||||
/**
|
||||
* What types are these? Why do we have "mapping" dropped in here instead of in
|
||||
@@ -337,7 +345,7 @@ export default Viewer;
|
||||
* @param {Study[]} studies
|
||||
* @param {DisplaySet[]} studies[].displaySets
|
||||
*/
|
||||
const _mapStudiesToThumbnails = function (studies) {
|
||||
const _mapStudiesToThumbnails = function(studies) {
|
||||
return studies.map(study => {
|
||||
const { studyInstanceUid } = study;
|
||||
|
||||
|
||||
@@ -13296,6 +13296,11 @@ node-dir@^0.1.10:
|
||||
dependencies:
|
||||
minimatch "^3.0.2"
|
||||
|
||||
node-ensure@^0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/node-ensure/-/node-ensure-0.0.0.tgz#ecae764150de99861ec5c810fd5d096b183932a7"
|
||||
integrity sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc=
|
||||
|
||||
node-fetch-npm@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7"
|
||||
@@ -14423,6 +14428,14 @@ pbkdf2@^3.0.3:
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
pdfjs-dist@^2.2.228:
|
||||
version "2.2.228"
|
||||
resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-2.2.228.tgz#777b068a0a16c96418433303807c183058b47aaa"
|
||||
integrity sha512-W5LhYPMS2UKX0ELIa4u+CFCMoox5qQNQElt0bAK2mwz1V8jZL0rvLao+0tBujce84PK6PvWG36Nwr7agCCWFGQ==
|
||||
dependencies:
|
||||
node-ensure "^0.0.0"
|
||||
worker-loader "^2.0.0"
|
||||
|
||||
pend@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
|
||||
Reference in new issue
Block a user