Compare commits

..
Author SHA1 Message Date
ohif-bot 561911056a chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.7.0
 - @ohif/extension-dicom-html@1.2.0
 - @ohif/extension-dicom-rt@0.1.0
 - @ohif/extension-dicom-segmentation@0.2.0
 - @ohif/extension-vtk@1.5.0
 - @ohif/core@2.9.0
 - @ohif/viewer@3.11.0
2020-04-24 10:29:19 +00:00
Igor OctavianoandJames Petts 835f64d47a feat: 🎸 Seg jump to slice + show/hide
* Add segment part 2 (jump to frame and visibility toggle)

* Cr updates

* Filter displaysets with images

* feat: 🎸 Seg jump to slice + show/hide

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-04-24 12:25:05 +02:00
ohif-bot fc40d84092 chore(release): publish [skip ci]
- @ohif/viewer@3.10.2
2020-04-23 16:20:51 +00:00
Danny Brown 709f14708e fix: undefined errorHandler in cornerstoneWadoImageLoader configuration (#1664) 2020-04-23 12:17:49 -04:00
ohif-bot 5f4c4ab5e7 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.6.1
 - @ohif/extension-vtk@1.4.1
 - @ohif/core@2.8.1
 - @ohif/viewer@3.10.1
2020-04-23 10:04:47 +00:00
James Petts 71205618ec fix: 🐛 Multiframe fix (#1661) 2020-04-23 12:02:51 +02:00
ohif-bot 5014ba864b chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.6.0
 - @ohif/extension-dicom-p10-downloader@0.1.0
 - @ohif/extension-vtk@1.4.0
 - @ohif/core@2.8.0
 - @ohif/viewer@3.10.0
2020-04-23 02:06:44 +00:00
James Pettsanddannyrb e96205de35 feat: configuration to hook into XHR Error handling
* Error handling.

* Fix error handling, add redirect example.

* remove debugger

* docs: whitelabeling and httpErrorHandler

* bump dicomweb-client version

* bump cornerstone-wado-image-loader version

* Add more documentation around httpErrorHandler callback

* test: add errorHandler as an expected top level export

Co-authored-by: dannyrb <danny.ri.brown@gmail.com>
2020-04-22 22:01:34 -04:00
ohif-bot 273f31ad56 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.3.14
 - @ohif/core@2.7.1
 - @ohif/viewer@3.9.2
2020-04-22 20:19:12 +00:00
Danny Brown 2093a00365 fix: whiteLabeling should support component creation by passing React to defined fn (#1659) 2020-04-22 16:16:53 -04:00
dependabot[bot] b1dad6b61b chore(deps): bump https-proxy-agent from 2.2.1 to 2.2.4 in /docs/latest (#1641)
Bumps [https-proxy-agent](https://github.com/TooTallNate/node-https-proxy-agent) from 2.2.1 to 2.2.4.
- [Release notes](https://github.com/TooTallNate/node-https-proxy-agent/releases)
- [Commits](https://github.com/TooTallNate/node-https-proxy-agent/compare/2.2.1...2.2.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-04-18 22:05:44 -04:00
ohif-bot 3a587f4e4b chore(release): publish [skip ci]
- @ohif/viewer@3.9.1
2020-04-17 16:28:57 +00:00
Danny Brown d9fc7bbb0e fix: showStudyList config (#1647)
* fix: `showStudyList` config

* showStudyList should default to true if not defined
2020-04-17 12:26:09 -04:00
65 changed files with 980 additions and 448 deletions

No files matched your search

+53 -1
View File
@@ -11,7 +11,59 @@ are:
```js
window.config = {
routerBasename: '/',
whiteLabelling: {},
/**
* "White Labeling" is used to change the branding, look, and feel of the OHIF
* Viewer. These settings, and the color variables that are used by our components,
* are the easiest way to rebrand the application.
*
* More extensive changes are made possible through swapping out the UI library,
* Viewer project, or extensions.
*/
whiteLabeling: {
/* Optional: Should return a React component to be rendered in the "Logo" section of the application's Top Navigation bar */
createLogoComponentFn: function(React) {
return React.createElement('a', {
target: '_self',
rel: 'noopener noreferrer',
className: 'header-brand',
href: '/',
style: {
display: 'block',
textIndent: '-9999px',
background: 'url(/svg-file-hosted-at-domain-root.svg)',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
width: '200px',
},
});
},
},
/**
* Internally, the OHIF Viewer fetches data primarily with the
* `cornerstoneWADOImageLoader` and the `DICOMWebClient`. If either of these
* receive a non-200 response, this method allows you to handle that error.
*
* Common use cases include:
* - Showing a notification with the UINotificationService
* - Redirecting the user
* - Refreshing an auth token
*
* @param {Object} error - JS new Error()
* @param {XMLHttpRequest} error.request - The XHR request that's onreadystate change triggered this callback
* @param {string} error.response - The XHR's response property
* @param {number} error.status - The XHR's status property
*/
httpErrorHandler: error => {
const { request: xhr, response, status } = err;
const { responseType, statusText } = xhr;
// In local files, status is 0 upon success in Firefox
if (xhr.readyState === XMLHttpRequest.DONE) {
console.log(statusText, response, responseType);
} else {
console.warn('Likely CORS error');
}
},
extensions: [],
showStudyList: true,
filterQueryParam: false,
+4 -4
View File
@@ -66,11 +66,11 @@ open to pull requests and discussion issues.
Current white-labeling options are limited. We expose the ability to replace the
"Logo" section of the application with a custom "Logo" component. You can do
this by adding a `whiteLabelling` key to your
this by adding a `whiteLabeling` key to your
[configuration file](./configuration.md).
```js
function RadicalImagingLogo() {
function RadicalImagingLogo(React) {
return React.createElement(
'a',
{
@@ -83,8 +83,8 @@ function RadicalImagingLogo() {
);
}
props.whiteLabelling = {
logoComponent: RadicalImagingLogo(),
props.whiteLabeling = {
createLogoComponentFn: RadicalImagingLogo,
};
```
+17 -12
View File
@@ -20,10 +20,10 @@ abbrev@~1.0.9:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU=
agent-base@4, agent-base@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
agent-base@4, agent-base@^4.1.0, agent-base@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
dependencies:
es6-promisify "^5.0.0"
@@ -773,9 +773,9 @@ err-code@^1.0.0:
prr "~1.0.1"
es6-promise@^4.0.3:
version "4.2.6"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f"
integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
es6-promisify@^5.0.0:
version "5.0.0"
@@ -1289,11 +1289,11 @@ http-signature@~1.2.0:
sshpk "^1.7.0"
https-proxy-agent@^2.1.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0"
integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==
version "2.2.4"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b"
integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==
dependencies:
agent-base "^4.1.0"
agent-base "^4.3.0"
debug "^3.1.0"
humanize-ms@^1.2.1:
@@ -1807,11 +1807,16 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@^2.0.0, ms@^2.1.1:
ms@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
mute-stream@~0.0.4:
version "0.0.8"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
+33
View File
@@ -3,6 +3,39 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.7.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.6.1...@ohif/extension-cornerstone@2.7.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [2.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.6.0...@ohif/extension-cornerstone@2.6.1) (2020-04-23)
### Bug Fixes
* 🐛 Multiframe fix ([#1661](https://github.com/OHIF/Viewers/issues/1661)) ([7120561](https://github.com/OHIF/Viewers/commit/71205618ecb8b592247c5acb32284bfe7e18fce5))
# [2.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.5.2...@ohif/extension-cornerstone@2.6.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## [2.5.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.5.1...@ohif/extension-cornerstone@2.5.2) (2020-04-09)
**Note:** Version bump only for package @ohif/extension-cornerstone
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "2.5.2",
"version": "2.7.0",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -35,8 +35,8 @@
"cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"dcmjs": "^0.10.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2",
"dicom-parser": "^1.8.3",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
+1 -1
View File
@@ -267,7 +267,7 @@ const commandsModule = ({ servicesManager }) => {
const study = studyMetadataManager.get(StudyInstanceUID);
const displaySet = study.findDisplaySet(ds => {
return ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
return ds.images && ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
});
displaySet.SOPInstanceUID = SOPInstanceUID;
@@ -2,6 +2,9 @@ import Hammer from 'hammerjs';
import cornerstone from 'cornerstone-core';
import cornerstoneMath from 'cornerstone-math';
import cornerstoneTools from 'cornerstone-tools';
import OHIF from '@ohif/core';
const { log } = OHIF;
export default function(configuration = {}) {
// For debugging
@@ -12,6 +15,13 @@ export default function(configuration = {}) {
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
cornerstoneTools.init(configuration);
cornerstoneTools.loadHandlerManager.setErrorLoadingHandler(
(element, imageId, error) => {
log.error(imageId);
throw error;
}
);
// Set the tool font and font size
// context.font = "[style] [variant] [weight] [size]/[line height] [font family]";
const fontFamily =
+11
View File
@@ -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.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.1.2...@ohif/extension-dicom-html@1.2.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [1.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.1.1...@ohif/extension-dicom-html@1.1.2) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-dicom-html
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-html",
"version": "1.1.2",
"version": "1.2.0",
"description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF",
"license": "MIT",
@@ -29,7 +29,7 @@
},
"peerDependencies": {
"@ohif/core": "^0.50.0",
"dcmjs": "^0.10.1",
"dcmjs": "^0.12.2",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
@@ -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.
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.0.2...@ohif/extension-dicom-p10-downloader@0.1.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## 0.0.2 (2020-04-15)
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-p10-downloader",
"version": "0.0.2",
"version": "0.1.0",
"description": "OHIF extension for downloading DICOM P10 files",
"author": "OHIF",
"license": "MIT",
@@ -33,6 +33,7 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"dicomweb-client": "^0.6.0",
"file-saver": "^2.0.2",
"jszip": "^3.2.2"
}
+11
View File
@@ -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.
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.0.2...@ohif/extension-dicom-rt@0.1.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## 0.0.2 (2020-04-09)
**Note:** Version bump only for package @ohif/extension-dicom-rt
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-rt",
"version": "0.0.2",
"version": "0.1.0",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF",
"license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.0.9",
"dcmjs": "^0.8.3",
"dcmjs": "^0.12.2",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
@@ -61,14 +61,17 @@
}
.dcmrt-structure-set-item .item-label {
overflow: hidden;
max-width: calc(100% - 15px); /* calc(100% - 50px); 20px = eye icon */
text-overflow: ellipsis;
overflow-wrap: normal;
white-space: nowrap;
display: flex;
justify-content: space-between;
padding-right: 10px;
}
.dcmrt-structure-set-item .item-label span {
overflow-wrap: normal;
white-space: nowrap;
overflow: hidden;
max-width: calc(100% - 40px); /* calc(100% - 50px); 20px = eye icon */
text-overflow: ellipsis;
}
.dcmrt-structure-set-item .item-label .eye-icon {
@@ -41,7 +41,7 @@ const StructureSetItem = ({
>
<div>
<div className="item-label" style={{ marginBottom: 4 }}>
{label}
<span>{label}</span>
<Icon
className={`eye-icon ${isVisible && '--visible'}`}
name={isVisible ? 'eye' : 'eye-closed'}
+5 -6
View File
@@ -27,10 +27,7 @@ export default {
};
return (
<RTPanel
{...props}
onContourItemClick={contourItemClickHandler}
/>
<RTPanel {...props} onContourItemClick={contourItemClickHandler} />
);
};
@@ -51,8 +48,10 @@ export default {
if (study && study.series) {
for (let j = 0; j < study.series.length; j++) {
const series = study.series[j];
console.log(studies);
if (['RTSTRUCT', 'RTPLAN', 'RTDOSE'].includes(series.Modality)) {
if (
/* Could be expanded to contain RTPLAN and RTDOSE information in the future */
['RTSTRUCT'].includes(series.Modality)
) {
return false;
}
}
@@ -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.
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.1.2...@ohif/extension-dicom-segmentation@0.2.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [0.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.1.1...@ohif/extension-dicom-segmentation@0.1.2) (2020-04-09)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-segmentation",
"version": "0.1.2",
"version": "0.2.0",
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF",
"license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.12.5",
"dcmjs": "^0.6.1",
"dcmjs": "^0.12.2",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
@@ -12,14 +12,6 @@
height: 20px;
}
.dcmseg-segment-item .segment-label {
overflow: hidden;
max-width: calc(100% - 50px);
text-overflow: ellipsis;
overflow-wrap: normal;
white-space: nowrap;
}
.dcmseg-segment-item .segment-info {
display: inline-block;
margin-top: 9px;
@@ -62,3 +54,30 @@
.dcmseg-segment-item .segment-actions .btnAction i {
margin-right: 4px;
}
.dcmseg-segment-item .segment-label {
display: flex;
justify-content: space-between;
padding-right: 20px;
}
.dcmseg-segment-item .segment-label span {
overflow-wrap: normal;
white-space: nowrap;
overflow: hidden;
max-width: calc(100% - 40px); /* calc(100% - 50px); 20px = eye icon */
text-overflow: ellipsis;
}
.dcmseg-segment-item .segment-label .eye-icon {
cursor: pointer;
color: var(--active-color);
}
.dcmseg-segment-item .segment-label .eye-icon:hover {
color: var(--hover-color);
}
.dcmseg-segment-item .segment-label .eye-icon.--visible {
color: var(--default-color);
}
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { TableListItem, Icon } from '@ohif/ui';
@@ -17,48 +17,63 @@ ColoredCircle.propTypes = {
color: PropTypes.array.isRequired,
};
const SegmentItem = ({ index, label, onClick, itemClass, color }) => (
<div className="dcmseg-segment-item">
<TableListItem
key={index}
itemKey={index}
itemIndex={index}
itemClass={itemClass}
itemMeta={<ColoredCircle color={color} />}
itemMetaClass="segment-color-section"
onItemClick={onClick}
>
<div>
<div className="segment-label" style={{ marginBottom: 4 }}>
{label}
</div>
{false && <div className="segment-info">{'...'}</div>}
{false && (
<div className="segment-actions">
<button
className="btnAction"
onClick={() => console.log('Relabelling...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
const SegmentItem = ({ index, label, onClick, itemClass, color, visible = true, onVisibilityChange }) => {
const [isVisible, setIsVisible] = useState(visible);
return (
<div className="dcmseg-segment-item">
<TableListItem
key={index}
itemKey={index}
itemIndex={index}
itemClass={itemClass}
itemMeta={<ColoredCircle color={color} />}
itemMetaClass="segment-color-section"
onItemClick={onClick}
>
<div>
<div className="segment-label" style={{ marginBottom: 4 }}>
<span>{label}</span>
<Icon
className={`eye-icon ${isVisible && '--visible'}`}
name={isVisible ? 'eye' : 'eye-closed'}
width="20px"
height="20px"
onClick={event => {
event.stopPropagation();
const newVisibility = !isVisible;
setIsVisible(newVisibility);
onVisibilityChange(newVisibility);
}}
/>
</div>
{false && <div className="segment-info">{'...'}</div>}
{false && (
<div className="segment-actions">
<button
className="btnAction"
onClick={() => console.log('Relabelling...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Relabel
</button>
<button
className="btnAction"
onClick={() => console.log('Editing description...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
<button
className="btnAction"
onClick={() => console.log('Editing description...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Description
</button>
</div>
)}
</div>
</TableListItem>
</div>
);
</div>
)}
</div>
</TableListItem>
</div>
);
};
SegmentItem.propTypes = {
index: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import cornerstoneTools from 'cornerstone-tools';
import cornerstone from 'cornerstone-core';
@@ -28,13 +28,20 @@ const refreshViewport = () => {
/**
* SegmentationPanel component
*
* @param {Object} props
* @param {Array} props.studies
* @param {Array} props.viewports - viewportSpecificData
* @param {number} props.activeIndex - activeViewportIndex
* @param {Array} props.studies - Studies data
* @param {Array} props.viewports - Viewports data (viewportSpecificData)
* @param {number} props.activeIndex - Active viewport index
* @param {boolean} props.isOpen - Boolean that indicates if the panel is expanded
* @param {Function} props.onSegItemClick - Segment click handler
* @returns component
*/
const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
const SegmentationPanel = ({
studies,
viewports,
activeIndex,
isOpen,
onSegItemClick,
}) => {
/*
* TODO: wrap get/set interactions with the cornerstoneTools
* store with context to make these kind of things less blurry.
@@ -43,13 +50,14 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
const DEFAULT_BRUSH_RADIUS = configuration.radius || 10;
const [state, setState] = useState({
brushRadius: DEFAULT_BRUSH_RADIUS,
brushColor: 'rgba(221, 85, 85, 1)', /* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */
brushColor:
'rgba(221, 85, 85, 1)' /* TODO: We shouldn't hardcode this color, in the future the SEG may set the colorLUT to whatever it wants. */,
selectedSegment: null,
selectedSegmentation: null,
showSegSettings: false,
brushStackState: null,
labelmapList: [],
segmentList: []
segmentList: [],
});
useEffect(() => {
@@ -57,7 +65,12 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
log.warn('Segmentation Panel: labelmap modified', event);
const module = cornerstoneTools.getModule('segmentation');
const activeViewport = viewports[activeIndex];
const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID);
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const firstImageId = studyMetadata.getFirstImageId(
activeViewport.displaySetInstanceUID
);
updateState('brushStackState', module.state.series[firstImageId]);
};
@@ -86,20 +99,29 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
useEffect(() => {
const module = cornerstoneTools.getModule('segmentation');
const activeViewport = viewports[activeIndex];
const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID);
const firstImageId = studyMetadata.getFirstImageId(activeViewport.displaySetInstanceUID);
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const firstImageId = studyMetadata.getFirstImageId(
activeViewport.displaySetInstanceUID
);
const brushStackState = module.state.series[firstImageId];
if (brushStackState) {
const labelmap3D = brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
const labelmapList = getLabelmapList(brushStackState, firstImageId, activeViewport);
const segmentList = getSegmentList(labelmap3D, firstImageId);
const labelmap3D =
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
const labelmapList = getLabelmapList(
brushStackState,
firstImageId,
activeViewport
);
const segmentList = getSegmentList(labelmap3D, firstImageId, brushStackState);
setState(state => ({
...state,
brushStackState,
selectedSegmentation: brushStackState.activeLabelmapIndex,
labelmapList,
segmentList
segmentList,
}));
} else {
setState(state => ({
@@ -108,126 +130,201 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
segmentList: [],
}));
}
}, [studies, viewports, activeIndex]);
}, [studies, viewports, activeIndex, getLabelmapList, getSegmentList]);
/* Handle open/closed panel behaviour */
useEffect(() => {
updateState('showSegSettings', state.showSegSettings && !isOpen);
setState(state => ({
...state,
showSegSettings: state.showSegSettings && !isOpen,
}));
}, [isOpen]);
const getLabelmapList = (brushStackState, firstImageId, activeViewport) => {
/* Get list of SEG labelmaps specific to active viewport (reference series) */
const referencedSegDisplaysets = _getReferencedSegDisplaysets(
activeViewport.StudyInstanceUID,
activeViewport.SeriesInstanceUID
);
const getLabelmapList = useCallback(
(brushStackState, firstImageId, activeViewport) => {
/* Get list of SEG labelmaps specific to active viewport (reference series) */
const referencedSegDisplaysets = _getReferencedSegDisplaysets(
activeViewport.StudyInstanceUID,
activeViewport.SeriesInstanceUID
);
return referencedSegDisplaysets.map((displaySet, index) => {
const { labelmapIndex, SeriesDate, SeriesTime } = displaySet;
return referencedSegDisplaysets.map((displaySet, index) => {
const { labelmapIndex, SeriesDate, SeriesTime } = displaySet;
/* Map to display representation */
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
const isActiveLabelmap =
labelmapIndex === brushStackState.activeLabelmapIndex;
const displayDate = date.format('ddd, MMM Do YYYY');
const displayTime = date.format('h:mm:ss a');
const displayDescription = displaySet.SeriesDescription;
/* Map to display representation */
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
const isActiveLabelmap =
labelmapIndex === brushStackState.activeLabelmapIndex;
const displayDate = date.format('ddd, MMM Do YYYY');
const displayTime = date.format('h:mm:ss a');
const displayDescription = displaySet.SeriesDescription;
return {
value: labelmapIndex,
title: displayDescription,
description: displayDate,
onClick: async () => {
const activatedLabelmapIndex = await _setActiveLabelmap(
activeViewport,
studies,
displaySet,
firstImageId,
brushStackState.activeLabelmapIndex
);
updateState('selectedSegmentation', activatedLabelmapIndex);
},
};
});
};
return {
value: labelmapIndex,
title: displayDescription,
description: displayDate,
onClick: async () => {
const activatedLabelmapIndex = await _setActiveLabelmap(
activeViewport,
studies,
displaySet,
firstImageId,
brushStackState.activeLabelmapIndex
);
updateState('selectedSegmentation', activatedLabelmapIndex);
},
};
});
},
[studies]
);
const getSegmentList = (labelmap3D, firstImageId) => {
/*
* Newly created segments have no `meta`
* So we instead build a list of all segment indexes in use
* Then find any associated metadata
*/
const uniqueSegmentIndexes = labelmap3D.labelmaps2D
.reduce((acc, labelmap2D) => {
if (labelmap2D) {
const segmentIndexes = labelmap2D.segmentsOnLabelmap;
const getSegmentList = useCallback(
(labelmap3D, firstImageId, brushStackState) => {
/*
* Newly created segments have no `meta`
* So we instead build a list of all segment indexes in use
* Then find any associated metadata
*/
const uniqueSegmentIndexes = labelmap3D.labelmaps2D
.reduce((acc, labelmap2D) => {
if (labelmap2D) {
const segmentIndexes = labelmap2D.segmentsOnLabelmap;
for (let i = 0; i < segmentIndexes.length; i++) {
if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) {
acc.push(segmentIndexes[i]);
for (let i = 0; i < segmentIndexes.length; i++) {
if (!acc.includes(segmentIndexes[i]) && segmentIndexes[i] !== 0) {
acc.push(segmentIndexes[i]);
}
}
}
return acc;
}, [])
.sort((a, b) => a - b);
const module = cornerstoneTools.getModule('segmentation');
const colorLutTable =
module.state.colorLutTables[labelmap3D.colorLUTIndex];
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
const segmentList = [];
for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
const segmentIndex = uniqueSegmentIndexes[i];
const color = colorLutTable[segmentIndex];
let segmentLabel = '(unlabeled)';
let segmentNumber = segmentIndex;
/* Meta */
if (hasLabelmapMeta) {
const segmentMeta = labelmap3D.metadata.data[segmentIndex];
if (segmentMeta) {
segmentNumber = segmentMeta.SegmentNumber;
segmentLabel = segmentMeta.SegmentLabel;
}
}
return acc;
}, [])
.sort((a, b) => a - b);
const sameSegment = state.selectedSegment === segmentNumber;
const setCurrentSelectedSegment = () => {
_setActiveSegment(
firstImageId,
segmentNumber,
labelmap3D.activeSegmentIndex
);
updateState('selectedSegment', sameSegment ? null : segmentNumber);
const module = cornerstoneTools.getModule('segmentation');
const colorLutTable =
module.state.colorLutTables[labelmap3D.colorLUTIndex];
const hasLabelmapMeta = labelmap3D.metadata && labelmap3D.metadata.data;
const validIndexList = [];
labelmap3D.labelmaps2D.forEach((labelMap2D, index) => {
if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) {
validIndexList.push(index);
}
});
const avg = array => array.reduce((a, b) => a + b) / array.length;
const average = avg(validIndexList);
const closest = validIndexList.reduce((prev, curr) => {
return Math.abs(curr - average) < Math.abs(prev - average)
? curr
: prev;
});
const segmentList = [];
for (let i = 0; i < uniqueSegmentIndexes.length; i++) {
const segmentIndex = uniqueSegmentIndexes[i];
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
const color = colorLutTable[segmentIndex];
let segmentLabel = '(unlabeled)';
let segmentNumber = segmentIndex;
if (!toolState) {
return;
}
/* Meta */
if (hasLabelmapMeta) {
const segmentMeta = labelmap3D.metadata.data[segmentIndex];
const imageIds = toolState.data[0].imageIds;
const imageId = imageIds[closest];
const frameIndex = imageIds.indexOf(imageId);
if (segmentMeta) {
segmentNumber = segmentMeta.SegmentNumber;
segmentLabel = segmentMeta.SegmentLabel;
const SOPInstanceUID = cornerstone.metaData.get(
'SOPInstanceUID',
imageId
);
const StudyInstanceUID = cornerstone.metaData.get(
'StudyInstanceUID',
imageId
);
onSegItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex,
});
};
const enabledElements = cornerstone.getEnabledElements();
const enabledElementViewport = enabledElements[activeIndex];
let isVisible = true;
if (enabledElementViewport) {
const element = enabledElementViewport.element;
const module = cornerstoneTools.getModule('segmentation');
isVisible = module.getters.isSegmentVisible(
element,
segmentNumber,
brushStackState.activeLabelmapIndex
);
}
segmentList.push(
<SegmentItem
key={segmentNumber}
itemClass={`segment-item ${sameSegment && 'selected'}`}
onClick={setCurrentSelectedSegment}
label={segmentLabel}
index={segmentNumber}
color={color}
visible={isVisible}
onVisibilityChange={() => {
const element = enabledElements[activeIndex].element;
module.setters.toggleSegmentVisibility(
element,
segmentNumber,
brushStackState.activeLabelmapIndex
);
refreshViewport();
}}
/>
);
}
const sameSegment = state.selectedSegment === segmentNumber;
const setCurrentSelectedSegment = () => {
_setActiveSegment(
firstImageId,
segmentNumber,
labelmap3D.activeSegmentIndex
);
updateState('selectedSegment', sameSegment ? null : segmentNumber);
};
return segmentList;
segmentList.push(
<SegmentItem
key={segmentNumber}
itemClass={`segment-item ${sameSegment && 'selected'}`}
onClick={setCurrentSelectedSegment}
label={segmentLabel}
index={segmentNumber}
color={color}
/>
);
}
return segmentList;
/*
* Let's iterate over segmentIndexes ^ above
* If meta has a match, use it to show info
* If now, add "no-meta" class
* Show default name
*/
};
/*
* Let's iterate over segmentIndexes ^ above
* If meta has a match, use it to show info
* If now, add "no-meta" class
* Show default name
*/
},
[activeIndex, onSegItemClick, state.selectedSegment]
);
const updateState = (field, value) => {
setState(state => ({ ...state, [field]: value }));
@@ -270,8 +367,7 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
}
const module = cornerstoneTools.getModule('segmentation');
const colorLutTable =
module.state.colorLutTables[labelmap3D.colorLUTIndex];
const colorLutTable = module.state.colorLutTables[labelmap3D.colorLUTIndex];
const color = colorLutTable[labelmap3D.activeSegmentIndex];
return `rgba(${color.join(',')})`;
@@ -329,7 +425,9 @@ const SegmentationPanel = ({ studies, viewports, activeIndex, isOpen }) => {
<div className="segmentations">
<SegmentationSelect
value={
state.labelmapList.find(i => i.value === state.selectedSegmentation) || null
state.labelmapList.find(
i => i.value === state.selectedSegmentation
) || null
}
formatOptionLabel={SegmentationItem}
options={state.labelmapList}
@@ -17,6 +17,10 @@ const defaultColor = computedstyle.getPropertyValue('--default-color');
const uiGrayDark = computedstyle.getPropertyValue('--ui-gray-dark');
const segmentationSelectStyles = {
singleValue: (base, state) => ({
...base,
width: '100%'
}),
control: (base, state) => ({
...base,
cursor: 'pointer',
+51 -3
View File
@@ -1,7 +1,9 @@
import React from 'react';
import init from './init.js';
import toolbarModule from './toolbarModule.js';
import panelModule from './panelModule.js';
import sopClassHandlerModule from './OHIFDicomSegSopClassHandler.js';
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
export default {
/**
@@ -21,8 +23,54 @@ export default {
getToolbarModule({ servicesManager }) {
return toolbarModule;
},
getPanelModule({ servicesManager }) {
return panelModule;
getPanelModule({ commandsManager }) {
const ExtendedSegmentationPanel = props => {
const segItemClickHandler = segData => {
commandsManager.runCommand('jumpToImage', segData);
};
return (
<SegmentationPanel {...props} onSegItemClick={segItemClickHandler} />
);
};
return {
menuOptions: [
{
icon: 'list',
label: 'Segmentations',
target: 'segmentation-panel',
isDisabled: studies => {
if (!studies) {
return true;
}
for (let i = 0; i < studies.length; i++) {
const study = studies[i];
if (study && study.series) {
for (let j = 0; j < study.series.length; j++) {
const series = study.series[j];
if (series.Modality === 'SEG') {
return false;
}
}
}
}
return true;
},
},
],
components: [
{
id: 'segmentation-panel',
component: ExtendedSegmentationPanel,
},
],
defaultContext: ['VIEWER'],
};
},
getSopClassHandlerModule({ servicesManager }) {
return sopClassHandlerModule;
@@ -1,39 +0,0 @@
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
export default {
menuOptions: [
{
icon: 'list',
label: 'Segmentations',
target: 'segmentation-panel',
isDisabled: studies => {
if (!studies) {
return true;
}
for (let i = 0; i < studies.length; i++) {
const study = studies[i];
if (study && study.series) {
for (let j = 0; j < study.series.length; j++) {
const series = study.series[j];
if (series.Modality === 'SEG') {
return false;
}
}
}
}
return true;
},
},
],
components: [
{
id: 'segmentation-panel',
component: SegmentationPanel,
},
],
defaultContext: ['VIEWER'],
};
+38
View File
@@ -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.
# [1.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.4.1...@ohif/extension-vtk@1.5.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [1.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.4.0...@ohif/extension-vtk@1.4.1) (2020-04-23)
**Note:** Version bump only for package @ohif/extension-vtk
# [1.4.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.14...@ohif/extension-vtk@1.4.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## [1.3.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.13...@ohif/extension-vtk@1.3.14) (2020-04-22)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.3.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.3.12...@ohif/extension-vtk@1.3.13) (2020-04-17)
**Note:** Version bump only for package @ohif/extension-vtk
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-vtk",
"version": "1.3.13",
"version": "1.5.0",
"description": "OHIF extension for VTK.js",
"author": "OHIF",
"license": "MIT",
@@ -34,8 +34,8 @@
"@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"dcmjs": "^0.10.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2",
"dicom-parser": "^1.8.3",
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
@@ -53,10 +53,10 @@
"react-vtkjs-viewport": "^0.8.3"
},
"devDependencies": {
"@ohif/core": "^2.7.0",
"@ohif/core": "^2.9.0",
"@ohif/ui": "^1.4.3",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3",
"gh-pages": "^2.0.1",
"i18next": "^17.0.3",
+44
View File
@@ -3,6 +3,50 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.9.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.8.1...@ohif/core@2.9.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [2.8.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.8.0...@ohif/core@2.8.1) (2020-04-23)
### Bug Fixes
* 🐛 Multiframe fix ([#1661](https://github.com/OHIF/Viewers/issues/1661)) ([7120561](https://github.com/OHIF/Viewers/commit/71205618ecb8b592247c5acb32284bfe7e18fce5))
# [2.8.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.7.1...@ohif/core@2.8.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## [2.7.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.7.0...@ohif/core@2.7.1) (2020-04-22)
### Bug Fixes
* whiteLabeling should support component creation by passing React to defined fn ([#1659](https://github.com/OHIF/Viewers/issues/1659)) ([2093a00](https://github.com/OHIF/Viewers/commit/2093a0036584b2cc698c8f06fe62b334523b1029))
# [2.7.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.6.11...@ohif/core@2.7.0) (2020-04-17)
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/core",
"version": "2.7.0",
"version": "2.9.0",
"description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team",
"license": "MIT",
@@ -33,14 +33,14 @@
"peerDependencies": {
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3"
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"ajv": "^6.10.0",
"dcmjs": "^0.12.0",
"dicomweb-client": "^0.5.2",
"dcmjs": "^0.12.2",
"dicomweb-client": "^0.6.0",
"immer": "6.0.2",
"isomorphic-base64": "^1.0.2",
"lodash.clonedeep": "^4.5.0",
@@ -5,6 +5,7 @@ import DICOMWeb from '../DICOMWeb';
import parseDicomStructuredReport from './parseDicomStructuredReport';
import parseMeasurementsData from './parseMeasurementsData';
import getAllDisplaySets from './utils/getAllDisplaySets';
import errorHandler from '../errorHandler';
const VERSION_NAME = 'dcmjs-0.0';
const TRANSFER_SYNTAX_UID = '1.2.840.10008.1.2.1';
@@ -21,6 +22,7 @@ const retrieveMeasurementFromSR = async (series, studies, serverUrl) => {
const config = {
url: serverUrl,
headers: DICOMWeb.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -71,6 +73,7 @@ const stowSRFromMeasurements = async (measurements, serverUrl) => {
const config = {
url: serverUrl,
headers: DICOMWeb.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -378,7 +378,7 @@ class MetadataProvider {
}
metadata = {
overlays
overlays,
};
break;
@@ -400,6 +400,7 @@ class MetadataProvider {
case WADO_IMAGE_LOADER_TAGS.GENERAL_IMAGE_MODULE:
metadata = {
sopInstanceUid: instance.SOPInstanceUID,
instanceNumber: instance.InstanceNumber,
lossyImageCompression: instance.LossyImageCompression,
lossyImageCompressionRatio: instance.LossyImageCompressionRatio,
@@ -11,6 +11,7 @@ import { api } from 'dicomweb-client';
import { isImage } from '../../utils/isImage';
import isDisplaySetReconstructable from '../../utils/isDisplaySetReconstructable';
import isLowPriorityModality from '../../utils/isLowPriorityModality';
import errorHandler from '../../errorHandler';
export class StudyMetadata extends Metadata {
constructor(data, uid) {
@@ -102,10 +103,7 @@ export class StudyMetadata extends Metadata {
* @param {SeriesMetadata} series The series metadata object from which the display sets will be created
* @returns {Array} The list of display sets created for the given series object
*/
_createDisplaySetsForSeries(
sopClassHandlerModules,
series,
) {
_createDisplaySetsForSeries(sopClassHandlerModules, series) {
const study = this;
const displaySets = [];
@@ -293,14 +291,13 @@ export class StudyMetadata extends Metadata {
}
// Loop through the series (SeriesMetadata)
this.forEachSeries(
series => {
const displaySetsForSeries = this._createDisplaySetsForSeries(
sopClassHandlerModules,
series,
);
this.forEachSeries(series => {
const displaySetsForSeries = this._createDisplaySetsForSeries(
sopClassHandlerModules,
series
);
displaySets.push(...displaySetsForSeries);
displaySets.push(...displaySetsForSeries);
});
return sortDisplaySetList(displaySets);
@@ -321,7 +318,10 @@ export class StudyMetadata extends Metadata {
return false;
}
const displaySets = this._createDisplaySetsForSeries(sopClassHandlerModules, series)
const displaySets = this._createDisplaySetsForSeries(
sopClassHandlerModules,
series
);
// Note: filtering in place because this._displaySets has writable: false
for (let i = this._displaySets.length - 1; i >= 0; i--) {
@@ -835,9 +835,11 @@ function _getDisplaySetFromSopClassModule(
const plugin = handlersForSopClassUID[0];
const headers = DICOMWeb.getAuthorizationHeader();
const errorInterceptor = errorHandler.getHTTPErrorHandler();
const dicomWebClient = new dwc({
url: study.getData().wadoRoot,
headers,
errorInterceptor,
});
let displaySet = plugin.getDisplaySetFromSeries(
+6
View File
@@ -0,0 +1,6 @@
// These should be overridden by the implementation
const errorHandler = {
getHTTPErrorHandler: () => null,
};
export default errorHandler;
+3
View File
@@ -18,6 +18,7 @@ import string from './string.js';
import studies from './studies/';
import ui from './ui';
import user from './user.js';
import errorHandler from './errorHandler.js';
import utils, { hotkeys } from './utils/';
import {
@@ -46,6 +47,7 @@ const OHIF = {
string,
ui,
user,
errorHandler,
object,
log,
DICOMWeb,
@@ -79,6 +81,7 @@ export {
string,
ui,
user,
errorHandler,
object,
log,
DICOMWeb,
+1
View File
@@ -24,6 +24,7 @@ describe('Top level exports', () => {
'header',
'cornerstone',
'default', //
'errorHandler',
'string',
'ui',
'user',
@@ -1,6 +1,8 @@
import DICOMWeb from '../../../DICOMWeb/';
import { api } from 'dicomweb-client';
import errorHandler from '../../../errorHandler';
/**
* Parses data returned from a QIDO search and transforms it into
* an array of series that are present in the study
@@ -79,6 +81,7 @@ export default function Instances(server, StudyInstanceUID) {
const config = {
url: server.qidoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
const queryParams = getQIDOQueryParams(
@@ -1,6 +1,8 @@
import { api } from 'dicomweb-client';
import DICOMWeb from '../../../DICOMWeb/';
import errorHandler from '../../../errorHandler';
/**
* Creates a QIDO date string for a date range query
* Assumes the year is positive, at most 4 digits long.
@@ -115,6 +117,7 @@ export default function Studies(server, filter) {
const config = {
url: server.qidoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -9,6 +9,8 @@ import {
addInstancesToStudy,
} from './studyInstanceHelpers';
import errorHandler from '../../../errorHandler';
const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary;
/**
@@ -73,6 +75,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
const client = new api.DICOMwebClient({
url: server.qidoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
});
this.client = client;
@@ -117,7 +120,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
return {
seriesInstanceUIDsMap,
seriesData
seriesData,
};
}
@@ -155,11 +158,14 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
SeriesDescription: series.SeriesDescription,
SeriesNumber: series.SeriesNumber,
Modality: series.Modality,
instances: []
instances: [],
};
if (study.series[idx]) {
study.series[idx] = Object.assign(seriesDataFromQIDO, study.series[idx]);
study.series[idx] = Object.assign(
seriesDataFromQIDO,
study.series[idx]
);
} else {
study.series[idx] = seriesDataFromQIDO;
}
@@ -3,6 +3,8 @@ import DICOMWeb from '../../../DICOMWeb/';
import { createStudyFromSOPInstanceList } from './studyInstanceHelpers';
import RetrieveMetadataLoader from './retrieveMetadataLoader';
import errorHandler from '../../../errorHandler';
/**
* Class for sync load of study metadata.
* It inherits from RetrieveMetadataLoader
@@ -58,6 +60,7 @@ export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
const client = new api.DICOMwebClient({
url: server.wadoRoot,
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
});
this.client = client;
@@ -144,7 +144,7 @@ async function makeSOPInstance(server, study, instance) {
wadorsuri,
wadoRoot: server.wadoRoot,
imageRendering: server.imageRendering,
thumbnailRendering: server.thumbnailRendering
thumbnailRendering: server.thumbnailRendering,
};
series.instances.push(sopInstance);
@@ -160,8 +160,10 @@ async function makeSOPInstance(server, study, instance) {
const wadoRSMetadata = Object.assign(instance);
if (sopInstance.NumberOfFrames) {
for (let i = 0; i < sopInstance.NumberOfFrames; i++) {
const { NumberOfFrames } = sopInstance.metadata;
if (NumberOfFrames) {
for (let i = 0; i < NumberOfFrames; i++) {
const wadorsImageId = getWADORSImageId(sopInstance, i);
cornerstoneWADOImageLoader.wadors.metaDataManager.add(
@@ -190,7 +192,7 @@ async function makeSOPInstance(server, study, instance) {
*/
async function addInstancesToStudy(server, study, sopInstanceList) {
return Promise.all(
sopInstanceList.map(function (sopInstance) {
sopInstanceList.map(function(sopInstance) {
return makeSOPInstance(server, study, sopInstance);
})
);
@@ -3,6 +3,8 @@ import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import { api } from 'dicomweb-client';
import DICOMWeb from '../DICOMWeb';
import errorHandler from '../errorHandler';
const getImageId = imageObj => {
if (!imageObj) {
return;
@@ -57,11 +59,13 @@ const wadorsRetriever = (
studyInstanceUID,
seriesInstanceUID,
sopInstanceUID,
headers = DICOMWeb.getAuthorizationHeader()
headers = DICOMWeb.getAuthorizationHeader(),
errorInterceptor = errorHandler.getHTTPErrorHandler()
) => {
const config = {
url,
headers,
errorInterceptor,
};
const dicomWeb = new api.DICOMwebClient(config);
@@ -3,6 +3,8 @@ import DICOMWeb from '../../DICOMWeb';
import str2ab from '../str2ab';
import unpackOverlay from './unpackOverlay';
import errorHandler from '../../errorHandler';
export default async function fetchOverlayData(instance, server) {
const OverlayDataPromises = [];
const OverlayDataTags = [];
@@ -21,14 +23,20 @@ export default async function fetchOverlayData(instance, server) {
const inlineBinaryData = atob(instance[OverlayDataTag].InlineBinary);
const arraybuffer = str2ab(inlineBinaryData);
instance[OverlayDataTag] = unpackOverlay(arraybuffer)
} else if (instance[OverlayDataTag] && instance[OverlayDataTag].BulkDataURI) {
instance[OverlayDataTag] = unpackOverlay(arraybuffer);
} else if (
instance[OverlayDataTag] &&
instance[OverlayDataTag].BulkDataURI
) {
OverlayDataPromises.push(
_getOverlayData(instance[OverlayDataTag], server)
);
OverlayDataTags.push(OverlayDataTag);
} else if (instance[OverlayDataTag] && instance[OverlayDataTag] instanceof ArrayBuffer) {
instance[OverlayDataTag] = unpackOverlay(instance[OverlayDataTag])
} else if (
instance[OverlayDataTag] &&
instance[OverlayDataTag] instanceof ArrayBuffer
) {
instance[OverlayDataTag] = unpackOverlay(instance[OverlayDataTag]);
}
}
@@ -60,6 +68,7 @@ async function _getOverlayData(tag, server) {
const config = {
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
const options = {
@@ -71,5 +80,3 @@ async function _getOverlayData(tag, server) {
.then(result => result[0])
.then(unpackOverlay);
}
@@ -2,6 +2,8 @@ import { api } from 'dicomweb-client';
import DICOMWeb from '../../DICOMWeb';
import str2ab from '../str2ab';
import errorHandler from '../../errorHandler';
export default async function fetchPaletteColorLookupTableData(
instance,
server
@@ -146,6 +148,7 @@ function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
const config = {
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
headers: DICOMWeb.getAuthorizationHeader(server),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const dicomWeb = new api.DICOMwebClient(config);
const options = {
@@ -164,6 +167,8 @@ function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
resolve(arrayBufferToPaletteColorLUT(arraybuffer));
});
} else {
return Promise.resolve(arrayBufferToPaletteColorLUT(paletteColorLookupTableData));
return Promise.resolve(
arrayBufferToPaletteColorLUT(paletteColorLookupTableData)
);
}
}
+63
View File
@@ -3,6 +3,69 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.10.2...@ohif/viewer@3.11.0) (2020-04-24)
### Features
* 🎸 Seg jump to slice + show/hide ([835f64d](https://github.com/OHIF/Viewers/commit/835f64d47a9994f6a25aaf3941a4974e215e7e7f))
## [3.10.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.10.1...@ohif/viewer@3.10.2) (2020-04-23)
### Bug Fixes
* undefined `errorHandler` in cornerstoneWadoImageLoader configuration ([#1664](https://github.com/OHIF/Viewers/issues/1664)) ([709f147](https://github.com/OHIF/Viewers/commit/709f14708e2b0f912b5ea509114acd87af3149cb))
## [3.10.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.10.0...@ohif/viewer@3.10.1) (2020-04-23)
**Note:** Version bump only for package @ohif/viewer
# [3.10.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.9.2...@ohif/viewer@3.10.0) (2020-04-23)
### Features
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
## [3.9.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.9.1...@ohif/viewer@3.9.2) (2020-04-22)
### Bug Fixes
* whiteLabeling should support component creation by passing React to defined fn ([#1659](https://github.com/OHIF/Viewers/issues/1659)) ([2093a00](https://github.com/OHIF/Viewers/commit/2093a0036584b2cc698c8f06fe62b334523b1029))
## [3.9.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.9.0...@ohif/viewer@3.9.1) (2020-04-17)
### Bug Fixes
* `showStudyList` config ([#1647](https://github.com/OHIF/Viewers/issues/1647)) ([d9fc7bb](https://github.com/OHIF/Viewers/commit/d9fc7bbb0e6d868f507c515f031aaf88a2353e2f))
# [3.9.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.8.21...@ohif/viewer@3.9.0) (2020-04-17)
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/viewer",
"version": "3.9.0",
"version": "3.11.0",
"description": "OHIF Viewer",
"author": "OHIF Contributors",
"license": "MIT",
@@ -48,16 +48,16 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"@ohif/core": "^2.7.0",
"@ohif/extension-cornerstone": "^2.5.2",
"@ohif/extension-dicom-html": "^1.1.2",
"@ohif/core": "^2.9.0",
"@ohif/extension-cornerstone": "^2.7.0",
"@ohif/extension-dicom-html": "^1.2.0",
"@ohif/extension-dicom-microscopy": "^0.50.9",
"@ohif/extension-dicom-p10-downloader": "^0.0.2",
"@ohif/extension-dicom-p10-downloader": "^0.1.0",
"@ohif/extension-dicom-pdf": "^1.0.3",
"@ohif/extension-dicom-rt": "^0.0.2",
"@ohif/extension-dicom-segmentation": "^0.1.2",
"@ohif/extension-dicom-rt": "^0.1.0",
"@ohif/extension-dicom-segmentation": "^0.2.0",
"@ohif/extension-lesion-tracker": "^0.2.0",
"@ohif/extension-vtk": "^1.3.13",
"@ohif/extension-vtk": "^1.5.0",
"@ohif/i18n": "^0.52.8",
"@ohif/ui": "^1.4.3",
"@tanem/react-nprogress": "^1.1.25",
@@ -66,8 +66,8 @@
"cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.12.5",
"cornerstone-wado-image-loader": "^3.0.0",
"dcmjs": "^0.12.0",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2",
"dicom-parser": "^1.8.3",
"dicomweb-client": "^0.4.4",
"hammerjs": "^2.0.8",
-1
View File
@@ -1,7 +1,6 @@
window.config = {
// default: '/'
routerBasename: '/',
whiteLabelling: {},
extensions: [],
showStudyList: true,
filterQueryParam: false,
-1
View File
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
extensions: [],
showStudyList: true,
servers: {
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
showStudyList: true,
servers: {
dicomWeb: [
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
showStudyList: true,
servers: {
// This is an array, but we'll only use the first entry for now
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
showStudyList: true,
servers: {
// This is an array, but we'll only use the first entry for now
-1
View File
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
enableGoogleCloudAdapter: true,
healthcareApiEndpoint: 'https://healthcare.googleapis.com/v1beta1',
servers: {
+19
View File
@@ -0,0 +1,19 @@
window.config = function(props) {
var servicesManager = props.servicesManager;
return {
routerBasename: '/',
enableGoogleCloudAdapter: true,
enableGoogleCloudAdapterUI: false,
showStudyList: true,
httpErrorHandler: error => {
// This is 429 when rejected from the public idc sandbox too often.
console.warn(error.status);
// Could use services manager here to bring up a dialog/modal if needed.
console.warn('test, navigate to https://ohif.org/');
window.location = 'https://ohif.org/';
},
healthcareApiEndpoint: 'https://idc-sandbox-002.appspot.com/v1beta1',
};
};
@@ -1,7 +1,6 @@
window.config = {
// default: '/'
routerBasename: '/',
whiteLabelling: {},
// default: ''
showStudyList: true,
servers: {
-1
View File
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/pwa',
whiteLabelling: {},
showStudyList: true,
servers: {
dicomWeb: [
@@ -1,6 +1,5 @@
window.config = {
routerBasename: '/',
whiteLabelling: {},
showStudyList: true,
servers: {
dicomWeb: [
+15 -10
View File
@@ -31,7 +31,8 @@ import {
import i18n from '@ohif/i18n';
// TODO: This should not be here
import './config';
//import './config';
import { setConfiguration } from './config';
/** Utils */
import {
@@ -50,7 +51,7 @@ import { getActiveContexts } from './store/layout/selectors.js';
import store from './store';
/** Contexts */
import WhiteLabellingContext from './context/WhiteLabellingContext';
import WhiteLabelingContext from './context/WhiteLabelingContext';
import UserManagerContext from './context/UserManagerContext';
import AppContext from './context/AppContext';
@@ -77,7 +78,9 @@ class App extends Component {
PropTypes.shape({
routerBasename: PropTypes.string.isRequired,
oidc: PropTypes.array,
whiteLabelling: PropTypes.object,
whiteLabeling: PropTypes.shape({
createLogoComponentFn: PropTypes.func,
}),
extensions: PropTypes.array,
}),
]).isRequired,
@@ -86,7 +89,7 @@ class App extends Component {
static defaultProps = {
config: {
whiteLabelling: {},
showStudyList: true,
oidc: [],
extensions: [],
},
@@ -102,10 +105,10 @@ class App extends Component {
const { config, defaultExtensions } = props;
const appDefaultConfig = {
showStudyList: true,
cornerstoneExtensionConfig: {},
extensions: [],
routerBasename: '/',
whiteLabelling: {},
};
this._appConfig = {
@@ -121,6 +124,8 @@ class App extends Component {
oidc,
} = this._appConfig;
setConfiguration(this._appConfig);
this.initUserManager(oidc);
_initServices([
UINotificationService,
@@ -144,7 +149,7 @@ class App extends Component {
}
render() {
const { whiteLabelling, routerBasename } = this._appConfig;
const { whiteLabeling, routerBasename } = this._appConfig;
const {
UINotificationService,
UIDialogService,
@@ -160,7 +165,7 @@ class App extends Component {
<OidcProvider store={store} userManager={this._userManager}>
<UserManagerContext.Provider value={this._userManager}>
<Router basename={routerBasename}>
<WhiteLabellingContext.Provider value={whiteLabelling}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider
@@ -173,7 +178,7 @@ class App extends Component {
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabellingContext.Provider>
</WhiteLabelingContext.Provider>
</Router>
</UserManagerContext.Provider>
</OidcProvider>
@@ -188,7 +193,7 @@ class App extends Component {
<Provider store={store}>
<I18nextProvider i18n={i18n}>
<Router basename={routerBasename}>
<WhiteLabellingContext.Provider value={whiteLabelling}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider modal={OHIFModal} service={UIModalService}>
@@ -196,7 +201,7 @@ class App extends Component {
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabellingContext.Provider>
</WhiteLabelingContext.Provider>
</Router>
</I18nextProvider>
</Provider>
@@ -4,8 +4,16 @@
height: var(--top-bar-height);
}
.dd-item {
width: 100%;
.header-left-box {
display: inline-block;
position: relative;
}
.header-brand {
text-decoration: none;
height: 30px;
float: left;
clear: both;
}
/* Home Page */
@@ -17,35 +25,11 @@
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;
height: 50px;
padding: 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-right: 20px;
width: 50px;
@@ -57,6 +41,24 @@
height: auto;
}
/** OTHER STYLES **/
.dd-item {
width: 100%;
}
.header-viewerLink {
float: left;
}
.header-logo-image {
fill: transparent;
height: 100%;
margin: 0 8px 0 0;
width: 30px;
float: left;
}
.header-btn {
color: var(--text-secondary-color);
cursor: pointer;
@@ -130,13 +132,6 @@
width: 40%;
}
/* Account for notification bar height */
.entry-header.header-big .header-brand {
}
.dd-menu {
}
/* Toggle Notification Bar */
.notification-bar {
display: block;
+18 -18
View File
@@ -1,30 +1,29 @@
import React, { useState, useEffect } from 'react';
import { Link, withRouter } from 'react-router-dom';
import { withTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Dropdown, AboutContent, withModal } from '@ohif/ui';
//
import { UserPreferences } from './../UserPreferences';
import OHIFLogo from '../OHIFLogo/OHIFLogo.js';
import './Header.css';
// Context
import AppContext from './../../context/AppContext';
function Header(props) {
const {
t,
user,
userManager,
modal: { show },
home,
useLargeLogo,
linkPath,
linkText,
location,
children,
} = props;
const [options, setOptions] = useState([]);
const hasLink = linkText && linkPath;
useEffect(() => {
const optionsValue = [
@@ -61,15 +60,12 @@ function Header(props) {
setOptions(optionsValue);
}, [setOptions, show, t, user, userManager]);
const { appConfig = {} } = AppContext;
const showStudyList =
appConfig.showStudyList !== undefined ? appConfig.showStudyList : true;
// ANTD -- Hamburger, Drawer, Menu
return (
<>
<div className="notification-bar">{t('INVESTIGATIONAL USE ONLY')}</div>
<div className={`entry-header ${home ? 'header-big' : ''}`}>
<div
className={classNames('entry-header', { 'header-big': useLargeLogo })}
>
<div className="header-left-box">
{location && location.studyLink && (
<Link
@@ -82,15 +78,15 @@ function Header(props) {
{children}
{showStudyList && !home && (
{hasLink && (
<Link
className="header-btn header-studyListLinkSection"
to={{
pathname: '/',
pathname: linkPath,
state: { studyLink: location.pathname },
}}
>
{t('Study list')}
{t(linkText)}
</Link>
)}
</div>
@@ -105,7 +101,11 @@ function Header(props) {
}
Header.propTypes = {
home: PropTypes.bool.isRequired,
// Study list, /
linkText: PropTypes.string,
linkPath: PropTypes.string,
useLargeLogo: PropTypes.bool,
//
location: PropTypes.object.isRequired,
children: PropTypes.node,
t: PropTypes.func.isRequired,
@@ -115,7 +115,7 @@ Header.propTypes = {
};
Header.defaultProps = {
home: true,
useLargeLogo: false,
children: OHIFLogo(),
};
+50 -34
View File
@@ -3,43 +3,59 @@ import cornerstone from 'cornerstone-core';
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import dicomParser from 'dicom-parser';
import version from './version.js';
import AppContext from './context/AppContext';
let homepage;
const { process } = window;
if (process && process.env && process.env.PUBLIC_URL) {
homepage = process.env.PUBLIC_URL;
}
window.info = {
version,
homepage,
};
// For debugging
//if (process.env.node_env === 'development') {
window.cornerstone = cornerstone;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
//}
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.user.getAccessToken = () => {
// TODO: Get the Redux store from somewhere else
const state = window.store.getState();
if (!state.oidc || !state.oidc.user) {
return;
export function setConfiguration(appConfig) {
let homepage;
const { process } = window;
if (process && process.env && process.env.PUBLIC_URL) {
homepage = process.env.PUBLIC_URL;
}
return state.oidc.user.access_token;
};
window.info = {
version,
homepage,
};
cornerstoneWADOImageLoader.configure({
beforeSend: function(xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
// For debugging
//if (process.env.node_env === 'development') {
window.cornerstone = cornerstone;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
//}
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization);
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
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;
};
OHIF.errorHandler.getHTTPErrorHandler = () => {
// const { appConfig = {} } = AppContext;
return appConfig.httpErrorHandler;
};
cornerstoneWADOImageLoader.configure({
beforeSend: function(xhr) {
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
if (headers.Authorization) {
xhr.setRequestHeader('Authorization', headers.Authorization);
}
},
errorInterceptor: error => {
// const { appConfig = {} } = AppContext;
if (typeof appConfig.httpErrorHandler === 'function') {
appConfig.httpErrorHandler(error);
}
},
});
}
@@ -14,8 +14,9 @@ import SidePanel from './../components/SidePanel.js';
import { extensionManager } from './../App.js';
// Contexts
import WhiteLabellingContext from '../context/WhiteLabellingContext.js';
import WhiteLabelingContext from '../context/WhiteLabelingContext.js';
import UserManagerContext from '../context/UserManagerContext';
import AppContext from '../context/AppContext';
import './Viewer.css';
@@ -226,17 +227,33 @@ class Viewer extends Component {
return (
<>
{/* HEADER */}
<WhiteLabellingContext.Consumer>
{whiteLabelling => (
<WhiteLabelingContext.Consumer>
{whiteLabeling => (
<UserManagerContext.Consumer>
{userManager => (
<ConnectedHeader home={false} userManager={userManager}>
{whiteLabelling.logoComponent}
</ConnectedHeader>
<AppContext.Consumer>
{appContext => (
<ConnectedHeader
linkText={
appContext.appConfig.showStudyList
? 'Study List'
: undefined
}
linkPath={
appContext.appConfig.showStudyList ? '/' : undefined
}
userManager={userManager}
>
{whiteLabeling &&
whiteLabeling.createLogoComponentFn &&
whiteLabeling.createLogoComponentFn(React)}
</ConnectedHeader>
)}
</AppContext.Consumer>
)}
</UserManagerContext.Consumer>
)}
</WhiteLabellingContext.Consumer>
</WhiteLabelingContext.Consumer>
{/* TOOLBAR */}
<ConnectedToolbarRow
@@ -290,11 +307,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 */}
@@ -332,7 +349,7 @@ export default withDialog(Viewer);
* @param {Study[]} studies
* @param {DisplaySet[]} studies[].displaySets
*/
const _mapStudiesToThumbnails = function (studies) {
const _mapStudiesToThumbnails = function(studies) {
return studies.map(study => {
const { StudyInstanceUID } = study;
@@ -0,0 +1,10 @@
import OHIFLogo from '../components/OHIFLogo/OHIFLogo.js';
import React from 'react';
const defaultContextValues = {
createLogoComponentFn: React => OHIFLogo(),
};
const WhiteLabelingContext = React.createContext(defaultContextValues);
export default WhiteLabelingContext;
@@ -1,10 +0,0 @@
import OHIFLogo from '../components/OHIFLogo/OHIFLogo.js';
import React from 'react';
const defaultContextValues = {
logoComponent: OHIFLogo(),
};
const WhiteLabellingContext = React.createContext(defaultContextValues);
export default WhiteLabellingContext;
@@ -1,5 +1,6 @@
import { httpErrorToStr, checkDicomFile } from '../utils/helpers';
import { api } from 'dicomweb-client';
import { errorHandler } from '@ohif/core';
class DicomUploadService {
async smartUpload(files, url, uploadCallback, cancellationToken) {
@@ -22,8 +23,7 @@ class DicomUploadService {
let error = null;
try {
if (chunk.length > 1) throw new Error('Not implemented');
if (chunk.length === 1)
await this.simpleUpload(chunk[0], url);
if (chunk.length === 1) await this.simpleUpload(chunk[0], url);
} catch (err) {
// It looks like a stupid bug of Babel that err is not an actual Exception object
error = httpErrorToStr(err);
@@ -76,11 +76,12 @@ class DicomUploadService {
getClient(url) {
const headers = this.retrieveAuthHeaderFunc();
const errorInterceptor = errorHandler.getHTTPErrorHandler();
// TODO: a bit weird we are creating a new dicomweb client instance for every upload
return new api.DICOMwebClient({
url,
headers
headers,
});
}
}
+2 -7
View File
@@ -45,9 +45,7 @@ const ROUTES_DEF = {
path: ['/studylist', '/'],
component: StudyListRouting,
condition: appConfig => {
return appConfig.showStudyList !== undefined
? appConfig.showStudyList
: true;
return appConfig.showStudyList;
},
},
local: {
@@ -72,10 +70,7 @@ const ROUTES_DEF = {
'/projects/:project/locations/:location/datasets/:dataset/dicomStores/:dicomStore',
component: StudyListRouting,
condition: appConfig => {
const showList =
appConfig.showStudyList !== undefined
? appConfig.showStudyList
: true;
const showList = appConfig.showStudyList;
return showList && !!appConfig.enableGoogleCloudAdapter;
},
@@ -19,7 +19,7 @@ import filesToStudies from '../lib/filesToStudies.js';
// Contexts
import UserManagerContext from '../context/UserManagerContext';
import WhiteLabellingContext from '../context/WhiteLabellingContext';
import WhiteLabelingContext from '../context/WhiteLabelingContext';
import AppContext from '../context/AppContext';
const { urlUtil: UrlUtil } = OHIF.utils;
@@ -209,21 +209,23 @@ function StudyListRoute(props) {
/>
) : null}
{healthCareApiWindows}
<WhiteLabellingContext.Consumer>
{whiteLabelling => (
<WhiteLabelingContext.Consumer>
{whiteLabeling => (
<UserManagerContext.Consumer>
{userManager => (
<ConnectedHeader
home={true}
useLargeLogo={true}
user={user}
userManager={userManager}
>
{whiteLabelling.logoComponent}
{whiteLabeling &&
whiteLabeling.createLogoComponentFn &&
whiteLabeling.createLogoComponentFn(React)}
</ConnectedHeader>
)}
</UserManagerContext.Consumer>
)}
</WhiteLabellingContext.Consumer>
</WhiteLabelingContext.Consumer>
<div className="study-list-header">
<div className="header">
<h1 style={{ fontWeight: 300, fontSize: '22px' }}>
+17 -12
View File
@@ -5953,10 +5953,10 @@ cornerstone-tools@4.12.5:
"@babel/runtime" "7.1.2"
cornerstone-math "0.1.7"
cornerstone-wado-image-loader@^3.0.0:
version "3.0.6"
resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.0.6.tgz#7b2be91f8f658de3a7474e029aa8314a35725132"
integrity sha512-1luWOh1GzkboZ3uxfu4T8vh7K9nlDiiEbQ4dYIY9oobRZVZQbQiLT8wEZPbb4JTUe7UWJISHPCNgtAU8UE9DNw==
cornerstone-wado-image-loader@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.1.0.tgz#6007470be896678636da3188f194f5e66ed07858"
integrity sha512-n2yadbwK7rCHqJyh5AUmYVPUY5m7boKpheK60sXV41Z0eSZLin9Ja+P9yeAvkg0VGV+/zaBZGPUGY3YyjdWBgg==
dependencies:
dicom-parser "^1.8.3"
pako "^1.0.10"
@@ -6534,10 +6534,10 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
dcmjs@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.12.0.tgz#8b1634f9b66e452075295f5d1f2c5bf3dabdf03e"
integrity sha512-AZAnFMvzAxUv5+KWoZcxnTYflLKk0rNPeeFt2KrVAgaExFBFfDgJQh1lEgqdNlIP+XQDFrEEUmp726SQhgmVCg==
dcmjs@^0.12.2:
version "0.12.2"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.12.2.tgz#1c16e19c27ff43202abc7cd25c5ecd6d8bf7672e"
integrity sha512-B7cEVCfDi3mOLKoBYUMYy+6COjLZhtKaesNpq5XNA4IbEH4EJYVweJfiI9gsR5hypv9LAPg72xG/xrbqjy8TgQ==
dependencies:
"@babel/polyfill" "^7.8.3"
"@babel/runtime" "^7.8.4"
@@ -6858,6 +6858,11 @@ dicomweb-client@^0.5.2:
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.5.2.tgz#aa5a4a6a5044b0702bb0a8a2662c86cd16901951"
integrity sha512-e11n2+g7HfBuMyopWq76W11eSZJ707g4U1XiO8URA+23aoe0g1kGAtlPB29NKXl7zX7RznTJ7wfRBxFkW6EJDA==
dicomweb-client@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/dicomweb-client/-/dicomweb-client-0.6.0.tgz#5e35ada52fe0155af1cc1f0e84f9c7f76477f92d"
integrity sha512-VAkBg4W6odIo2XsFxqjN/rptd7bQ8oHpRuKH5d46E9BUIPzRschazE8Dx1xg7/l3N3f1M70jB7yJ339arAXiDQ==
diff-sequences@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
@@ -11428,9 +11433,9 @@ jszip@3.2.2:
set-immediate-shim "~1.0.1"
jszip@^3.2.2:
version "3.3.0"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.3.0.tgz#29d72c21a54990fa885b11fc843db320640d5271"
integrity sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw==
version "3.4.0"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350"
integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
@@ -16385,7 +16390,7 @@ regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2:
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4:
version "0.13.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==