Compare commits

..
Author SHA1 Message Date
ohif-bot 41225eefc0 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.6.8
 - @ohif/core@2.9.13
 - @ohif/ui@1.5.2
 - @ohif/viewer@4.2.12
2020-07-22 19:12:41 +00:00
Erik Ziegler 7772fee21a fix: Switch DICOMFileUploader to use the UIModalService (#1904) 2020-07-22 21:10:04 +02:00
ohif-bot d870ac914a chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.8.4
 - @ohif/extension-dicom-rt@0.2.5
 - @ohif/extension-vtk@1.6.7
 - @ohif/core@2.9.12
 - @ohif/viewer@4.2.11
2020-07-13 16:03:44 +00:00
James Petts 451f7eab92 fix: 🐛 - Put guards in all places that a cornerstone re-render (#1899)
* fix: 🐛 - Put guards in all places that a cornerstone re-render
2020-07-13 18:02:23 +02:00
ohif-bot 029f8af4c7 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.8.3
 - @ohif/extension-dicom-html@1.2.4
 - @ohif/extension-dicom-rt@0.2.4
 - @ohif/extension-dicom-segmentation@0.3.3
 - @ohif/extension-vtk@1.6.6
 - @ohif/core@2.9.11
 - @ohif/viewer@4.2.10
2020-07-13 15:37:54 +00:00
James Petts b1b4c8c861 IDC 1856 - Update dcmjs version to prevent failed naturalization of some DICOM instances. (#1898)
* fix: 🐛 Fix RT Panel hide/show

* Prevent infinite loop by persisting load errors on displaySet.

* fix: 🐛 Fix infinite error messages

* Fix unit tests.

* WIP

* fix: 🐛 If no contours for structure, cleanly exit and notify

✅ Closes: #1892

* Update dcmjs to solve issue.

* fix: 🐛 Update dcmjs to solve end of file issue
2020-07-13 17:35:55 +02:00
ohif-bot cdde2af224 chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.2.3
 - @ohif/viewer@4.2.9
2020-07-13 14:30:47 +00:00
James Petts 4a2922dda0 Idc 1892 (#1896)
* fix: 🐛 Fix RT Panel hide/show

* Prevent infinite loop by persisting load errors on displaySet.

* fix: 🐛 Fix infinite error messages

* Fix unit tests.

* WIP

* fix: 🐛 If no contours for structure, cleanly exit and notify

✅ Closes: #1892
2020-07-13 16:29:29 +02:00
ohif-bot b6b5a8414f chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.2.2
 - @ohif/extension-dicom-segmentation@0.3.2
 - @ohif/extension-vtk@1.6.5
 - @ohif/core@2.9.10
 - @ohif/viewer@4.2.8
2020-07-13 12:53:22 +00:00
James Petts e7cc735c03 fix: 🐛 Fix RT Panel hide/show and Fix looping load errors (#1877)
* fix: 🐛 Fix RT Panel hide/show

* Prevent infinite loop by persisting load errors on displaySet.

* fix: 🐛 Fix infinite error messages

* Fix unit tests.

* comment out reamining MRSTUDY line
2020-07-13 14:51:33 +02:00
Danny Brown 6121bdcf63 Merge pull request #1828 from Aurelius333/patch-1
Fix README.md Quick Start Deployment instructions
2020-06-30 10:59:20 -04:00
Aurelius333 da70079675 Fix README.md Quick Start Deployment instructions
Change `window.OHIFStandaloneViewer` to `window.OHIFViewer`, since the former is undefined
2020-06-29 00:31:45 -04:00
ohif-bot 9089875ca7 chore(release): publish [skip ci]
- @ohif/extension-dicom-p10-downloader@0.1.2
 - @ohif/viewer@4.2.7
2020-06-18 09:01:18 +00:00
Rodrigo AntinarelliandJames Petts b4627ecfa3 Feat/1746 idc dev tool (#1778)
* feat: Create downloadAndUploadStudy method

* create upload handler to store instances in another server

* refactor and create reusable method to download buffers

* fix: e2e studies amount searching by Modality

* fix destructuring

* fix dataset and log info

* split instances to send multiple storeInstances calls

* minor refactor progress

* fix e2e

* replace error message for upload

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-06-18 10:59:07 +02:00
ohif-bot 4ede8a525a chore(release): publish [skip ci]
- @ohif/extension-vtk@1.6.4
 - @ohif/core@2.9.9
 - @ohif/viewer@4.2.6
2020-06-18 07:28:48 +00:00
Rodrigo Antinarelli 2c23818c86 Fix: #1776 - Segmentations not loading (#1795)
* temp comments analyze

* fix: cache derivedDisplaySets

* replace const

* minor refactor

* fix e2e inconsistent value

* increase e2e timeout
2020-06-18 09:24:39 +02:00
ohif-bot fa4b03d62d chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.8.2
 - @ohif/extension-dicom-html@1.2.3
 - @ohif/extension-dicom-rt@0.2.1
 - @ohif/extension-dicom-segmentation@0.3.1
 - @ohif/extension-vtk@1.6.3
 - @ohif/core@2.9.8
 - @ohif/viewer@4.2.5
2020-06-15 15:44:36 +00:00
Igor OctavianoandJames Petts 698e900b85 fix: 🐛 Disable seg panel when data for seg unavailable (#1732)
* Add disable state for invalid data

* fix: 🐛 Disable seg panel when data for seg unavailable

We need to gracefully handle errors if data for segmentation is
unavailable and disable the segmentation panel

✅ Closes: #1728

* Bump dcmjs version

dcmjs
  * @ohif/core: ^0.12.2 → ^0.13.0
  * @ohif/extension-cornerstone: ^0.12.3 → ^0.13.0
  * @ohif/extension-dicom-html: ^0.12.3 → ^0.13.0
  * @ohif/extension-dicom-rt: ^0.12.3 → ^0.13.0
  * @ohif/extension-dicom-segmentation: ^0.12.2 → ^0.13.0
  * @ohif/extension-vtk: ^0.12.2 → ^0.13.0
  * @ohif/viewer: ^0.12.2 → ^0.13.0

* Remove error barrier

* Fix e2e

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-06-15 17:41:36 +02:00
ohif-bot b7d47383c5 chore(release): publish [skip ci]
- @ohif/viewer@4.2.4
2020-06-15 12:47:19 +00:00
Rodrigo AntinarelliandJames Petts 6123741765 fix: OIDC Redirect erases query parameters (#1773)
* fix: OIDC Redirect erases query parameters

* prevent issues

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-06-15 14:43:10 +02:00
ohif-bot ab25be3520 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.6.2
 - @ohif/ui@1.5.1
 - @ohif/viewer@4.2.3
2020-06-05 16:03:56 +00:00
Rodrigo AntinarelliandJames Petts 950a54c704 Feat/1767 error boundary stack trace (#1772)
* feat: ErrorBoundary collapsed stack trace

* button styles

* Change "Toggle stack trace" to "Stack Trace"

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-06-05 18:00:36 +02:00
ohif-bot 4293c073f0 chore(release): publish [skip ci]
- @ohif/extension-dicom-p10-downloader@0.1.1
 - @ohif/extension-vtk@1.6.1
 - @ohif/core@2.9.7
 - @ohif/viewer@4.2.2
2020-06-04 11:02:53 +00:00
Emanuel Fiuza de OliveiraandJames Petts 62a2cd5ff3 Fix: #1130 Fixing support for download of an entire study (#1753)
Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-06-04 13:01:05 +02:00
ohif-bot 2d827c2a49 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.8.1
 - @ohif/viewer@4.2.1
2020-06-04 10:27:47 +00:00
Igor OctavianoandJames Petts a8305772d0 fix: 🐛 Infinite frame index change when quickly jumping between (#1734)
* Remove debounce and use react-cornerstone-viewport native debounce time

* fix: 🐛 Infinite frame index change when quickly jumping between

This change removes the debounced callback function from the app and
instead uses the native debounce time from react-cornerstone-viewport.
This change allows the application to set its state in a more controled
way.

✅ Closes: #1733

* Bump rcv version

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-06-04 12:25:19 +02:00
ohif-bot eab115e70c chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.8.0
 - @ohif/extension-dicom-microscopy@0.51.0
 - @ohif/extension-dicom-rt@0.2.0
 - @ohif/extension-dicom-segmentation@0.3.0
 - @ohif/extension-vtk@1.6.0
 - @ohif/ui@1.5.0
 - @ohif/viewer@4.2.0
2020-06-04 09:54:24 +00:00
c02b232b0c feat: 🎸 1729 - error boundary wrapper (#1764)
* Add error boundaries

* Fix PWA e2e.

* feat: ErrorBoundaryDialog

* replace component to use ErrorBoundaryDialog

* add proptypes

* fix context

* remove ErrorBoundary from extensions

Co-authored-by: igoroctaviano <igoroctaviano@gmail.com>
Co-authored-by: James A. Petts <jamesapetts@gmail.com>
2020-06-04 11:52:30 +02:00
ohif-bot f01640d009 chore(release): publish [skip ci]
- @ohif/viewer@4.1.0
2020-05-15 18:59:23 +00:00
Steve Pieperanddannyrb 63fd65690c feat: expose some app internals as window.app (#1735)
* feat: expose some app internals as window.app

This can help developers explore and access some
internal functionaltiy for debugging in the console.

For example, this command can download the currently
viewed study:

ohif.app.commandsManager.runCommand("downloadAndZip", {listOfUIDs: [window.location.href.split("/").pop()]})

TODO: collect this example and other handy functions on a wiki page

Co-authored-by: dannyrb <danny.ri.brown@gmail.com>

* fix: typo in window.ohif.app assignment

Co-authored-by: dannyrb <danny.ri.brown@gmail.com>
2020-05-15 14:55:54 -04:00
ohif-bot 944afe4a3e chore(release): publish [skip ci]
- @ohif/viewer@4.0.0
2020-05-14 14:24:17 +00:00
Igor OctavianoandJames Petts b1678ce639 fix: 🐛 Fix race condition when loading derived display sets (#1718)
When we have multiple SEGs/RTSTRUCTs we intend to load the most recent
by default.

BREAKING CHANGE: 🧨 However we start to load once the first set of metadata arrives. We need
to wait until all series metadata is fetched.

✅ Closes: #1715

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-05-14 16:20:23 +02:00
ohif-bot 39937cca08 chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.1.4
 - @ohif/extension-vtk@1.5.6
 - @ohif/core@2.9.6
 - @ohif/viewer@3.11.11
2020-05-14 11:53:42 +00:00
Igor OctavianoandJames Petts f7b8b6a41c fix: 🐛 Load default display set when no time metadata (#1684)
* fix: 🐛 Load default display set when no time metadata

Load default displayset (first item in the array) when no series date or
series time availalbe

✅ Closes: #1683

* Add batch update

* Fix tests.

Co-authored-by: James Petts <jamesapetts@gmail.com>
2020-05-14 13:51:57 +02:00
ohif-bot 79fe442293 chore(release): publish [skip ci]
- @ohif/viewer@3.11.10
2020-05-13 14:03:09 +00:00
Danny Brown 875238e5b0 chore: add fuzzymatching to default server configs
Add fuzzymatching to default server configs
2020-05-13 10:00:55 -04:00
Erik Ziegler d2d3ca2828 Add fuzzymatching to default server configs 2020-05-13 15:58:29 +02:00
ohif-bot cebea832a3 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.7.3
 - @ohif/extension-dicom-rt@0.1.3
 - @ohif/extension-dicom-segmentation@0.2.6
 - @ohif/extension-vtk@1.5.5
 - @ohif/core@2.9.5
 - @ohif/viewer@3.11.9
2020-05-12 14:49:58 +00:00
James Petts c4f84b1174 fix: 🐛 Fix seg color load (#1724) 2020-05-12 16:47:35 +02:00
ohif-bot f9fe4232e7 chore(release): publish [skip ci]
- @ohif/extension-dicom-segmentation@0.2.5
 - @ohif/extension-vtk@1.5.4
 - @ohif/core@2.9.4
 - @ohif/viewer@3.11.8
2020-05-06 17:20:22 +00:00
Igor Octaviano 42c22df1b6 Segmentation UI for VTKjs (#1685)
* Add single viewport configuration

* Multiple viewport configuration

* Improve performance by using independent set methods

* Add jump to slice command

* Add context configuration

* Cache panel visibility

* Fix sync between vtk and cornerstone

* Remove apis index

* Add approach

* Add loading to update volumes

* Fix broken configuration

* Bump vtk version

* Use loading label

* Update cy tests after vtk loading label changed

* Remove loading for segs
2020-05-06 19:17:44 +02:00
ohif-bot 19a8e71317 chore(release): publish [skip ci]
- @ohif/extension-dicom-segmentation@0.2.4
 - @ohif/viewer@3.11.7
2020-05-04 14:46:58 +00:00
James Petts fa95bae6ef Support initialization of segment color from RecommendedDisplayCIELabValue (#1709)
* fix: 🐛 Proper error handling for derived display sets

* Revert idc.js change

* Load correct Segment colors.
2020-05-04 16:43:09 +02:00
93 changed files with 2222 additions and 591 deletions

No files matched your search

+1 -1
View File
@@ -122,7 +122,7 @@ window.config = {
```
- Install the viewer:
`window.OHIFStandaloneViewer.installViewer(window.config);`
`window.OHIFViewer.installViewer(window.config);`
This exact setup is demonstrated in this
[CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our
+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.
## [2.8.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.3...@ohif/extension-cornerstone@2.8.4) (2020-07-13)
### Bug Fixes
* 🐛 - Put guards in all places that a cornerstone re-render ([#1899](https://github.com/OHIF/Viewers/issues/1899)) ([451f7ea](https://github.com/OHIF/Viewers/commit/451f7eab9258e7a193eb362e0926b13aedc4b3c9))
## [2.8.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.2...@ohif/extension-cornerstone@2.8.3) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.8.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.1...@ohif/extension-cornerstone@2.8.2) (2020-06-15)
### Bug Fixes
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
## [2.8.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.0...@ohif/extension-cornerstone@2.8.1) (2020-06-04)
### Bug Fixes
* 🐛 Infinite frame index change when quickly jumping between ([#1734](https://github.com/OHIF/Viewers/issues/1734)) ([a830577](https://github.com/OHIF/Viewers/commit/a8305772d0fbec506549ad7ea08504a8bc3a4f39)), closes [#1733](https://github.com/OHIF/Viewers/issues/1733)
# [2.8.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.3...@ohif/extension-cornerstone@2.8.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [2.7.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.2...@ohif/extension-cornerstone@2.7.3) (2020-05-12)
### Bug Fixes
* 🐛 Fix seg color load ([#1724](https://github.com/OHIF/Viewers/issues/1724)) ([c4f84b1](https://github.com/OHIF/Viewers/commit/c4f84b1174d04ba84d37ed89b6d7ab541be28181))
## [2.7.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.1...@ohif/extension-cornerstone@2.7.2) (2020-05-04)
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "2.7.2",
"version": "2.8.4",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -34,9 +34,9 @@
"@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.12.5",
"cornerstone-tools": "4.15.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.3",
"dcmjs": "0.16.0",
"dicom-parser": "^1.8.3",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
@@ -51,6 +51,6 @@
"classnames": "^2.2.6",
"lodash.merge": "^4.6.2",
"lodash.throttle": "^4.1.1",
"react-cornerstone-viewport": "2.3.8"
"react-cornerstone-viewport": "2.3.9"
}
}
@@ -4,7 +4,6 @@ import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
import OHIF from '@ohif/core';
import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import debounce from 'lodash.debounce';
const { StackManager } = OHIF.utils;
@@ -216,9 +215,10 @@ class OHIFCornerstoneViewport extends Component {
});
}
const debouncedNewImageHandler = debounce(({ currentImageIdIndex, sopInstanceUid }) => {
const newImageHandler = ({ currentImageIdIndex, sopInstanceUid }) => {
const { displaySet } = this.props.viewportData;
const { StudyInstanceUID } = displaySet;
if (currentImageIdIndex > 0) {
this.props.onNewImage({
StudyInstanceUID,
@@ -227,7 +227,7 @@ class OHIFCornerstoneViewport extends Component {
activeViewportIndex: viewportIndex,
});
}
}, 700);
};
return (
<>
@@ -235,7 +235,8 @@ class OHIFCornerstoneViewport extends Component {
viewportIndex={viewportIndex}
imageIds={imageIds}
imageIdIndex={currentImageIdIndex}
onNewImage={debouncedNewImageHandler}
onNewImage={newImageHandler}
onNewImageDebounceTime={700}
// ~~ Connected (From REDUX)
// frameRate={frameRate}
// isPlaying={false}
+19 -11
View File
@@ -10,6 +10,14 @@ const scroll = cornerstoneTools.import('util/scroll');
const { studyMetadataManager } = OHIF.utils;
const { setViewportSpecificData } = OHIF.redux.actions;
const refreshCornerstoneViewports = () => {
cornerstone.getEnabledElements().forEach(enabledElement => {
if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
});
};
const commandsModule = ({ servicesManager }) => {
const actions = {
rotateViewport: ({ viewports, rotation }) => {
@@ -180,10 +188,7 @@ const commandsModule = ({ servicesManager }) => {
measurementApi.syncMeasurementsAndToolData();
// Update images in all active viewports
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
refreshCornerstoneViewports();
},
getNearbyToolData({ element, canvasCoordinates, availableToolTypes }) {
const nearbyTool = {};
@@ -262,23 +267,26 @@ const commandsModule = ({ servicesManager }) => {
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex
activeViewportIndex,
}) => {
const study = studyMetadataManager.get(StudyInstanceUID);
const displaySet = study.findDisplaySet(ds => {
return ds.images && ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
return (
ds.images &&
ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
);
});
displaySet.SOPInstanceUID = SOPInstanceUID;
displaySet.frameIndex = frameIndex;
window.store.dispatch(setViewportSpecificData(activeViewportIndex, displaySet));
window.store.dispatch(
setViewportSpecificData(activeViewportIndex, displaySet)
);
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
}
refreshCornerstoneViewports();
},
};
const definitions = {
+3 -1
View File
@@ -39,7 +39,9 @@ export default {
const onNewImageHandler = jumpData => {
commandsManager.runCommand('jumpToImage', jumpData);
};
return <OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />;
return (
<OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />
);
};
return ExtendedOHIFCornerstoneViewport;
+19
View File
@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.3...@ohif/extension-dicom-html@1.2.4) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.2.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.2...@ohif/extension-dicom-html@1.2.3) (2020-06-15)
### Bug Fixes
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
## [1.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.1...@ohif/extension-dicom-html@1.2.2) (2020-05-04)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-html",
"version": "1.2.2",
"version": "1.2.4",
"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.12.3",
"dcmjs": "0.16.0",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
+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.51.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.9...@ohif/extension-dicom-microscopy@0.51.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [0.50.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.8...@ohif/extension-dicom-microscopy@0.50.9) (2020-04-02)
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-microscopy",
"version": "0.50.9",
"version": "0.51.0",
"description": "OHIF extension for Dicom Microscopy",
"author": "OHIF",
"license": "MIT",
@@ -92,8 +92,8 @@ class DicomMicroscopyViewport extends Component {
{this.state.error ? (
<h2>{JSON.stringify(this.state.error)}</h2>
) : (
<div style={style} ref={this.container} />
)}
<div style={style} ref={this.container} />
)}
</div>
);
}
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.1.1...@ohif/extension-dicom-p10-downloader@0.1.2) (2020-06-18)
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
## [0.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.1.0...@ohif/extension-dicom-p10-downloader@0.1.1) (2020-06-04)
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
# [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)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-p10-downloader",
"version": "0.1.0",
"version": "0.1.2",
"description": "OHIF extension for downloading DICOM P10 files",
"author": "OHIF",
"license": "MIT",
@@ -1,16 +1,16 @@
import OHIF from '@ohif/core';
import {
save,
upload,
getDicomWebClientFromContext,
getStudyInstanceUIDFromStudies,
getSOPInstanceReferenceFromActiveViewport,
getSOPInstanceReferencesFromViewports,
} from './utils';
import _downloadAndZip from './downloadAndZip';
import _downloadAndZip, { downloadInstances } from './downloadAndZip';
const {
utils: {
Queue,
},
utils: { Queue },
} = OHIF;
export function getCommands(context) {
@@ -41,6 +41,14 @@ export function getCommands(context) {
listOfUIDs
);
},
downloadAndZipStudy({ servers, studies, progress }) {
const dicomWebClient = getDicomWebClientFromContext(context, servers);
const listOfUIDs = getStudyInstanceUIDFromStudies(studies);
return save(
_downloadAndZip(dicomWebClient, listOfUIDs, { progress }),
listOfUIDs
);
},
downloadAndZipSeriesOnViewports({ servers, viewports, progress }) {
const dicomWebClient = getDicomWebClientFromContext(context, servers);
const listOfUIDs = getSOPInstanceReferencesFromViewports(viewports);
@@ -57,6 +65,28 @@ export function getCommands(context) {
listOfUIDs
);
},
downloadAndUploadStudy({ servers, studies, progress, serverConfig }) {
const dicomWebClient = getDicomWebClientFromContext(context, servers);
const listOfUIDs = getStudyInstanceUIDFromStudies(studies);
return upload(
downloadInstances(dicomWebClient, listOfUIDs, { progress }),
/**
* serverConfig is an object with the values used to create a new
* instance of DICOMwebClient.
*
* Basic Structure:
*
* const config = {
* url,
* headers,
* errorInterceptor
* }
*
* const dicomWeb = new api.DICOMwebClient(config);
*/
serverConfig
);
},
};
const definitions = {
@@ -64,6 +94,11 @@ export function getCommands(context) {
commandFn: queue.bindSafe(actions.downloadAndZip, error),
storeContexts: ['servers'],
},
downloadAndZipStudy: {
commandFn: queue.bindSafe(actions.downloadAndZipStudy, error),
storeContexts: ['servers', 'studies'],
options: { progress },
},
downloadAndZipSeriesOnViewports: {
commandFn: queue.bindSafe(actions.downloadAndZipSeriesOnViewports, error),
storeContexts: ['servers', 'viewports'],
@@ -77,6 +112,11 @@ export function getCommands(context) {
storeContexts: ['servers', 'viewports'],
options: { progress },
},
downloadAndUploadStudy: {
commandFn: queue.bindSafe(actions.downloadAndUploadStudy, error),
storeContexts: ['servers', 'studies'],
options: { progress },
},
};
return {
@@ -79,13 +79,7 @@ async function downloadAndZip(dicomWebClient, listOfUIDs, options) {
if (dicomWebClient instanceof api.DICOMwebClient) {
const settings = buildSettings(listOfUIDs, options);
const { compression } = settings.tasks;
// Register user-provided progress handler as a task list observer
progressUtils.addObserver(settings.taskList, settings.options.progress);
const buffers = await downloadAll(dicomWebClient, settings).catch(error => {
// Reject promise from compression task on download failure
compression.deferred.reject(error);
throw error;
});
const buffers = await downloadBuffers(settings, dicomWebClient);
compression.deferred.resolve(zipAll(buffers, settings));
const url = await compression.deferred.promise;
return url;
@@ -93,6 +87,27 @@ async function downloadAndZip(dicomWebClient, listOfUIDs, options) {
throw new Error('A valid DICOM Web Client instance is expected');
}
async function downloadInstances(dicomWebClient, listOfUIDs, options) {
if (dicomWebClient instanceof api.DICOMwebClient) {
const settings = buildSettings(listOfUIDs, options);
const buffers = await downloadBuffers(settings, dicomWebClient);
return buffers;
}
throw new Error('A valid DICOM Web Client instance is expected');
}
async function downloadBuffers(settings, dicomWebClient) {
const { compression } = settings.tasks;
// Register user-provided progress handler as a task list observer
progressUtils.addObserver(settings.taskList, settings.options.progress);
const buffers = await downloadAll(dicomWebClient, settings).catch(error => {
// Reject promise from compression task on download failure
compression.deferred.reject(error);
throw error;
});
return buffers;
}
/**
* Utils
*/
@@ -241,4 +256,4 @@ async function download(
* Exports
*/
export { downloadAndZip as default, downloadAndZip };
export { downloadAndZip as default, downloadAndZip, downloadInstances };
@@ -100,11 +100,59 @@ function save(promise, listOfUIDs) {
});
}
function upload(promise, serverConfig) {
return Promise.resolve(promise)
.then(async instances => {
const instancesAmount = instances.length;
OHIF.log.info(`Uploading study to ${serverConfig.url}`);
OHIF.log.info(
`${instancesAmount} instances are being uploaded. Don't close your browser.`
);
try {
const dicomWeb = new api.DICOMwebClient(serverConfig);
let progress = 0;
const getProgress = () => {
return ((progress * 100) / instancesAmount).toFixed();
};
for (const instance of instances) {
const options = {
datasets: [instance],
};
await dicomWeb.storeInstances(options);
progress++;
OHIF.log.info(`Progress: ${getProgress()}%`);
}
OHIF.log.info('Successfully uploaded!');
} catch (error) {
OHIF.log.error(`Failed to upload: ${error}`);
}
return instances;
})
.catch(error => {
OHIF.log.error(`Failed to upload: ${error}`);
return null;
});
}
function getStudyInstanceUIDFromStudies(studies) {
return Object.keys(Object(Object(studies).studyData)).slice(0, 1);
}
export {
save,
upload,
validDicomUid,
getDicomWebClientFromConfig,
getDicomWebClientFromContext,
getStudyInstanceUIDFromStudies,
getSOPInstanceReferenceFromActiveViewport,
getSOPInstanceReferencesFromViewports,
};
+82
View File
@@ -3,6 +3,88 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.4...@ohif/extension-dicom-rt@0.2.5) (2020-07-13)
### Bug Fixes
* 🐛 - Put guards in all places that a cornerstone re-render ([#1899](https://github.com/OHIF/Viewers/issues/1899)) ([451f7ea](https://github.com/OHIF/Viewers/commit/451f7eab9258e7a193eb362e0926b13aedc4b3c9))
## [0.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.3...@ohif/extension-dicom-rt@0.2.4) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.2.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.2...@ohif/extension-dicom-rt@0.2.3) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.1...@ohif/extension-dicom-rt@0.2.2) (2020-07-13)
### Bug Fixes
* 🐛 Fix RT Panel hide/show and Fix looping load errors ([#1877](https://github.com/OHIF/Viewers/issues/1877)) ([e7cc735](https://github.com/OHIF/Viewers/commit/e7cc735c03d02eeb0d3af4ba02c15ed4f81bbec2))
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.0...@ohif/extension-dicom-rt@0.2.1) (2020-06-15)
### Bug Fixes
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.4...@ohif/extension-dicom-rt@0.2.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [0.1.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.3...@ohif/extension-dicom-rt@0.1.4) (2020-05-14)
### Bug Fixes
* 🐛 Load default display set when no time metadata ([#1684](https://github.com/OHIF/Viewers/issues/1684)) ([f7b8b6a](https://github.com/OHIF/Viewers/commit/f7b8b6a41c4626084ef56b0fdf7363e914b143c4)), closes [#1683](https://github.com/OHIF/Viewers/issues/1683)
## [0.1.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.2...@ohif/extension-dicom-rt@0.1.3) (2020-05-12)
### Bug Fixes
* 🐛 Fix seg color load ([#1724](https://github.com/OHIF/Viewers/issues/1724)) ([c4f84b1](https://github.com/OHIF/Viewers/commit/c4f84b1174d04ba84d37ed89b6d7ab541be28181))
## [0.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.1...@ohif/extension-dicom-rt@0.1.2) (2020-05-04)
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-rt",
"version": "0.1.2",
"version": "0.2.5",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF",
"license": "MIT",
@@ -30,8 +30,8 @@
"peerDependencies": {
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "^4.0.9",
"dcmjs": "^0.12.3",
"cornerstone-tools": "4.15.1",
"dcmjs": "0.16.0",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
@@ -14,7 +14,7 @@ const OHIFDicomRTStructSopClassHandler = {
id: 'OHIFDicomRTStructSopClassHandler',
type: MODULE_TYPES.SOP_CLASS_HANDLER,
sopClassUIDs,
getDisplaySetFromSeries: function (
getDisplaySetFromSeries: function(
series,
study,
dicomWebClient,
@@ -67,13 +67,14 @@ const OHIFDicomRTStructSopClassHandler = {
}
}
rtStructDisplaySet.load = function (referencedDisplaySet, studies) {
rtStructDisplaySet.load = function(referencedDisplaySet, studies) {
return loadRTStruct(
rtStructDisplaySet,
referencedDisplaySet,
studies
).catch(error => {
rtStructDisplaySet.isLoaded = false;
rtStructDisplaySet.loadError = true;
throw new Error(error);
});
};
@@ -17,7 +17,10 @@ const { studyMetadataManager } = utils;
const refreshViewport = () => {
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
debugger;
if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
});
};
@@ -31,7 +34,19 @@ const refreshViewport = () => {
* @param {number} props.isOpen - isOpen
* @returns component
*/
const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }) => {
const RTPanel = ({
studies,
viewports,
activeIndex,
isOpen,
onContourItemClick,
noContoursNotification,
activeContexts = [],
contexts = {},
}) => {
const isVTK = () => activeContexts.includes(contexts.VTK);
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
const [selectedContour, setSelectedContour] = useState();
const DEFAULT_SET_INDEX = 0;
const DEFAULT_STATE = {
@@ -65,7 +80,10 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
document.addEventListener('extensiondicomrtrtloaded', updateStructureSets);
return () => {
document.removeEventListener('extensiondicomrtrtloaded', updateStructureSets);
document.removeEventListener(
'extensiondicomrtrtloaded',
updateStructureSets
);
};
}, []);
@@ -78,7 +96,9 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
activeViewport.SeriesInstanceUID
);
const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID);
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
Modality: 'RTSTRUCT',
@@ -89,7 +109,7 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
setState({
referencedDisplaysets,
selectedSet: defaultSet,
sets: viewportSets
sets: viewportSets,
});
} else {
setState(DEFAULT_STATE);
@@ -105,7 +125,10 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
setShowSettings(showSettings && !isOpen);
}, [isOpen]);
const toContourItem = ({ ROINumber, ROIName, RTROIObservations, colorArray, visible }, loadedSet) => {
const toContourItem = (
{ ROINumber, ROIName, RTROIObservations, colorArray, visible },
loadedSet
) => {
let interpretedType = '';
if (RTROIObservations && RTROIObservations.RTROIInterpretedType) {
interpretedType = `(${RTROIObservations.RTROIInterpretedType})`;
@@ -119,33 +142,47 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
onClick={() => {
setSelectedContour(isSameContour ? null : ROINumber);
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
if (isCornerstone()) {
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
if (!toolState) {
return;
if (!toolState) {
return;
}
const imageIds = toolState.data[0].imageIds;
const module = cornerstoneTools.getModule('rtstruct');
const imageId = module.getters.imageIdOfCenterFrameOfROIContour(
state.selectedSet.SeriesInstanceUID,
ROINumber,
imageIds
);
if (!imageId) {
noContoursNotification();
return;
}
const frameIndex = imageIds.indexOf(imageId);
const SOPInstanceUID = cornerstone.metaData.get(
'SOPInstanceUID',
imageId
);
const StudyInstanceUID = cornerstone.metaData.get(
'StudyInstanceUID',
imageId
);
onContourItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex,
});
}
const imageIds = toolState.data[0].imageIds;
const module = cornerstoneTools.getModule('rtstruct');
const imageId = module.getters.imageIdOfCenterFrameOfROIContour(
state.selectedSet.SeriesInstanceUID,
ROINumber,
imageIds
);
const frameIndex = imageIds.indexOf(imageId);
const SOPInstanceUID = cornerstone.metaData.get('SOPInstanceUID', imageId);
const StudyInstanceUID = cornerstone.metaData.get('StudyInstanceUID', imageId);
onContourItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex
});
}}
label={`${ROIName} ${interpretedType}`}
index={ROINumber}
@@ -153,7 +190,10 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
visible={visible}
onVisibilityChange={() => {
const module = cornerstoneTools.getModule('rtstruct');
module.setters.toggleROIContour(state.selectedSet.SeriesInstanceUID, ROINumber);
module.setters.toggleROIContour(
state.selectedSet.SeriesInstanceUID,
ROINumber
);
}}
/>
);
@@ -189,44 +229,70 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
onClick={() => setShowSettings(true)}
/>
</div>
{!state.referencedDisplaysets.length && <LoadingIndicator expand height="70px" width="70px" />}
{state.referencedDisplaysets.map(displaySet => {
const { SeriesInstanceUID, metadata, isLoaded } = displaySet;
{!state.referencedDisplaysets.length && (
<LoadingIndicator expand height="70px" width="70px" />
)}
{state.sets &&
state.referencedDisplaysets.map(displaySet => {
const { SeriesInstanceUID, metadata, isLoaded } = displaySet;
const module = cornerstoneTools.getModule('rtstruct');
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(viewports[activeIndex].SeriesInstanceUID);
const module = cornerstoneTools.getModule('rtstruct');
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
viewports[activeIndex].SeriesInstanceUID
);
const loadedSet = sets.find(set => set.SeriesInstanceUID === SeriesInstanceUID);
return (
<PanelSection
key={SeriesInstanceUID}
title={metadata.StructureSetLabel}
loading={!isLoaded || !loadedSet}
visible={isLoaded && loadedSet.visible}
hideVisibleButton={!isLoaded}
expanded={isLoaded && loadedSet.SeriesInstanceUID === state.selectedSet.SeriesInstanceUID}
onVisibilityChange={() => {
const module = cornerstoneTools.getModule('rtstruct');
module.setters.toggleStructureSet(SeriesInstanceUID);
}}
onExpandChange={async () => {
if (!isLoaded) {
await displaySet.load(viewports[activeIndex], studies);
const module = cornerstoneTools.getModule('rtstruct');
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(viewports[activeIndex].SeriesInstanceUID);
const selectedSet = sets.find(set => set.SeriesInstanceUID === SeriesInstanceUID);
setState(state => ({ ...state, selectedSet, sets }));
const loadedSet = sets.find(
set => set.SeriesInstanceUID === SeriesInstanceUID
);
return (
<PanelSection
key={SeriesInstanceUID}
title={metadata.StructureSetLabel}
loading={!isLoaded || !loadedSet}
visible={isLoaded && loadedSet.visible}
hideVisibleButton={!isLoaded}
expanded={
isLoaded &&
loadedSet.SeriesInstanceUID ===
state.selectedSet.SeriesInstanceUID
}
}}
>
<ScrollableArea>
<TableList headless>
{isLoaded && loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))}
</TableList>
</ScrollableArea>
</PanelSection>
);
})}
onVisibilityChange={newVisibility => {
const module = cornerstoneTools.getModule('rtstruct');
if (newVisibility) {
module.setters.showStructureSet(loadedSet.SeriesInstanceUID);
} else {
module.setters.hideStructureSet(loadedSet.SeriesInstanceUID);
}
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
viewports[activeIndex].SeriesInstanceUID
);
setState(state => ({ ...state, sets }));
refreshViewport();
}}
onExpandChange={async () => {
if (!isLoaded) {
await displaySet.load(viewports[activeIndex], studies);
const module = cornerstoneTools.getModule('rtstruct');
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
viewports[activeIndex].SeriesInstanceUID
);
const selectedSet = sets.find(
set => set.SeriesInstanceUID === SeriesInstanceUID
);
setState(state => ({ ...state, selectedSet, sets }));
}
}}
>
<ScrollableArea>
<TableList headless>
{isLoaded &&
loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))}
</TableList>
</ScrollableArea>
</PanelSection>
);
})}
</div>
);
};
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { TableListItem, Icon } from '@ohif/ui';
@@ -28,6 +28,11 @@ const StructureSetItem = ({
selected = false,
}) => {
const [isVisible, setIsVisible] = useState(visible);
useEffect(() => {
setIsVisible(visible);
}, [visible])
return (
<div className={`dcmrt-structure-set-item ${selected && 'selected'}`}>
<TableListItem
+21 -2
View File
@@ -20,14 +20,33 @@ export default {
preRegistration({ servicesManager, configuration = {} }) {
init({ servicesManager, configuration });
},
getPanelModule({ commandsManager }) {
getPanelModule({ commandsManager, servicesManager, api }) {
const { UINotificationService } = servicesManager.services;
const ExtendedRTPanel = props => {
const { activeContexts } = api.hooks.useAppContext();
const noContoursNotificationHandler = () => {
UINotificationService.show({
title: 'ROI Contour empty',
message: 'The ROI contour has no structure set data.',
type: 'error',
autoClose: false,
});
};
const contourItemClickHandler = contourData => {
commandsManager.runCommand('jumpToImage', contourData);
};
return (
<RTPanel {...props} onContourItemClick={contourItemClickHandler} />
<RTPanel
{...props}
onContourItemClick={contourItemClickHandler}
activeContexts={activeContexts}
contexts={api.contexts}
noContoursNotification={noContoursNotificationHandler}
/>
);
};
+4 -4
View File
@@ -16,7 +16,6 @@ export default async function loadRTStruct(
studies
) {
const rtStructModule = cornerstoneTools.getModule('rtstruct');
// Set here is loading is asynchronous.
// If this function throws its set back to false.
rtStructDisplaySet.isLoaded = true;
@@ -68,7 +67,6 @@ export default async function loadRTStruct(
const rtStructDisplayToolName = TOOL_NAMES.RTSTRUCT_DISPLAY_TOOL;
for (let i = 0; i < ROIContourSequence.length; i++) {
const ROIContour = ROIContourSequence[i];
const { ReferencedROINumber, ContourSequence } = ROIContour;
@@ -238,7 +236,7 @@ function _setROIContourRTROIObservations(
function _setToolEnabledIfNotEnabled(toolName) {
cornerstone.getEnabledElements().forEach(enabledElement => {
const { element } = enabledElement;
const { element, image } = enabledElement;
const tool = cornerstoneTools.getToolForElement(element, toolName);
if (tool.mode !== 'enabled') {
@@ -246,7 +244,9 @@ function _setToolEnabledIfNotEnabled(toolName) {
cornerstoneTools.setToolEnabled(toolName);
}
cornerstone.updateImage(element);
if (image) {
cornerstone.updateImage(element);
}
});
}
@@ -5,7 +5,9 @@ import structureSetReferencesSeriesInstanceUid from './lib/structureSetReference
// We should put this as a helper somewhere as we are using it in mutliple places.
function refreshViewport() {
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
});
}
@@ -81,7 +83,9 @@ function _setStructureSetVisible(SeriesInstanceUID, visible = true) {
const StructureSet = getStructureSet(SeriesInstanceUID);
if (StructureSet) {
StructureSet.visible = visible;
StructureSet.ROIContours.forEach(ROIContour => {
ROIContour.visible = visible;
});
refreshViewport();
}
@@ -154,15 +158,10 @@ function setToggleROIContour(SeriesInstanceUID, ROINumber) {
* Returns an array of StructureSets which reference the given SeriesInstanceUID.
* @param {string} SeriesInstanceUID The SeriesInstanceUID to check.
*/
function getStructuresSetsWhichReferenceSeriesInstanceUid(
SeriesInstanceUID
) {
function getStructuresSetsWhichReferenceSeriesInstanceUid(SeriesInstanceUID) {
const { StructureSets } = state;
return StructureSets.filter(StructureSet =>
structureSetReferencesSeriesInstanceUid(
StructureSet,
SeriesInstanceUID
)
structureSetReferencesSeriesInstanceUid(StructureSet, SeriesInstanceUID)
);
}
@@ -3,6 +3,74 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.3.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.2...@ohif/extension-dicom-segmentation@0.3.3) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.3.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.1...@ohif/extension-dicom-segmentation@0.3.2) (2020-07-13)
### Bug Fixes
* 🐛 Fix RT Panel hide/show and Fix looping load errors ([#1877](https://github.com/OHIF/Viewers/issues/1877)) ([e7cc735](https://github.com/OHIF/Viewers/commit/e7cc735c03d02eeb0d3af4ba02c15ed4f81bbec2))
## [0.3.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.0...@ohif/extension-dicom-segmentation@0.3.1) (2020-06-15)
### Bug Fixes
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
# [0.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.6...@ohif/extension-dicom-segmentation@0.3.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [0.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.5...@ohif/extension-dicom-segmentation@0.2.6) (2020-05-12)
### Bug Fixes
* 🐛 Fix seg color load ([#1724](https://github.com/OHIF/Viewers/issues/1724)) ([c4f84b1](https://github.com/OHIF/Viewers/commit/c4f84b1174d04ba84d37ed89b6d7ab541be28181))
## [0.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.4...@ohif/extension-dicom-segmentation@0.2.5) (2020-05-06)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.3...@ohif/extension-dicom-segmentation@0.2.4) (2020-05-04)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.2.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.2...@ohif/extension-dicom-segmentation@0.2.3) (2020-05-04)
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-segmentation",
"version": "0.2.3",
"version": "0.3.3",
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF",
"license": "MIT",
@@ -30,14 +30,15 @@
"peerDependencies": {
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.12.5",
"dcmjs": "^0.12.2",
"cornerstone-tools": "4.15.1",
"dcmjs": "0.16.0",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"gl-matrix": "^3.3.0",
"react-select": "^3.0.8"
}
}
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { TableListItem, Icon } from '@ohif/ui';
@@ -19,6 +19,11 @@ ColoredCircle.propTypes = {
const SegmentItem = ({ index, label, onClick, itemClass, color, visible = true, onVisibilityChange }) => {
const [isVisible, setIsVisible] = useState(visible);
useEffect(() => {
setIsVisible(visible);
}, [visible]);
return (
<div className="dcmseg-segment-item">
<TableListItem
@@ -8,6 +8,11 @@
flex-direction: column;
}
.dcmseg-segmentation-panel.disabled {
opacity: 0.6;
pointer-events: none;
}
.dcmseg-segmentation-panel h3 {
padding-left: 20px;
margin-top: 0;
@@ -32,7 +32,11 @@ const refreshViewport = () => {
* @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
* @param {Function} props.onSegmentItemClick - Segment click handler
* @param {Function} props.onSegmentVisibilityChange - Segment visibiliy change handler
* @param {Function} props.onConfigurationChange - Configuration change handler
* @param {Function} props.activeContexts - List of active application contexts
* @param {Function} props.contexts - List of available application contexts
* @returns component
*/
const SegmentationPanel = ({
@@ -40,25 +44,41 @@ const SegmentationPanel = ({
viewports,
activeIndex,
isOpen,
onSegItemClick,
UINotificationService,
onSegmentItemClick,
onSegmentVisibilityChange,
onConfigurationChange,
onDisplaySetLoadFailure,
onSelectedSegmentationChange,
activeContexts = [],
contexts = {},
}) => {
const isVTK = () => activeContexts.includes(contexts.VTK);
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
/*
* TODO: wrap get/set interactions with the cornerstoneTools
* store with context to make these kind of things less blurry.
*/
const { configuration } = cornerstoneTools.getModule('segmentation');
const DEFAULT_BRUSH_RADIUS = configuration.radius || 10;
/*
* TODO: We shouldn't hardcode brushColor color, in the future
* the SEG may set the colorLUT to whatever it wants.
*/
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. */,
'rgba(221, 85, 85, 1)',
selectedSegment: null,
selectedSegmentation: null,
showSegSettings: false,
showSegmentationSettings: false,
brushStackState: null,
labelmapList: [],
segmentList: [],
cachedSegmentsProperties: [],
isLoading: false,
isDisabled: true
});
useEffect(() => {
@@ -75,6 +95,25 @@ const SegmentationPanel = ({
updateState('brushStackState', module.state.series[firstImageId]);
};
/*
* TODO: Improve the way we notify parts of the app that depends on segs to be loaded.
*
* Currently we are using a non-ideal implementation through a custom event to notify the segmentation panel
* or other components that could rely on loaded segmentations that
* the segments were loaded so that e.g. when the user opens the panel
* before the segments are fully loaded, the panel can subscribe to this custom event
* and update itself with the new segments.
*
* This limitation is due to the fact that the cs segmentation module is an object (which will be
* updated after the segments are loaded) that React its not aware of its changes
* because the module object its not passed in to the panel component as prop but accessed externally.
*
* Improving this event approach to something reactive that can be tracked inside the react lifecycle,
* allows us to easily watch the module or the segmentations loading process in any other component
* without subscribing to external events.
*/
document.addEventListener('extensiondicomsegmentationsegloaded', refreshSegmentations);
/*
* These are specific to each element;
* Need to iterate cornerstone-tools tracked enabled elements?
@@ -88,6 +127,7 @@ const SegmentationPanel = ({
);
return () => {
document.removeEventListener('extensiondicomsegmentationsegloaded', refreshSegmentations);
cornerstoneTools.store.state.enabledElements.forEach(enabledElement =>
enabledElement.removeEventListener(
'cornerstonetoolslabelmapmodified',
@@ -95,60 +135,66 @@ const SegmentationPanel = ({
)
);
};
});
}, [activeIndex, viewports]);
useEffect(() => {
const refreshSegmentations = useCallback(() => {
const module = cornerstoneTools.getModule('segmentation');
const activeViewport = viewports[activeIndex];
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 isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
if (!isDisabled) {
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const segmentList = getSegmentList(
labelmap3D,
firstImageId,
brushStackState
const firstImageId = studyMetadata.getFirstImageId(
activeViewport.displaySetInstanceUID
);
setState(state => ({
...state,
brushStackState,
selectedSegmentation: brushStackState.activeLabelmapIndex,
labelmapList,
segmentList,
}));
} else {
setState(state => ({
...state,
labelmapList: [],
segmentList: [],
}));
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,
brushStackState
);
setState(state => ({
...state,
brushStackState,
selectedSegmentation: brushStackState.activeLabelmapIndex,
labelmapList,
segmentList,
isDisabled
}));
} else {
setState(state => ({
...state,
labelmapList: [],
segmentList: [],
isDisabled
}));
}
}
}, [
studies,
viewports,
activeIndex,
getLabelmapList,
getSegmentList,
state.selectedSegmentation,
state.isLoading
]);
useEffect(() => {
refreshSegmentations();
}, [viewports, activeIndex, isOpen, state.selectedSegmentation, activeContexts, state.isLoading]);
/* Handle open/closed panel behaviour */
useEffect(() => {
setState(state => ({
...state,
showSegSettings: state.showSegSettings && !isOpen,
showSegmentationSettings: state.showSegmentationSettings && !isOpen,
}));
}, [isOpen]);
@@ -183,7 +229,8 @@ const SegmentationPanel = ({
displaySet,
firstImageId,
brushStackState.activeLabelmapIndex,
UINotificationService
() => onSelectedSegmentationChange(),
onDisplaySetLoadFailure
);
updateState('selectedSegmentation', activatedLabelmapIndex);
},
@@ -262,65 +309,104 @@ const SegmentationPanel = ({
: prev;
});
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
if (isCornerstone()) {
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
if (!toolState) {
return;
if (!toolState) {
return;
}
const imageIds = toolState.data[0].imageIds;
const imageId = imageIds[closest];
const frameIndex = imageIds.indexOf(imageId);
const SOPInstanceUID = cornerstone.metaData.get(
'SOPInstanceUID',
imageId
);
const StudyInstanceUID = cornerstone.metaData.get(
'StudyInstanceUID',
imageId
);
onSegmentItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex,
});
}
const imageIds = toolState.data[0].imageIds;
const imageId = imageIds[closest];
const frameIndex = imageIds.indexOf(imageId);
if (isVTK()) {
const activeViewport = viewports[activeIndex];
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const allDisplaySets = studyMetadata.getDisplaySets();
const currentDisplaySet = allDisplaySets.find(
displaySet =>
displaySet.displaySetInstanceUID ===
activeViewport.displaySetInstanceUID
);
const SOPInstanceUID = cornerstone.metaData.get(
'SOPInstanceUID',
imageId
);
const StudyInstanceUID = cornerstone.metaData.get(
'StudyInstanceUID',
imageId
);
const frame = labelmap3D.labelmaps2D[closest];
onSegItemClick({
StudyInstanceUID,
SOPInstanceUID,
frameIndex,
activeViewportIndex: activeIndex,
});
onSegmentItemClick({
studies,
StudyInstanceUID: currentDisplaySet.StudyInstanceUID,
displaySetInstanceUID: currentDisplaySet.displaySetInstanceUID,
SOPClassUID: viewports[activeIndex].sopClassUIDs[0],
SOPInstanceUID: currentDisplaySet.SOPInstanceUID,
segmentNumber,
frameIndex: closest,
frame,
});
}
};
const enabledElements = cornerstone.getEnabledElements();
const enabledElementViewport = enabledElements[activeIndex];
const isSegmentVisible = () => {
return !labelmap3D.segmentsHidden[segmentIndex];
};
let isVisible = true;
if (enabledElementViewport) {
const element = enabledElementViewport.element;
const module = cornerstoneTools.getModule('segmentation');
isVisible = module.getters.isSegmentVisible(
element,
segmentNumber,
brushStackState.activeLabelmapIndex
);
const toggleSegmentVisibility = () => {
const segmentsHidden = labelmap3D.segmentsHidden;
segmentsHidden[segmentIndex] = !segmentsHidden[segmentIndex];
return !segmentsHidden[segmentIndex];
};
const cachedSegmentProperties = state.cachedSegmentsProperties[segmentNumber];
let visible = isSegmentVisible();
if (cachedSegmentProperties && cachedSegmentProperties.visible !== visible) {
toggleSegmentVisibility();
}
segmentList.push(
<SegmentItem
key={segmentNumber}
itemClass={`segment-item ${sameSegment && 'selected'}`}
onClick={setCurrentSelectedSegment}
onClick={() => setCurrentSelectedSegment()}
label={segmentLabel}
index={segmentNumber}
color={color}
visible={isVisible}
onVisibilityChange={() => {
const element = enabledElements[activeIndex].element;
module.setters.toggleSegmentVisibility(
element,
segmentNumber,
brushStackState.activeLabelmapIndex
);
visible={visible}
onVisibilityChange={newVisibility => {
if (isCornerstone()) {
const enabledElements = cornerstone.getEnabledElements();
const element = enabledElements[activeIndex].element;
module.setters.toggleSegmentVisibility(
element,
segmentNumber,
brushStackState.activeLabelmapIndex
);
}
if (isVTK()) {
onSegmentVisibilityChange(segmentNumber, newVisibility);
}
updateCachedSegmentsProperties(segmentNumber, { visible: newVisibility });
refreshViewport();
}}
/>
@@ -336,9 +422,25 @@ const SegmentationPanel = ({
* Show default name
*/
},
[activeIndex, onSegItemClick, state.selectedSegment]
[activeIndex, onSegmentItemClick, state.selectedSegment, state.isLoading]
);
const updateCachedSegmentsProperties = (segmentNumber, properties) => {
const segmentsProperties = state.cachedSegmentsProperties;
const segmentProperties = state.cachedSegmentsProperties[segmentNumber];
segmentsProperties[segmentNumber] =
segmentProperties ?
{ ...segmentProperties, ...properties } :
properties;
updateState('cachedSegmentsProperties', segmentsProperties);
};
useEffect(() => {
updateState('cachedSegmentsProperties', []);
}, [activeContexts]);
const updateState = (field, value) => {
setState(state => ({ ...state, [field]: value }));
};
@@ -387,7 +489,6 @@ const SegmentationPanel = ({
};
const updateConfiguration = newConfiguration => {
/* Supported configuration */
configuration.renderFill = newConfiguration.renderFill;
configuration.renderOutline = newConfiguration.renderOutline;
configuration.shouldRenderInactiveLabelmaps =
@@ -397,26 +498,29 @@ const SegmentationPanel = ({
configuration.outlineWidth = newConfiguration.outlineWidth;
configuration.fillAlphaInactive = newConfiguration.fillAlphaInactive;
configuration.outlineAlphaInactive = newConfiguration.outlineAlphaInactive;
onConfigurationChange(newConfiguration);
refreshViewport();
};
if (state.showSegSettings) {
const disabledConfigurationFields = ['outlineAlpha', 'shouldRenderInactiveLabelmaps'];
if (state.showSegmentationSettings) {
return (
<SegmentationSettings
disabledFields={isVTK() ? disabledConfigurationFields : []}
configuration={configuration}
onBack={() => updateState('showSegSettings', false)}
onBack={() => updateState('showSegmentationSettings', false)}
onChange={updateConfiguration}
/>
);
} else {
return (
<div className="dcmseg-segmentation-panel">
<div className={`dcmseg-segmentation-panel ${state.isDisabled && 'disabled'}`}>
<Icon
className="cog-icon"
name="cog"
width="25px"
height="25px"
onClick={() => updateState('showSegSettings', true)}
onClick={() => updateState('showSegmentationSettings', true)}
/>
{false && (
<form className="selector-form">
@@ -525,7 +629,8 @@ const _setActiveLabelmap = async (
displaySet,
firstImageId,
activeLabelmapIndex,
UINotificationService
callback = () => { },
onDisplaySetLoadFailure
) => {
if (displaySet.labelmapIndex === activeLabelmapIndex) {
log.warn(`${activeLabelmapIndex} is already the active labelmap`);
@@ -539,12 +644,7 @@ const _setActiveLabelmap = async (
const loadPromise = displaySet.load(viewportSpecificData, studies);
loadPromise.catch(error => {
UINotificationService.show({
title: 'DICOM Segmentation Loader',
message: error.message,
type: 'error',
autoClose: false,
});
onDisplaySetLoadFailure(error);
// Return old index.
return activeLabelmapIndex;
@@ -559,6 +659,8 @@ const _setActiveLabelmap = async (
refreshViewport();
callback();
return displaySet.labelmapIndex;
};
@@ -4,7 +4,7 @@ import { Range } from '@ohif/ui';
import './SegmentationSettings.css';
const SegmentationSettings = ({ configuration, onBack, onChange }) => {
const SegmentationSettings = ({ configuration, onBack, onChange, disabledFields = [] }) => {
const [state, setState] = useState({
renderFill: configuration.renderFill,
renderOutline: configuration.renderOutline,
@@ -70,28 +70,32 @@ const SegmentationSettings = ({ configuration, onBack, onChange }) => {
/>
{state.renderOutline && (
<>
<CustomRange
value={state.outlineAlpha * 100}
label="Opacity"
showPercentage
step={1}
min={0}
max={100}
onChange={event => save('outlineAlpha', toFloat(event.target.value))}
/>
<CustomRange
value={state.outlineWidth}
label="Width"
showValue
step={1}
min={0}
max={5}
onChange={event => save('outlineWidth', parseInt(event.target.value))}
/>
{!disabledFields.includes('outlineAlpha') && (
<CustomRange
value={state.outlineAlpha * 100}
label="Opacity"
showPercentage
step={1}
min={0}
max={100}
onChange={event => save('outlineAlpha', toFloat(event.target.value))}
/>
)}
{!disabledFields.includes('outlineWidth') && (
<CustomRange
value={state.outlineWidth}
label="Width"
showValue
step={1}
min={0}
max={5}
onChange={event => save('outlineWidth', parseInt(event.target.value))}
/>
)}
</>
)}
</div>
{(state.renderFill || state.renderOutline) && (
{(state.renderFill || state.renderOutline) && !disabledFields.includes('shouldRenderInactiveLabelmaps') && (
<div
className="settings-group"
style={{ marginBottom: state.shouldRenderInactiveLabelmaps ? 15 : 0 }}
@@ -103,7 +107,7 @@ const SegmentationSettings = ({ configuration, onBack, onChange }) => {
/>
{state.shouldRenderInactiveLabelmaps && (
<>
{state.renderFill && (
{state.renderFill && !disabledFields.includes('fillAlphaInactive') && (
<CustomRange
label="Fill Opacity"
showPercentage
@@ -114,7 +118,7 @@ const SegmentationSettings = ({ configuration, onBack, onChange }) => {
onChange={event => save('fillAlphaInactive', toFloat(event.target.value))}
/>
)}
{state.renderOutline && (
{state.renderOutline && !disabledFields.includes('outlineAlphaInactive') && (
<CustomRange
label="Outline Opacity"
showPercentage
+43 -6
View File
@@ -23,19 +23,56 @@ export default {
getToolbarModule({ servicesManager }) {
return toolbarModule;
},
getPanelModule({ commandsManager, servicesManager }) {
getPanelModule({ commandsManager, api, servicesManager }) {
const { UINotificationService } = servicesManager.services;
const ExtendedSegmentationPanel = props => {
const segItemClickHandler = segData => {
commandsManager.runCommand('jumpToImage', segData);
const { activeContexts } = api.hooks.useAppContext();
const onDisplaySetLoadFailureHandler = error => {
UINotificationService.show({
title: 'DICOM Segmentation Loader',
message: error.message,
type: 'error',
autoClose: false,
});
};
const { UINotificationService } = servicesManager.services;
const segmentItemClickHandler = data => {
commandsManager.runCommand('jumpToImage', data);
commandsManager.runCommand('jumpToSlice', data);
};
const onSegmentVisibilityChangeHandler = (segmentNumber, visible) => {
commandsManager.runCommand('setSegmentConfiguration', {
segmentNumber,
visible,
});
};
const onConfigurationChangeHandler = configuration => {
commandsManager.runCommand('setSegmentationConfiguration', {
globalOpacity: configuration.fillAlpha,
outlineThickness: configuration.outlineWidth,
renderOutline: configuration.renderOutline,
visible: configuration.renderFill,
});
};
const onSelectedSegmentationChangeHandler = () => {
commandsManager.runCommand('requestNewSegmentation');
};
return (
<SegmentationPanel
{...props}
onSegItemClick={segItemClickHandler}
UINotificationService={UINotificationService}
activeContexts={activeContexts}
contexts={api.contexts}
onSegmentItemClick={segmentItemClickHandler}
onSegmentVisibilityChange={onSegmentVisibilityChangeHandler}
onConfigurationChange={onConfigurationChangeHandler}
onSelectedSegmentationChange={onSelectedSegmentationChangeHandler}
onDisplaySetLoadFailure={onDisplaySetLoadFailureHandler}
/>
);
};
@@ -41,6 +41,7 @@ export default async function loadSegmentation(
results = _parseSeg(segArrayBuffer, imageIds);
} catch (error) {
segDisplaySet.isLoaded = false;
segDisplaySet.loadError = true;
reject(error);
}
@@ -49,6 +50,7 @@ export default async function loadSegmentation(
// TODO: Could define a color LUT based on colors in the SEG.
const labelmapIndex = _getNextLabelmapIndex(imageIds[0]);
const colorLUTIndex = _makeColorLUTAndGetIndex(segMetadata);
setters.labelmap3DByFirstImageId(
imageIds[0],
@@ -56,11 +58,33 @@ export default async function loadSegmentation(
labelmapIndex,
segMetadata,
imageIds.length,
segmentsOnFrame
segmentsOnFrame,
colorLUTIndex
);
segDisplaySet.labelmapIndex = labelmapIndex;
/*
* TODO: Improve the way we notify parts of the app that depends on segs to be loaded.
*
* Currently we are using a non-ideal implementation through a custom event to notify the segmentation panel
* or other components that could rely on loaded segmentations that
* the segments were loaded so that e.g. when the user opens the panel
* before the segments are fully loaded, the panel can subscribe to this custom event
* and update itself with the new segments.
*
* This limitation is due to the fact that the cs segmentation module is an object (which will be
* updated after the segments are loaded) that React its not aware of its changes
* because the module object its not passed in to the panel component as prop but accessed externally.
*
* Improving this event approach to something reactive that can be tracked inside the react lifecycle,
* allows us to easily watch the module or the segmentations loading process in any other component
* without subscribing to external events.
*/
console.log('Segmentation loaded.');
const event = new CustomEvent('extensiondicomsegmentationsegloaded');
document.dispatchEvent(event);
resolve(labelmapIndex);
});
}
@@ -86,6 +110,69 @@ function _getNextLabelmapIndex(firstImageId) {
return labelmapIndex;
}
function _makeColorLUTAndGetIndex(segMetadata) {
const { setters, state } = cornerstoneTools.getModule('segmentation');
const { colorLutTables } = state;
const colorLUTIndex = _getNextColorLUTIndex();
const { data } = segMetadata;
if (
!data.some(
segment =>
segment &&
(segment.ROIDisplayColor || segment.RecommendedDisplayCIELabValue)
)
) {
// Use default cornerstoneTools colorLUT.
return 0;
}
const colorLUT = [];
for (let i = 0; i < data.length; i++) {
const segment = data[i];
if (!segment) {
continue;
}
const { ROIDisplayColor, RecommendedDisplayCIELabValue } = segment;
if (RecommendedDisplayCIELabValue) {
const rgb = dcmjs.data.Colors.dicomlab2RGB(
RecommendedDisplayCIELabValue
).map(x => Math.round(x * 255));
colorLUT[i] = [...rgb, 255];
} else if (ROIDisplayColor) {
colorLUT[i] = [...ROIDisplayColor, 255];
} else {
colorLUT[i] = [...colorLutTables[0][i]];
}
}
colorLUT.shift();
setters.colorLUT(colorLUTIndex, colorLUT);
return colorLUTIndex;
}
function _getNextColorLUTIndex() {
const { state } = cornerstoneTools.getModule('segmentation');
const { colorLutTables } = state;
let colorLUTIndex = colorLutTables.length;
for (let i = 0; i < colorLutTables.length; i++) {
if (!colorLutTables[i]) {
colorLUTIndex = i;
break;
}
}
return colorLUTIndex;
}
function _parseSeg(arrayBuffer, imageIds) {
return dcmjs.adapters.Cornerstone.Segmentation.generateToolState(
imageIds,
+105
View File
@@ -3,6 +3,111 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.6.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.7...@ohif/extension-vtk@1.6.8) (2020-07-22)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.6.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.6...@ohif/extension-vtk@1.6.7) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.6.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.5...@ohif/extension-vtk@1.6.6) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.6.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.4...@ohif/extension-vtk@1.6.5) (2020-07-13)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.6.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.3...@ohif/extension-vtk@1.6.4) (2020-06-18)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.6.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.2...@ohif/extension-vtk@1.6.3) (2020-06-15)
### Bug Fixes
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
## [1.6.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.1...@ohif/extension-vtk@1.6.2) (2020-06-05)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.0...@ohif/extension-vtk@1.6.1) (2020-06-04)
**Note:** Version bump only for package @ohif/extension-vtk
# [1.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.5.6...@ohif/extension-vtk@1.6.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [1.5.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.5.5...@ohif/extension-vtk@1.5.6) (2020-05-14)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.5.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.5.4...@ohif/extension-vtk@1.5.5) (2020-05-12)
### Bug Fixes
* 🐛 Fix seg color load ([#1724](https://github.com/OHIF/Viewers/issues/1724)) ([c4f84b1](https://github.com/OHIF/Viewers/commit/c4f84b1174d04ba84d37ed89b6d7ab541be28181))
## [1.5.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.5.3...@ohif/extension-vtk@1.5.4) (2020-05-06)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.5.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.5.2...@ohif/extension-vtk@1.5.3) (2020-05-04)
**Note:** Version bump only for package @ohif/extension-vtk
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-vtk",
"version": "1.5.3",
"version": "1.6.8",
"description": "OHIF extension for VTK.js",
"author": "OHIF",
"license": "MIT",
@@ -33,9 +33,9 @@
"@ohif/i18n": "^0.50.0",
"@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.12.5",
"cornerstone-tools": "4.15.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2",
"dcmjs": "0.16.0",
"dicom-parser": "^1.8.3",
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
@@ -50,12 +50,12 @@
"dependencies": {
"@babel/runtime": "^7.5.5",
"lodash.throttle": "^4.1.1",
"react-vtkjs-viewport": "^0.8.3"
"react-vtkjs-viewport": "^0.9.0"
},
"devDependencies": {
"@ohif/core": "^2.9.3",
"@ohif/ui": "^1.4.4",
"cornerstone-tools": "4.12.5",
"@ohif/core": "^2.9.13",
"@ohif/ui": "^1.5.2",
"cornerstone-tools": "4.15.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3",
"gh-pages": "^2.0.1",
+9 -9
View File
@@ -32,16 +32,16 @@ class LoadingIndicator extends PureComponent {
</div>
</div>
) : (
<div className="imageViewerLoadingIndicator loadingIndicator">
<div className="indicatorContents">
<p>
{this.props.t('Reformatting')}...
<i className="fa fa-spin fa-circle-o-notch fa-fw" />
{percComplete}
</p>
<div className="imageViewerLoadingIndicator loadingIndicator">
<div className="indicatorContents">
<p>
{this.props.t('Loading...')}
<i className="fa fa-spin fa-circle-o-notch fa-fw" />
{percComplete}
</p>
</div>
</div>
</div>
)}
)}
</React.Fragment>
);
}
+16 -12
View File
@@ -49,7 +49,7 @@ class OHIFVTKViewport extends Component {
state = {
volumes: null,
paintFilterLabelMapImageData: null,
paintFilterBackgroundImageData: null,
paintFilterBackgroundImageData: null
};
static propTypes = {
@@ -69,7 +69,7 @@ class OHIFVTKViewport extends Component {
};
static defaultProps = {
onScroll: () => {},
onScroll: () => { },
};
static id = 'OHIFVTKViewport';
@@ -156,6 +156,10 @@ class OHIFVTKViewport extends Component {
const { activeLabelmapIndex } = brushStackState;
const labelmap3D = brushStackState.labelmaps3D[activeLabelmapIndex];
this.segmentsDefaultProperties = labelmap3D.segmentsHidden.map(isHidden => {
return { visible: !isHidden };
});
const vtkLabelmapID = `${firstImageId}_${activeLabelmapIndex}`;
if (labelmapCache[vtkLabelmapID]) {
@@ -339,13 +343,13 @@ class OHIFVTKViewport extends Component {
this.setStateFromProps();
}
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps, prevState) {
const { displaySet } = this.props.viewportData;
const prevDisplaySet = prevProps.viewportData.displaySet;
if (
displaySet.displaySetInstanceUID !==
prevDisplaySet.displaySetInstanceUID ||
prevDisplaySet.displaySetInstanceUID ||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
displaySet.frameIndex !== prevDisplaySet.frameIndex
) {
@@ -405,10 +409,6 @@ class OHIFVTKViewport extends Component {
const style = { width: '100%', height: '100%', position: 'relative' };
const visible = configuration.renderFill || configuration.renderOutline;
const opacity = configuration.fillAlpha;
const outlineThickness = configuration.outlineThickness;
return (
<>
<div style={style}>
@@ -428,10 +428,14 @@ class OHIFVTKViewport extends Component {
dataDetails={this.state.dataDetails}
labelmapRenderingOptions={{
colorLUT: this.state.labelmapColorLUT,
globalOpacity: opacity,
visible,
outlineThickness,
renderOutline: true,
globalOpacity: configuration.fillAlpha,
visible: configuration.renderFill,
outlineThickness: configuration.outlineWidth,
renderOutline: configuration.renderOutline,
segmentsDefaultProperties: this.segmentsDefaultProperties,
onNewSegmentationRequested: () => {
this.setStateFromProps();
}
}}
onScroll={this.props.onScroll}
/>
+151 -2
View File
@@ -5,10 +5,13 @@ import {
vtkInteractorStyleMPRRotate,
vtkSVGCrosshairsWidget,
} from 'react-vtkjs-viewport';
import { getImageData } from 'react-vtkjs-viewport';
import { vec3 } from 'gl-matrix';
import setMPRLayout from './utils/setMPRLayout.js';
import setViewportToVTK from './utils/setViewportToVTK.js';
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
import OHIFVTKViewport from './OHIFVTKViewport';
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
const { BlendMode } = Constants;
@@ -34,7 +37,6 @@ const commandsModule = ({ commandsManager }) => {
}
const displaySet = viewportSpecificData[activeViewportIndex];
let api;
if (!api) {
try {
@@ -99,6 +101,20 @@ const commandsModule = ({ commandsManager }) => {
});
}
const _convertModelToWorldSpace = (position, vtkImageData) => {
const indexToWorld = vtkImageData.getIndexToWorld();
const pos = vec3.create();
position[0] += 0.5; /* Move to the centre of the voxel. */
position[1] += 0.5; /* Move to the centre of the voxel. */
position[2] += 0.5; /* Move to the centre of the voxel. */
vec3.set(pos, position[0], position[1], position[2]);
vec3.transformMat4(pos, pos, indexToWorld);
return pos;
};
const actions = {
getVtkApis: ({ index }) => {
return apis[index];
@@ -124,6 +140,119 @@ const commandsModule = ({ commandsManager }) => {
_setView(api, [0, 1, 0], [0, 0, 1]);
},
requestNewSegmentation: async ({ viewports }) => {
const allViewports = Object.values(viewports.viewportSpecificData);
const promises = allViewports.map(async (viewport, viewportIndex) => {
let api = apis[viewportIndex];
if (!api) {
api = await _getActiveViewportVTKApi(viewports);
apis[viewportIndex] = api;
}
api.requestNewSegmentation();
api.updateImage();
});
await Promise.all(promises);
},
jumpToSlice: async ({
viewports,
studies,
StudyInstanceUID,
displaySetInstanceUID,
SOPClassUID,
SOPInstanceUID,
segmentNumber,
frameIndex,
frame,
done = () => { }
}) => {
let api = apis[viewports.activeViewportIndex];
if (!api) {
api = await _getActiveViewportVTKApi(viewports);
apis[viewports.activeViewportIndex] = api;
}
const stack = OHIFVTKViewport.getCornerstoneStack(
studies,
StudyInstanceUID,
displaySetInstanceUID,
SOPClassUID,
SOPInstanceUID,
frameIndex,
);
const imageDataObject = getImageData(stack.imageIds, displaySetInstanceUID);
let pixelIndex = 0;
let x = 0;
let y = 0;
let count = 0;
const rows = imageDataObject.dimensions[1];
const cols = imageDataObject.dimensions[0];
for (let j = 0; j < rows; j++) {
for (let i = 0; i < cols; i++) {
// [i, j] =
const pixel = frame.pixelData[pixelIndex];
if (pixel === segmentNumber) {
x += i;
y += j;
count++;
}
pixelIndex++;
}
}
x /= count;
y /= count;
const position = [x, y, frameIndex];
const worldPos = _convertModelToWorldSpace(position, imageDataObject.vtkImageData);
api.svgWidgets.crosshairsWidget.moveCrosshairs(worldPos, apis);
done();
},
setSegmentationConfiguration: async ({
viewports,
globalOpacity,
visible,
renderOutline,
outlineThickness,
}) => {
const allViewports = Object.values(viewports.viewportSpecificData);
const promises = allViewports.map(async (viewport, viewportIndex) => {
let api = apis[viewportIndex];
if (!api) {
api = await _getActiveViewportVTKApi(viewports);
apis[viewportIndex] = api;
}
api.setGlobalOpacity(globalOpacity);
api.setVisibility(visible);
api.setOutlineThickness(outlineThickness);
api.setOutlineRendering(renderOutline);
api.updateImage();
});
await Promise.all(promises);
},
setSegmentConfiguration: async ({ viewports, visible, segmentNumber }) => {
const allViewports = Object.values(viewports.viewportSpecificData);
const promises = allViewports.map(async (viewport, viewportIndex) => {
let api = apis[viewportIndex];
if (!api) {
api = await _getActiveViewportVTKApi(viewports);
apis[viewportIndex] = api;
}
api.setSegmentVisibility(segmentNumber, visible);
api.updateImage();
});
await Promise.all(promises);
},
enableRotateTool: () => {
apis.forEach(api => {
const istyle = vtkInteractorStyleMPRRotate.newInstance();
@@ -280,6 +409,26 @@ const commandsModule = ({ commandsManager }) => {
window.vtkActions = actions;
const definitions = {
requestNewSegmentation: {
commandFn: actions.requestNewSegmentation,
storeContexts: ['viewports'],
options: {},
},
jumpToSlice: {
commandFn: actions.jumpToSlice,
storeContexts: ['viewports'],
options: {},
},
setSegmentationConfiguration: {
commandFn: actions.setSegmentationConfiguration,
storeContexts: ['viewports'],
options: {},
},
setSegmentConfiguration: {
commandFn: actions.setSegmentConfiguration,
storeContexts: ['viewports'],
options: {},
},
axial: {
commandFn: actions.axial,
storeContexts: ['viewports'],
+3 -1
View File
@@ -1,3 +1,4 @@
import React from 'react';
import asyncComponent from './asyncComponent.js';
import commandsModule from './commandsModule.js';
import toolbarModule from './toolbarModule.js';
@@ -16,7 +17,8 @@ const vtkExtension = {
id: 'vtk',
getViewportModule({ commandsManager }) {
return withCommandsManager(OHIFVTKViewport, commandsManager);
const ExtendedVTKViewport = props => <OHIFVTKViewport {...props} />;
return withCommandsManager(ExtendedVTKViewport, commandsManager);
},
getToolbarModule() {
return toolbarModule;
+98
View File
@@ -3,6 +3,104 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.9.13](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.12...@ohif/core@2.9.13) (2020-07-22)
### Bug Fixes
* Switch DICOMFileUploader to use the UIModalService ([#1904](https://github.com/OHIF/Viewers/issues/1904)) ([7772fee](https://github.com/OHIF/Viewers/commit/7772fee21ae6a65994e1251e2f1d2554b47781be))
## [2.9.12](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.11...@ohif/core@2.9.12) (2020-07-13)
### Bug Fixes
* 🐛 - Put guards in all places that a cornerstone re-render ([#1899](https://github.com/OHIF/Viewers/issues/1899)) ([451f7ea](https://github.com/OHIF/Viewers/commit/451f7eab9258e7a193eb362e0926b13aedc4b3c9))
## [2.9.11](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.10...@ohif/core@2.9.11) (2020-07-13)
**Note:** Version bump only for package @ohif/core
## [2.9.10](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.9...@ohif/core@2.9.10) (2020-07-13)
### Bug Fixes
* 🐛 Fix RT Panel hide/show and Fix looping load errors ([#1877](https://github.com/OHIF/Viewers/issues/1877)) ([e7cc735](https://github.com/OHIF/Viewers/commit/e7cc735c03d02eeb0d3af4ba02c15ed4f81bbec2))
## [2.9.9](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.8...@ohif/core@2.9.9) (2020-06-18)
**Note:** Version bump only for package @ohif/core
## [2.9.8](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.7...@ohif/core@2.9.8) (2020-06-15)
### Bug Fixes
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
## [2.9.7](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.6...@ohif/core@2.9.7) (2020-06-04)
**Note:** Version bump only for package @ohif/core
## [2.9.6](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.5...@ohif/core@2.9.6) (2020-05-14)
### Bug Fixes
* 🐛 Load default display set when no time metadata ([#1684](https://github.com/OHIF/Viewers/issues/1684)) ([f7b8b6a](https://github.com/OHIF/Viewers/commit/f7b8b6a41c4626084ef56b0fdf7363e914b143c4)), closes [#1683](https://github.com/OHIF/Viewers/issues/1683)
## [2.9.5](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.4...@ohif/core@2.9.5) (2020-05-12)
### Bug Fixes
* 🐛 Fix seg color load ([#1724](https://github.com/OHIF/Viewers/issues/1724)) ([c4f84b1](https://github.com/OHIF/Viewers/commit/c4f84b1174d04ba84d37ed89b6d7ab541be28181))
## [2.9.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.3...@ohif/core@2.9.4) (2020-05-06)
**Note:** Version bump only for package @ohif/core
## [2.9.3](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.2...@ohif/core@2.9.3) (2020-05-04)
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/core",
"version": "2.9.3",
"version": "2.9.13",
"description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team",
"license": "MIT",
@@ -32,14 +32,14 @@
},
"peerDependencies": {
"cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.12.5",
"cornerstone-tools": "4.15.1",
"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.2",
"dcmjs": "0.16.0",
"dicomweb-client": "^0.6.0",
"immer": "6.0.2",
"isomorphic-base64": "^1.0.2",
@@ -13,7 +13,7 @@ const findMostRecentStructuredReport = studies => {
// Skip series that may not have instances yet
// This can happen if we have retrieved just the initial
// details about the series via QIDO-RS, but not the full metadata
if (!series.instances.length) {
if (!series.instances || !series.instances.length) {
return;
}
@@ -135,6 +135,7 @@ export class StudyMetadata extends Metadata {
study,
sopClassUIDs
);
if (displaySet) {
displaySet.sopClassModule = true;
@@ -215,6 +216,14 @@ export class StudyMetadata extends Metadata {
// TODO -> Get x Modality by referencedSeriesInstanceUid, FoR, etc.
}
/**
* Adds the displaySets to the studies list of derived displaySets.
* @param {array} displaySets The displaySets array to append to the derived displaysets list.
*/
_addDerivedDisplaySets(displaySets) {
displaySets.map(displaySet => this._derivedDisplaySets.push(displaySet));
}
/**
* Returns a list of derived datasets in the study, filtered by the given filter.
* @param {object} filter An object containing search filters
@@ -263,7 +272,7 @@ export class StudyMetadata extends Metadata {
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
displaySet =>
displaySet.ReferencedFrameOfReferenceUID ===
ReferencedFrameOfReferenceUID
referencedFrameOfReferenceUID
);
}
@@ -2,7 +2,7 @@ import MODULE_TYPES from './MODULE_TYPES.js';
import log from './../log.js';
export default class ExtensionManager {
constructor({ commandsManager, servicesManager, appConfig = {} }) {
constructor({ commandsManager, servicesManager, api, appConfig = {} }) {
this.modules = {};
this.registeredExtensionIds = [];
this.moduleTypeNames = Object.values(MODULE_TYPES);
@@ -10,6 +10,7 @@ export default class ExtensionManager {
this._commandsManager = commandsManager;
this._servicesManager = servicesManager;
this._appConfig = appConfig;
this._api = api;
this.moduleTypeNames.forEach(moduleType => {
this.modules[moduleType] = [];
@@ -119,6 +120,7 @@ export default class ExtensionManager {
commandsManager: this._commandsManager,
appConfig: this._appConfig,
configuration,
api: this._api
});
if (!extensionModule) {
@@ -251,7 +251,9 @@ export default class MeasurementApi {
this.syncMeasurementsAndToolData();
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
});
// Let others know that the measurements are updated
@@ -0,0 +1,9 @@
import cornerstone from 'cornerstone-core';
export default function refreshCornerstoneViewports() {
cornerstone.getEnabledElements().forEach(enabledElement => {
if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
});
}
@@ -4,6 +4,7 @@ import log from '../../log';
import user from '../../user';
import getImageAttributes from '../lib/getImageAttributes';
import getLabel from '../lib/getLabel';
import refreshCornerstoneViewports from '../lib/refreshCornerstoneViewports';
export default function({ eventData, tool, toolGroupId, toolGroup }) {
const measurementApi = MeasurementApi.Instance;
@@ -86,9 +87,7 @@ export default function({ eventData, tool, toolGroupId, toolGroup }) {
}
// TODO: This is very hacky, but will work for now
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
refreshCornerstoneViewports();
// TODO: Notify about the last activated measurement
@@ -1,6 +1,7 @@
import cornerstone from 'cornerstone-core';
import { MeasurementApi } from '../classes';
import log from '../../log';
import refreshCornerstoneViewports from '../lib/refreshCornerstoneViewports';
export default function({ eventData, tool, toolGroupId, toolGroup }) {
log.info('CornerstoneToolsMeasurementRemoved');
@@ -37,9 +38,7 @@ export default function({ eventData, tool, toolGroupId, toolGroup }) {
}
// TODO: This is very hacky, but will work for now
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
refreshCornerstoneViewports();
if (MeasurementApi.isToolIncluded(tool)) {
// TODO: Notify that viewer suffered changes
@@ -4,6 +4,7 @@ import log from '../../log';
import user from '../../user';
import getImageAttributes from '../lib/getImageAttributes';
import getLabel from '../lib/getLabel';
import refreshCornerstoneViewports from '../lib/refreshCornerstoneViewports';
export default function handleSingleMeasurementAdded({ eventData, tool }) {
const measurementApi = MeasurementApi.Instance;
@@ -39,9 +40,7 @@ export default function handleSingleMeasurementAdded({ eventData, tool }) {
}
// TODO: This is very hacky, but will work for now
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
refreshCornerstoneViewports();
// TODO: Notify about the last activated measurement
@@ -1,6 +1,7 @@
import cornerstone from 'cornerstone-core';
import { MeasurementApi } from '../classes';
import log from '../../log';
import refreshCornerstoneViewports from '../lib/refreshCornerstoneViewports';
export default function handleSingleMeasurementRemoved({
eventData,
@@ -35,9 +36,7 @@ export default function handleSingleMeasurementRemoved({
});
// TODO: This is very hacky, but will work for now
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
refreshCornerstoneViewports();
if (MeasurementApi.isToolIncluded(tool)) {
// TODO: Notify that viewer suffered changes
+4 -3
View File
@@ -6,11 +6,12 @@ const defaultState = {
const servers = (state = defaultState, action) => {
switch (action.type) {
case 'SET_STUDY_DATA':
const updatedStudyData = cloneDeep(state).studyData;
updatedStudyData[action.StudyInstanceUID] = action.data;
case 'SET_STUDY_DATA': {
const updatedStudyData = cloneDeep(state.studyData);
updatedStudyData[action.StudyInstanceUID] = cloneDeep(action.data);
return Object.assign({}, state, { studyData: updatedStudyData });
}
default:
return state;
}
@@ -28,13 +28,14 @@ const serviceImplementation = {
/**
* Show a new UI modal;
*
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, closeButton, title, customClassName }
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, onClose, closeButton, title, customClassName }
*/
function _show({
content = null,
contentProps = null,
shouldCloseOnEsc = false,
isOpen = true,
onClose = null,
closeButton = true,
title = null,
customClassName = null,
@@ -44,6 +45,7 @@ function _show({
contentProps,
shouldCloseOnEsc,
isOpen,
onClose,
closeButton,
title,
customClassName,
@@ -82,15 +82,20 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
// For each type, see if any are loaded, if not load the most recent.
Object.keys(displaySetsPerModality).forEach(key => {
const displaySets = displaySetsPerModality[key];
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
if (isLoaded) {
return;
}
if (displaySets.some(displaySet => displaySet.loadError)) {
return;
}
// find most recent and load it.
let recentDateTime = 0;
let recentDisplaySet;
let recentDisplaySet = displaySets[0];
displaySets.forEach(displaySet => {
const dateTime = Number(
@@ -102,6 +107,8 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
}
});
recentDisplaySet.isLoading = true;
promises.push(recentDisplaySet.load(referencedDisplaySet, studies));
});
+30
View File
@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.5.2](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.5.1...@ohif/ui@1.5.2) (2020-07-22)
### Bug Fixes
* Switch DICOMFileUploader to use the UIModalService ([#1904](https://github.com/OHIF/Viewers/issues/1904)) ([7772fee](https://github.com/OHIF/Viewers/commit/7772fee21ae6a65994e1251e2f1d2554b47781be))
## [1.5.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.5.0...@ohif/ui@1.5.1) (2020-06-05)
**Note:** Version bump only for package @ohif/ui
# [1.5.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.4.4...@ohif/ui@1.5.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
## [1.4.4](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.4.3...@ohif/ui@1.4.4) (2020-05-04)
+6 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/ui",
"version": "1.4.4",
"version": "1.5.2",
"description": "A set of React components for Medical Imaging Viewers",
"author": "OHIF Contributors",
"license": "MIT",
@@ -41,20 +41,24 @@
"@ohif/i18n": "^0.2.3",
"browser-detect": "^0.2.28",
"classnames": "2.2.6",
"dom-helpers": "^3.3.1",
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
"lodash.debounce": "^4.0.8",
"lodash.isequal": "4.5.0",
"moment": "2.24.0",
"prop-types": "15.6.2",
"react-bootstrap-modal": "4.2.0",
"prop-types-extra": "^1.1.1",
"react-dates": "21.2.1",
"react-dnd": "9.4.0",
"react-dnd-html5-backend": "^9.4.0",
"react-dnd-touch-backend": "^9.4.0",
"react-draggable": "^4.1.0",
"react-error-boundary": "^2.2.1",
"react-i18next": "^10.11.0",
"react-modal": "^3.11.1",
"react-overlays": "^0.8.0",
"react-transition-group": "2.0.0",
"react-with-direction": "1.3.0"
},
"devDependencies": {
@@ -0,0 +1,51 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ErrorBoundary } from 'react-error-boundary';
import './ErrorFallback.css';
const ErrorFallback = ({ error, componentStack, resetErrorBoundary }) => {
return (
<div className="ErrorFallback" role="alert">
<p>Something went wrong.</p>
<pre>{error.message}</pre>
<pre>{componentStack}</pre>
</div>
);
};
const OHIFErrorBoundary = ({
context = 'OHIF',
onReset = () => {},
onError = () => {},
fallbackComponent,
children,
}) => {
const onErrorHandler = (error, componentStack) => {
console.error(`${context} Error Boundary`, error, componentStack);
onError(error, componentStack);
};
const onResetHandler = () => {
onReset();
};
return (
<ErrorBoundary
FallbackComponent={fallbackComponent || ErrorFallback}
onReset={onResetHandler}
onError={onErrorHandler}
>
{children}
</ErrorBoundary>
);
};
OHIFErrorBoundary.propTypes = {
context: PropTypes.string,
onReset: PropTypes.func,
onError: PropTypes.func,
children: PropTypes.node.isRequired,
fallbackComponent: PropTypes.element,
};
export default OHIFErrorBoundary;
@@ -0,0 +1,4 @@
.ErrorFallback {
padding: 10px;
color: var(--active-color);
}
@@ -0,0 +1 @@
export { default as ErrorBoundary } from './ErrorBoundary';
+2 -1
View File
@@ -7,7 +7,6 @@ import { AboutContent } from './content/aboutContent/AboutContent';
import { TabComponents, TabFooter } from './tabComponents';
import { HotkeyField } from './customForm';
import { LanguageSwitcher } from './languageSwitcher';
import { Checkbox } from './checkbox';
import { CineDialog } from './cineDialog';
import { ViewportDownloadForm } from './content/viewportDownloadForm';
@@ -25,8 +24,10 @@ import {
} from './studyList';
import { ToolbarSection } from './toolbarSection';
import { Tooltip } from './tooltip';
import { ErrorBoundary } from './errorBoundary';
export {
ErrorBoundary,
ContextMenu,
Checkbox,
CineDialog,
@@ -32,6 +32,7 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
contentProps: null,
shouldCloseOnEsc: false,
isOpen: true,
onClose: null,
closeButton: true,
title: null,
customClassName: '',
@@ -73,6 +74,7 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
content: ModalContent,
contentProps,
isOpen,
onClose,
title,
customClassName,
shouldCloseOnEsc,
@@ -88,7 +90,13 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
isOpen={isOpen}
title={title}
closeButton={closeButton}
onClose={hide}
onClose={() => {
if (onClose) {
onClose();
}
hide();
}}
>
<ModalContent {...contentProps} show={show} hide={hide} />
</Modal>
@@ -51,3 +51,9 @@ button.close
position: relative;
display: inline-block;
vertical-align: middle;
.btn-sm
padding: .25rem .5rem;
font-size: .875rem;
line-height: 1.5;
border-radius: .2rem;
+2
View File
@@ -29,6 +29,7 @@ import {
Tooltip,
AboutContent,
OHIFModal,
ErrorBoundary
} from './components';
import { useDebounce, useMedia } from './hooks';
@@ -123,6 +124,7 @@ export {
DialogProvider,
withDialog,
useDialog,
ErrorBoundary,
// Hooks
useDebounce,
useMedia,
+196
View File
@@ -3,6 +3,202 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.2.12](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.11...@ohif/viewer@4.2.12) (2020-07-22)
### Bug Fixes
* Switch DICOMFileUploader to use the UIModalService ([#1904](https://github.com/OHIF/Viewers/issues/1904)) ([7772fee](https://github.com/OHIF/Viewers/commit/7772fee21ae6a65994e1251e2f1d2554b47781be))
## [4.2.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.10...@ohif/viewer@4.2.11) (2020-07-13)
### Bug Fixes
* 🐛 - Put guards in all places that a cornerstone re-render ([#1899](https://github.com/OHIF/Viewers/issues/1899)) ([451f7ea](https://github.com/OHIF/Viewers/commit/451f7eab9258e7a193eb362e0926b13aedc4b3c9))
## [4.2.10](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.9...@ohif/viewer@4.2.10) (2020-07-13)
**Note:** Version bump only for package @ohif/viewer
## [4.2.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.8...@ohif/viewer@4.2.9) (2020-07-13)
**Note:** Version bump only for package @ohif/viewer
## [4.2.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.7...@ohif/viewer@4.2.8) (2020-07-13)
### Bug Fixes
* 🐛 Fix RT Panel hide/show and Fix looping load errors ([#1877](https://github.com/OHIF/Viewers/issues/1877)) ([e7cc735](https://github.com/OHIF/Viewers/commit/e7cc735c03d02eeb0d3af4ba02c15ed4f81bbec2))
## [4.2.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.6...@ohif/viewer@4.2.7) (2020-06-18)
**Note:** Version bump only for package @ohif/viewer
## [4.2.6](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.5...@ohif/viewer@4.2.6) (2020-06-18)
**Note:** Version bump only for package @ohif/viewer
## [4.2.5](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.4...@ohif/viewer@4.2.5) (2020-06-15)
### Bug Fixes
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
## [4.2.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.3...@ohif/viewer@4.2.4) (2020-06-15)
### Bug Fixes
* OIDC Redirect erases query parameters ([#1773](https://github.com/OHIF/Viewers/issues/1773)) ([6123741](https://github.com/OHIF/Viewers/commit/6123741765e81d0bea8fbd5dbb0f310aaca0fb33))
## [4.2.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.2...@ohif/viewer@4.2.3) (2020-06-05)
**Note:** Version bump only for package @ohif/viewer
## [4.2.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.1...@ohif/viewer@4.2.2) (2020-06-04)
**Note:** Version bump only for package @ohif/viewer
## [4.2.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.0...@ohif/viewer@4.2.1) (2020-06-04)
**Note:** Version bump only for package @ohif/viewer
# [4.2.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.1.0...@ohif/viewer@4.2.0) (2020-06-04)
### Features
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
# [4.1.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.0.0...@ohif/viewer@4.1.0) (2020-05-15)
### Features
* expose some app internals as window.app ([#1735](https://github.com/OHIF/Viewers/issues/1735)) ([63fd656](https://github.com/OHIF/Viewers/commit/63fd65690cba450721870a6222e0fb3ad71bb291))
# [4.0.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.11...@ohif/viewer@4.0.0) (2020-05-14)
### Bug Fixes
* 🐛 Fix race condition when loading derived display sets ([#1718](https://github.com/OHIF/Viewers/issues/1718)) ([b1678ce](https://github.com/OHIF/Viewers/commit/b1678ce6399dde37a9878f45ccc7c63286d93fab)), closes [#1715](https://github.com/OHIF/Viewers/issues/1715)
### BREAKING CHANGES
* 🧨 However we start to load once the first set of metadata arrives. We need
to wait until all series metadata is fetched.
## [3.11.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.10...@ohif/viewer@3.11.11) (2020-05-14)
### Bug Fixes
* 🐛 Load default display set when no time metadata ([#1684](https://github.com/OHIF/Viewers/issues/1684)) ([f7b8b6a](https://github.com/OHIF/Viewers/commit/f7b8b6a41c4626084ef56b0fdf7363e914b143c4)), closes [#1683](https://github.com/OHIF/Viewers/issues/1683)
## [3.11.10](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.9...@ohif/viewer@3.11.10) (2020-05-13)
**Note:** Version bump only for package @ohif/viewer
## [3.11.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.8...@ohif/viewer@3.11.9) (2020-05-12)
### Bug Fixes
* 🐛 Fix seg color load ([#1724](https://github.com/OHIF/Viewers/issues/1724)) ([c4f84b1](https://github.com/OHIF/Viewers/commit/c4f84b1174d04ba84d37ed89b6d7ab541be28181))
## [3.11.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.7...@ohif/viewer@3.11.8) (2020-05-06)
**Note:** Version bump only for package @ohif/viewer
## [3.11.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.6...@ohif/viewer@3.11.7) (2020-05-04)
**Note:** Version bump only for package @ohif/viewer
## [3.11.6](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.5...@ohif/viewer@3.11.6) (2020-05-04)
@@ -5,17 +5,18 @@ describe('OHIF Routes', function() {
cy.openStudyList();
});
it('checks TEST json url study route', function() {
cy.visit(
'/viewer?url=https://ohif-viewer.s3.eu-central-1.amazonaws.com/JSON/testJSON.json'
);
// TODO -> Bring back when testJSON is hosted again.
// it('checks TEST json url study route', function() {
// cy.visit(
// '/viewer?url=https://ohif-viewer.s3.eu-central-1.amazonaws.com/JSON/testJSON.json'
// );
cy.server();
cy.route('GET', '**/MRStudy/**').as('getTESTStudy');
// cy.server();
// cy.route('GET', '**/ MRStudy; /**').as('getTESTStudy');
cy.wait('@getTESTStudy.all');
cy.get('@getTESTStudy').should($route => {
expect($route.status).to.be.eq(200);
});
});
// cy.wait('@getTESTStudy.all');
// cy.get('@getTESTStudy').should($route => {
// expect($route.status).to.be.eq(200);
// });
// });
});
@@ -22,7 +22,7 @@ describe('OHIF Study List', function() {
//Wait result list to be displayed
cy.waitStudyList();
cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(2);
expect($list.length).to.be.eq(3);
expect($list).to.contain('Juno');
});
});
@@ -38,12 +38,12 @@ describe('OHIF Study List', function() {
});
it('searches Accession with exact string', function() {
cy.get('@AccessionNumber').type('fpcben98890');
cy.get('@AccessionNumber').type('0000481914');
//Wait result list to be displayed
cy.waitStudyList();
cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(1);
expect($list).to.contain('fpcben98890');
expect($list).to.contain('0000481914');
});
});
@@ -52,7 +52,8 @@ describe('OHIF Study List', function() {
//Wait result list to be displayed
cy.waitStudyList();
cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(15); // TODO: Where are you hiding MISTER^MR?
// TODO: Why are we facing some inconsistency with this result? ¯\_(ツ)_/¯
expect($list.length).to.be.eq(9);
expect($list).to.contain('MR');
});
});
@@ -127,7 +128,7 @@ describe('OHIF Study List', function() {
//Wait result list to be displayed
cy.waitStudyList();
cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(2);
expect($list.length).to.be.eq(3);
expect($list).to.contain('Juno');
});
});
@@ -137,7 +138,7 @@ describe('OHIF Study List', function() {
//Wait result list to be displayed
cy.waitStudyList();
cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(2);
expect($list.length).to.be.eq(6);
expect($list).to.contain('ProstateX-0000');
});
});
@@ -147,18 +148,19 @@ describe('OHIF Study List', function() {
//Wait result list to be displayed
cy.waitStudyList();
cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(15); // TODO: Where are you hiding MISTER^MR?
// TODO: Why are we facing some inconsistency with this result? ¯\_(ツ)_/¯
expect($list.length).to.be.eq(9);
expect($list).to.contain('MR');
});
});
it('searches Accession with exact string', function() {
cy.get('@accessionModalityDescription').type('fpcben98890');
cy.get('@accessionModalityDescription').type('0000481914');
//Wait result list to be displayed
cy.waitStudyList();
cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(1);
expect($list).to.contain('fpcben98890');
expect($list).to.contain('0000481914');
});
});
@@ -1,7 +1,7 @@
describe('OHIF Microscopy Extension', () => {
before(() => {
cy.openStudyModality('SM');
cy.expectMinimumThumbnails(6);
cy.expectMinimumThumbnails(2);
});
it('checks if series thumbnails are being displayed', () => {
@@ -21,8 +21,8 @@ describe('OHIF VTK Extension', () => {
//Select 2D MPR button
cy.get('[data-cy="2d mpr"]').click();
//Wait Reformatting Images
cy.waitVTKReformatting();
//Wait waitVTKLoading Images
cy.waitVTKLoading();
});
beforeEach(() => {
@@ -18,8 +18,8 @@ describe('Visual Regression - OHIF VTK Extension', () => {
//Select 2D MPR button
cy.get('[data-cy="2d mpr"]').click();
//Wait Reformatting Images
cy.waitVTKReformatting();
//Wait waitVTKLoading Images
cy.waitVTKwaitVTKLoading();
});
beforeEach(() => {
@@ -5,13 +5,15 @@ describe('Visual Regression - OHIF Routes', function() {
cy.openStudyList();
});
/*
// TODO -> Bring back when testJSON is hosted again.
it('checks TEST json url study route', function() {
cy.visit(
'/viewer?url=https://ohif-viewer.s3.eu-central-1.amazonaws.com/JSON/testJSON.json'
);
cy.server();
cy.route('GET', '**/TESTStudy/**').as('getTESTStudy');
cy.route('GET', '**/ TESTStudy; /**').as('getTESTStudy');
cy.wait('@getTESTStudy.all');
cy.get('@getTESTStudy').should($route => {
@@ -20,4 +22,5 @@ describe('Visual Regression - OHIF Routes', function() {
cy.percyCanvasSnapshot('TEST json study route');
});
*/
});
+6 -6
View File
@@ -107,15 +107,15 @@ Cypress.Commands.add('waitStudyList', () => {
});
});
Cypress.Commands.add('waitVTKReformatting', () => {
// Wait for start reformatting
cy.get('[data-cy="viewprt-grid"]', { timeout: 10000 }).should($grid => {
expect($grid).to.contain.text('Reform');
Cypress.Commands.add('waitVTKLoading', () => {
// Wait for start loading
cy.get('[data-cy="viewprt-grid"]', { timeout: 20000 }).should($grid => {
expect($grid).to.contain.text('Loading');
});
// Wait for finish reformatting
// Wait for finish loading
cy.get('[data-cy="viewprt-grid"]', { timeout: 30000 }).should($grid => {
expect($grid).not.to.contain.text('Reform');
expect($grid).not.to.contain.text('Loading');
});
});
+12 -12
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/viewer",
"version": "3.11.6",
"version": "4.2.12",
"description": "OHIF Viewer",
"author": "OHIF Contributors",
"license": "MIT",
@@ -48,26 +48,26 @@
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"@ohif/core": "^2.9.3",
"@ohif/extension-cornerstone": "^2.7.2",
"@ohif/extension-dicom-html": "^1.2.2",
"@ohif/extension-dicom-microscopy": "^0.50.9",
"@ohif/extension-dicom-p10-downloader": "^0.1.0",
"@ohif/core": "^2.9.13",
"@ohif/extension-cornerstone": "^2.8.4",
"@ohif/extension-dicom-html": "^1.2.4",
"@ohif/extension-dicom-microscopy": "^0.51.0",
"@ohif/extension-dicom-p10-downloader": "^0.1.2",
"@ohif/extension-dicom-pdf": "^1.0.3",
"@ohif/extension-dicom-rt": "^0.1.2",
"@ohif/extension-dicom-segmentation": "^0.2.3",
"@ohif/extension-dicom-rt": "^0.2.5",
"@ohif/extension-dicom-segmentation": "^0.3.3",
"@ohif/extension-lesion-tracker": "^0.2.0",
"@ohif/extension-vtk": "^1.5.3",
"@ohif/extension-vtk": "^1.6.8",
"@ohif/i18n": "^0.52.8",
"@ohif/ui": "^1.4.4",
"@ohif/ui": "^1.5.2",
"@tanem/react-nprogress": "^1.1.25",
"classnames": "^2.2.6",
"core-js": "^3.2.1",
"cornerstone-core": "^2.2.8",
"cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.12.5",
"cornerstone-tools": "4.15.1",
"cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2",
"dcmjs": "0.16.0",
"dicom-parser": "^1.8.3",
"dicomweb-client": "^0.4.4",
"hammerjs": "^2.0.8",
+1
View File
@@ -15,6 +15,7 @@ window.config = {
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
enableStudyLazyLoad: true,
supportsFuzzyMatching: true,
},
],
},
+1 -2
View File
@@ -9,11 +9,10 @@ window.config = function(props) {
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',
healthcareApiEndpoint: 'https://proxy-dot-idc-dev.appspot.com/v1beta1',
};
};
+1
View File
@@ -12,6 +12,7 @@ window.config = {
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
enableStudyLazyLoad: true,
supportsFuzzyMatching: true,
},
],
},
@@ -11,6 +11,7 @@ window.config = {
qidoSupportsIncludeField: true,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
supportsFuzzyMatching: true,
},
],
},
+59 -40
View File
@@ -13,6 +13,7 @@ import {
ModalProvider,
DialogProvider,
OHIFModal,
ErrorBoundary
} from '@ohif/ui';
import {
@@ -53,7 +54,7 @@ import store from './store';
/** Contexts */
import WhiteLabelingContext from './context/WhiteLabelingContext';
import UserManagerContext from './context/UserManagerContext';
import AppContext from './context/AppContext';
import { AppProvider, useAppContext, CONTEXTS } from './context/AppContext';
/** ~~~~~~~~~~~~~ Application Setup */
const commandsManagerConfig = {
@@ -71,6 +72,14 @@ let extensionManager;
// TODO[react] Use a provider when the whole tree is React
window.store = store;
window.ohif = window.ohif || {};
window.ohif.app = {
commandsManager,
hotkeysManager,
servicesManager,
extensionManager,
};
class App extends Component {
static propTypes = {
config: PropTypes.oneOfType([
@@ -159,53 +168,57 @@ class App extends Component {
if (this._userManager) {
return (
<AppContext.Provider value={{ appConfig: this._appConfig }}>
<ErrorBoundary context='App'>
<Provider store={store}>
<I18nextProvider i18n={i18n}>
<OidcProvider store={store} userManager={this._userManager}>
<UserManagerContext.Provider value={this._userManager}>
<Router basename={routerBasename}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider
modal={OHIFModal}
service={UIModalService}
>
<OHIFStandaloneViewer
userManager={this._userManager}
/>
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabelingContext.Provider>
</Router>
</UserManagerContext.Provider>
</OidcProvider>
</I18nextProvider>
<AppProvider config={this._appConfig}>
<I18nextProvider i18n={i18n}>
<OidcProvider store={store} userManager={this._userManager}>
<UserManagerContext.Provider value={this._userManager}>
<Router basename={routerBasename}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider
modal={OHIFModal}
service={UIModalService}
>
<OHIFStandaloneViewer
userManager={this._userManager}
/>
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabelingContext.Provider>
</Router>
</UserManagerContext.Provider>
</OidcProvider>
</I18nextProvider>
</AppProvider>
</Provider>
</AppContext.Provider>
</ErrorBoundary>
);
}
return (
<AppContext.Provider value={{ appConfig: this._appConfig }}>
<ErrorBoundary context='App'>
<Provider store={store}>
<I18nextProvider i18n={i18n}>
<Router basename={routerBasename}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider modal={OHIFModal} service={UIModalService}>
<OHIFStandaloneViewer />
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabelingContext.Provider>
</Router>
</I18nextProvider>
<AppProvider config={this._appConfig}>
<I18nextProvider i18n={i18n}>
<Router basename={routerBasename}>
<WhiteLabelingContext.Provider value={whiteLabeling}>
<SnackbarProvider service={UINotificationService}>
<DialogProvider service={UIDialogService}>
<ModalProvider modal={OHIFModal} service={UIModalService}>
<OHIFStandaloneViewer />
</ModalProvider>
</DialogProvider>
</SnackbarProvider>
</WhiteLabelingContext.Provider>
</Router>
</I18nextProvider>
</AppProvider>
</Provider>
</AppContext.Provider>
</ErrorBoundary>
);
}
@@ -255,6 +268,12 @@ function _initExtensions(extensions, cornerstoneExtensionConfig, appConfig) {
commandsManager,
servicesManager,
appConfig,
api: {
contexts: CONTEXTS,
hooks: {
useAppContext
}
}
});
const requiredExtensions = [
+20 -6
View File
@@ -5,7 +5,7 @@ import { Route, Switch } from 'react-router-dom';
import { NProgress } from '@tanem/react-nprogress';
import { CSSTransition } from 'react-transition-group';
import { connect } from 'react-redux';
import { ViewerbaseDragDropContext } from '@ohif/ui';
import { ViewerbaseDragDropContext, ErrorBoundary } from '@ohif/ui';
import { SignoutCallbackComponent } from 'redux-oidc';
import asyncComponent from './components/AsyncComponent.js';
import * as RoutesUtil from './routes/routesUtil';
@@ -52,10 +52,13 @@ class OHIFStandaloneViewer extends Component {
const { appConfig = {} } = this.context;
const userNotLoggedIn = userManager && (!user || user.expired);
if (userNotLoggedIn) {
const pathname = this.props.location.pathname;
const { pathname, search } = this.props.location;
if (pathname !== '/callback') {
sessionStorage.setItem('ohif-redirect-to', pathname);
sessionStorage.setItem(
'ohif-redirect-to',
JSON.stringify({ pathname, search })
);
}
return (
@@ -103,12 +106,21 @@ class OHIFStandaloneViewer extends Component {
userManager.removeUser().then(() => {
if (targetLinkUri !== null) {
const ohifRedirectTo = {
pathname: new URL(targetLinkUri).pathname,
};
sessionStorage.setItem(
'ohif-redirect-to',
new URL(targetLinkUri).pathname
JSON.stringify(ohifRedirectTo)
);
} else {
sessionStorage.setItem('ohif-redirect-to', '/');
const ohifRedirectTo = {
pathname: '/',
};
sessionStorage.setItem(
'ohif-redirect-to',
JSON.stringify(ohifRedirectTo)
);
}
if (loginHint !== null) {
@@ -191,7 +203,9 @@ class OHIFStandaloneViewer extends Component {
{match === null ? (
<></>
) : (
<Component match={match} location={this.props.location} />
<ErrorBoundary context={match.url}>
<Component match={match} location={this.props.location} />
</ErrorBoundary>
)}
</CSSTransition>
)}
@@ -329,7 +329,9 @@ const mapDispatchToProps = (dispatch, ownProps) => {
measurementApi.syncMeasurementsAndToolData();
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
});
// Needs to update viewports.layout state to set layout
@@ -0,0 +1,18 @@
.ErrorBoundaryDialogTitle {
margin-top: 0;
}
.ErrorBoundaryDialogButton {
outline: none;
}
.ErrorBoundaryDialogIcon {
margin-right: 5px;
width: 10px;
transform: rotate(-90deg);
vertical-align: middle;
}
.ErrorBoundaryDialogIcon.opened {
transform: rotate(0deg);
}
@@ -0,0 +1,72 @@
import React, { useState } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import { ErrorBoundary, Icon } from '@ohif/ui';
import { servicesManager } from './../../App';
import './ErrorBoundaryDialog.css';
const { UIModalService } = servicesManager.services;
const ErrorBoundaryDialog = ({ context, children }) => {
const handleOnError = (error, componentStack) => {
const ErrorDialog = () => {
const [open, setOpen] = useState(false);
return (
<div className="ErrorFallback" role="alert">
<div className="ErrorBoundaryDialog">
<h3 className="ErrorBoundaryDialogTitle">
{context}: <span>{error.message}</span>
</h3>
</div>
<button
className="btn btn-primary btn-sm ErrorBoundaryDialogButton"
onClick={() => setOpen(s => !s)}
>
<Icon
name="chevron-down"
className={classnames('ErrorBoundaryDialogIcon', {
opened: open,
})}
/>
Stack Trace
</button>
{open && <pre>{componentStack}</pre>}
</div>
);
};
UIModalService.show({
content: ErrorDialog,
title: `Something went wrong in ${context}`,
});
};
const fallbackComponent = () => (
<div className="ErrorFallback" role="alert">
<p>
Error rendering {context}. <br /> Check the browser console for more
details.
</p>
</div>
);
return (
<ErrorBoundary
fallbackComponent={fallbackComponent}
context={context}
onError={handleOnError}
>
{children}
</ErrorBoundary>
);
};
ErrorBoundaryDialog.propTypes = {
context: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
};
export default ErrorBoundaryDialog;
@@ -0,0 +1,3 @@
import ErrorBoundaryDialog from './ErrorBoundaryDialog';
export default ErrorBoundaryDialog;
@@ -24,6 +24,7 @@ const ViewportGrid = function(props) {
studies,
viewportData,
children,
isStudyLoaded,
} = props;
const rowSize = 100 / numRows;
@@ -37,21 +38,23 @@ const ViewportGrid = function(props) {
const snackbar = useSnackbarContext();
useEffect(() => {
viewportData.forEach(displaySet => {
const promises = loadAndCacheDerivedDisplaySets(displaySet, studies);
if (isStudyLoaded) {
viewportData.forEach(displaySet => {
const promises = loadAndCacheDerivedDisplaySets(displaySet, studies);
promises.forEach(promise => {
promise.catch(error => {
snackbar.show({
title: 'Error loading derived display set:',
message: error.message,
type: 'error',
autoClose: false,
promises.forEach(promise => {
promise.catch(error => {
snackbar.show({
title: 'Error loading derived display set:',
message: error.message,
type: 'error',
autoClose: false,
});
});
});
});
});
}, [studies, viewportData]);
}
}, [studies, viewportData, isStudyLoaded, snackbar]);
const getViewportPanes = () =>
layout.viewports.map((layout, viewportIndex) => {
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import './ViewportPane.css';
const ViewportPane = function(props) {
const ViewportPane = function (props) {
const { children, onDrop, viewportIndex, className: propClassName } = props;
const [{ hovered, highlighted }, drop] = useDrop({
accept: 'thumbnail',
@@ -1,15 +0,0 @@
// TODO: REPLACE THIS WITH A CONTEXT PROVIDER
// EVERYTHING IN `VIEWER.JS` COULD USE THIS FOR APPROPRIATE CONTEXT
import ToolbarRow from './ToolbarRow';
import { connect } from 'react-redux';
import { getActiveContexts } from './../store/layout/selectors.js';
const mapStateToProps = state => {
return {
activeContexts: getActiveContexts(state),
};
};
const ConnectedToolbarRow = connect(mapStateToProps)(ToolbarRow);
export default ConnectedToolbarRow;
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js';
import OHIF from '@ohif/core';
const { clearViewportSpecificData } = OHIF.redux.actions;
const { clearViewportSpecificData, setStudyData } = OHIF.redux.actions;
const isActive = a => a.active === true;
const mapStateToProps = (state, ownProps) => {
@@ -14,6 +14,9 @@ const mapStateToProps = (state, ownProps) => {
};
const mapDispatchToProps = dispatch => {
return {
setStudyData: (StudyInstanceUID, data) => {
dispatch(setStudyData(StudyInstanceUID, data));
},
clearViewportSpecificData: () => {
dispatch(clearViewportSpecificData());
},
@@ -16,6 +16,7 @@ import { commandsManager, extensionManager } from './../App.js';
import ConnectedCineDialog from './ConnectedCineDialog';
import ConnectedLayoutButton from './ConnectedLayoutButton';
import { withAppContext } from '../context/AppContext';
class ToolbarRow extends Component {
// TODO: Simplify these? isOpen can be computed if we say "any" value for selected,
@@ -381,5 +382,5 @@ function _handleBuiltIn(button) {
}
export default withTranslation(['Common', 'ViewportDownloadForm'])(
withModal(withDialog(ToolbarRow))
withModal(withDialog(withAppContext(ToolbarRow)))
);
@@ -2,15 +2,15 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { MODULE_TYPES } from '@ohif/core';
import OHIF, { DICOMSR } from '@ohif/core';
import OHIF, { MODULE_TYPES, DICOMSR } from '@ohif/core';
import { withDialog } from '@ohif/ui';
import moment from 'moment';
import ConnectedHeader from './ConnectedHeader.js';
import ConnectedToolbarRow from './ConnectedToolbarRow.js';
import ToolbarRow from './ToolbarRow.js';
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
import ConnectedViewerMain from './ConnectedViewerMain.js';
import SidePanel from './../components/SidePanel.js';
import ErrorBoundaryDialog from './../components/ErrorBoundaryDialog';
import { extensionManager } from './../App.js';
// Contexts
@@ -26,6 +26,7 @@ class Viewer extends Component {
PropTypes.shape({
StudyInstanceUID: PropTypes.string.isRequired,
StudyDate: PropTypes.string,
PatientID: PropTypes.string,
displaySets: PropTypes.arrayOf(
PropTypes.shape({
displaySetInstanceUID: PropTypes.string.isRequired,
@@ -256,42 +257,44 @@ class Viewer extends Component {
</WhiteLabelingContext.Consumer>
{/* TOOLBAR */}
<ConnectedToolbarRow
isLeftSidePanelOpen={this.state.isLeftSidePanelOpen}
isRightSidePanelOpen={this.state.isRightSidePanelOpen}
selectedLeftSidePanel={
this.state.isLeftSidePanelOpen
? this.state.selectedLeftSidePanel
: ''
}
selectedRightSidePanel={
this.state.isRightSidePanelOpen
? this.state.selectedRightSidePanel
: ''
}
handleSidePanelChange={(side, selectedPanel) => {
const sideClicked = side && side[0].toUpperCase() + side.slice(1);
const openKey = `is${sideClicked}SidePanelOpen`;
const selectedKey = `selected${sideClicked}SidePanel`;
const updatedState = Object.assign({}, this.state);
const isOpen = updatedState[openKey];
const prevSelectedPanel = updatedState[selectedKey];
// RoundedButtonGroup returns `null` if selected button is clicked
const isSameSelectedPanel =
prevSelectedPanel === selectedPanel || selectedPanel === null;
updatedState[selectedKey] = selectedPanel || prevSelectedPanel;
const isClosedOrShouldClose = !isOpen || isSameSelectedPanel;
if (isClosedOrShouldClose) {
updatedState[openKey] = !updatedState[openKey];
<ErrorBoundaryDialog context="ToolbarRow">
<ToolbarRow
isLeftSidePanelOpen={this.state.isLeftSidePanelOpen}
isRightSidePanelOpen={this.state.isRightSidePanelOpen}
selectedLeftSidePanel={
this.state.isLeftSidePanelOpen
? this.state.selectedLeftSidePanel
: ''
}
selectedRightSidePanel={
this.state.isRightSidePanelOpen
? this.state.selectedRightSidePanel
: ''
}
handleSidePanelChange={(side, selectedPanel) => {
const sideClicked = side && side[0].toUpperCase() + side.slice(1);
const openKey = `is${sideClicked}SidePanelOpen`;
const selectedKey = `selected${sideClicked}SidePanel`;
const updatedState = Object.assign({}, this.state);
this.setState(updatedState);
}}
studies={this.props.studies}
/>
const isOpen = updatedState[openKey];
const prevSelectedPanel = updatedState[selectedKey];
// RoundedButtonGroup returns `null` if selected button is clicked
const isSameSelectedPanel =
prevSelectedPanel === selectedPanel || selectedPanel === null;
updatedState[selectedKey] = selectedPanel || prevSelectedPanel;
const isClosedOrShouldClose = !isOpen || isSameSelectedPanel;
if (isClosedOrShouldClose) {
updatedState[openKey] = !updatedState[openKey];
}
this.setState(updatedState);
}}
studies={this.props.studies}
/>
</ErrorBoundaryDialog>
{/*<ConnectedStudyLoadingMonitor studies={this.props.studies} />*/}
{/*<StudyPrefetcher studies={this.props.studies} />*/}
@@ -299,37 +302,46 @@ class Viewer extends Component {
{/* VIEWPORTS + SIDEPANELS */}
<div className="FlexboxLayout">
{/* LEFT */}
<SidePanel from="left" isOpen={this.state.isLeftSidePanelOpen}>
{VisiblePanelLeft ? (
<VisiblePanelLeft
viewports={this.props.viewports}
studies={this.props.studies}
activeIndex={this.props.activeViewportIndex}
/>
) : (
<ConnectedStudyBrowser
studies={this.state.thumbnails}
studyMetadata={this.props.studies}
/>
)}
</SidePanel>
<ErrorBoundaryDialog context="LeftSidePanel">
<SidePanel from="left" isOpen={this.state.isLeftSidePanelOpen}>
{VisiblePanelLeft ? (
<VisiblePanelLeft
viewports={this.props.viewports}
studies={this.props.studies}
activeIndex={this.props.activeViewportIndex}
/>
) : (
<ConnectedStudyBrowser
studies={this.state.thumbnails}
studyMetadata={this.props.studies}
/>
)}
</SidePanel>
</ErrorBoundaryDialog>
{/* MAIN */}
<div className={classNames('main-content')}>
<ConnectedViewerMain studies={this.props.studies} />
<ErrorBoundaryDialog context="ViewerMain">
<ConnectedViewerMain
studies={this.props.studies}
isStudyLoaded={this.props.isStudyLoaded}
/>
</ErrorBoundaryDialog>
</div>
{/* RIGHT */}
<SidePanel from="right" isOpen={this.state.isRightSidePanelOpen}>
{VisiblePanelRight && (
<VisiblePanelRight
isOpen={this.state.isRightSidePanelOpen}
viewports={this.props.viewports}
studies={this.props.studies}
activeIndex={this.props.activeViewportIndex}
/>
)}
</SidePanel>
<ErrorBoundaryDialog context="RightSidePanel">
<SidePanel from="right" isOpen={this.state.isRightSidePanelOpen}>
{VisiblePanelRight && (
<VisiblePanelRight
isOpen={this.state.isRightSidePanelOpen}
viewports={this.props.viewports}
studies={this.props.studies}
activeIndex={this.props.activeViewportIndex}
/>
)}
</SidePanel>
</ErrorBoundaryDialog>
</div>
</>
);
@@ -150,6 +150,7 @@ class ViewerMain extends Component {
<div className="ViewerMain">
{this.state.displaySets.length && (
<ConnectedViewportGrid
isStudyLoaded={this.props.isStudyLoaded}
studies={this.props.studies}
viewportData={viewportData}
setViewportData={this.setViewportData}
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useContext } from 'react';
import React, { useState, useEffect, useContext, useCallback } from 'react';
import { metadata, studies, utils, log } from '@ohif/core';
import usePrevious from '../customHooks/usePrevious';
@@ -7,13 +7,13 @@ import PropTypes from 'prop-types';
import { extensionManager } from './../App.js';
import { useSnackbarContext } from '@ohif/ui';
// Contexts
import AppContext from '../context/AppContext';
const { OHIFStudyMetadata, OHIFSeriesMetadata } = metadata;
const { retrieveStudiesMetadata, deleteStudyMetadataPromise } = studies;
const { studyMetadataManager, makeCancelable } = utils;
// Contexts
import AppContext from '../context/AppContext';
const _promoteToFront = (list, value, searchMethod) => {
let response = [...list];
let promoted = false;
@@ -125,9 +125,14 @@ const _addSeriesToStudy = (studyMetadata, series) => {
studyMetadata.createAndAddDisplaySetsForSeries(
sopClassHandlerModules,
seriesMetadata,
seriesMetadata
);
study.displaySets = studyMetadata.getDisplaySets();
study.derivedDisplaySets = studyMetadata.getDerivedDatasets({
Modality: series.Modality,
});
_updateStudyMetadataManager(study, studyMetadata);
};
@@ -147,6 +152,10 @@ const _updateStudyDisplaySets = (study, studyMetadata) => {
study.displaySets = studyMetadata.createDisplaySets(sopClassHandlerModules);
}
if (study.derivedDisplaySets) {
studyMetadata._addDerivedDisplaySets(study.derivedDisplaySets);
}
studyMetadata.setDisplaySets(study.displaySets);
};
@@ -154,11 +163,21 @@ const _sortStudyDisplaySet = (study, studyMetadata) => {
studyMetadata.sortDisplaySets(study.displaySets);
};
const _thinStudyData = study => {
return {
StudyInstanceUID: study.StudyInstanceUID,
series: study.series.map(item => ({
SeriesInstanceUID: item.SeriesInstanceUID,
})),
};
};
function ViewerRetrieveStudyData({
server,
studyInstanceUIDs,
seriesInstanceUIDs,
clearViewportSpecificData,
setStudyData,
}) {
// hooks
const [error, setError] = useState(false);
@@ -220,6 +239,7 @@ function ViewerRetrieveStudyData({
if (Array.isArray(studiesData) && studiesData.length > 0) {
// Map studies to new format, update metadata manager?
const studies = studiesData.map(study => {
setStudyData(study.StudyInstanceUID, _thinStudyData(study));
const studyMetadata = new OHIFStudyMetadata(
study,
study.StudyInstanceUID
@@ -265,11 +285,13 @@ function ViewerRetrieveStudyData({
return loadNextSeries();
};
const concurrentRequestsAllowed = maxConcurrentMetadataRequests || studyMetadata.getSeriesCount();
const concurrentRequestsAllowed =
maxConcurrentMetadataRequests || studyMetadata.getSeriesCount();
const promises = Array(concurrentRequestsAllowed)
.fill(null)
.map(loadNextSeries);
await Promise.all(promises);
return await Promise.all(promises);
};
const loadStudies = async () => {
@@ -310,7 +332,7 @@ function ViewerRetrieveStudyData({
}
};
const purgeCancellablePromises = () => {
const purgeCancellablePromises = useCallback(() => {
for (let studyInstanceUIDs in cancelableStudiesPromises) {
if ('cancel' in cancelableStudiesPromises[studyInstanceUIDs]) {
cancelableStudiesPromises[studyInstanceUIDs].cancel();
@@ -324,7 +346,7 @@ function ViewerRetrieveStudyData({
studyMetadataManager.remove(studyInstanceUIDs);
}
}
};
});
const prevStudyInstanceUIDs = usePrevious(studyInstanceUIDs);
@@ -338,7 +360,7 @@ function ViewerRetrieveStudyData({
studyMetadataManager.purge();
purgeCancellablePromises();
}
}, [studyInstanceUIDs]);
}, [prevStudyInstanceUIDs, purgeCancellablePromises, studyInstanceUIDs]);
useEffect(() => {
cancelableSeriesPromises = {};
@@ -368,6 +390,7 @@ ViewerRetrieveStudyData.propTypes = {
seriesInstanceUIDs: PropTypes.array,
server: PropTypes.object,
clearViewportSpecificData: PropTypes.func.isRequired,
setStudyData: PropTypes.func.isRequired,
};
export default ViewerRetrieveStudyData;
+29 -1
View File
@@ -1,5 +1,33 @@
import React from 'react';
import React, { useContext } from 'react';
import { useSelector } from 'react-redux';
import { getActiveContexts } from '../store/layout/selectors.js';
let AppContext = React.createContext({});
export const CONTEXTS = {
CORNERSTONE: 'ACTIVE_VIEWPORT::CORNERSTONE',
VTK: 'ACTIVE_VIEWPORT::VTK'
};
export const useAppContext = () => useContext(AppContext);
export const AppProvider = ({ children, config }) => {
const activeContexts = useSelector(state => getActiveContexts(state));
return (
<AppContext.Provider value={{ appConfig: config, activeContexts }}>
{children}
</AppContext.Provider>
);
};
export const withAppContext = Component => {
return function WrappedComponent(props) {
const { appConfig, activeContexts } = useAppContext();
return (
<Component {...props} appConfig={appConfig} activeContexts={activeContexts} />
);
};
};
export default AppContext;
@@ -1,49 +1,44 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Modal from 'react-bootstrap-modal';
import DicomUploader from './DicomUploader';
import { withTranslation } from 'react-i18next';
import { servicesManager } from './../App.js';
class DicomFileUploaderModal extends Component {
static propTypes = {
url: PropTypes.string,
retrieveAuthHeaderFunction: PropTypes.func,
onClose: PropTypes.func,
};
function DicomFileUploaderModal({
isOpen = false,
onClose,
url,
retrieveAuthHeaderFunction,
t,
}) {
const { UIModalService } = servicesManager.services;
state = {
uploaded: false,
};
render() {
if (!this.props.url) {
return null;
const showDicomStorePickerModal = () => {
if (!UIModalService) {
return
}
return (
<Modal
show={this.props.isOpen}
onHide={this.props.onClose}
aria-labelledby="ModalHeader"
className="modal fade themed in"
backdrop={false}
size={'md'}
keyboard={true}
>
<Modal.Header closeButton>
<Modal.Title>
{this.props.t('Upload DICOM Files')}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<DicomUploader
url={this.props.url}
retrieveAuthHeaderFunction={this.props.retrieveAuthHeaderFunction}
/>
</Modal.Body>
</Modal>
);
}
UIModalService.show({
content: DicomUploader,
title: t('Upload DICOM Files'),
contentProps: {
url,
retrieveAuthHeaderFunction
},
onClose,
});
};
return (
<React.Fragment>{isOpen && showDicomStorePickerModal()}</React.Fragment>
);
}
DicomFileUploaderModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
retrieveAuthHeaderFunction: PropTypes.func.isRequired,
onClose: PropTypes.func,
url: PropTypes.string,
};
export default withTranslation('Common')(DicomFileUploaderModal);
@@ -36,6 +36,7 @@ function DicomStorePickerModal({
user,
url,
},
onClose
});
}
};
+4 -2
View File
@@ -14,9 +14,11 @@ class CallbackPage extends Component {
<CallbackComponent
userManager={this.props.userManager}
successCallback={() => {
const pathname = sessionStorage.getItem('ohif-redirect-to');
const { pathname, search = '' } = JSON.parse(
sessionStorage.getItem('ohif-redirect-to')
);
this.props.history.push(pathname);
this.props.history.push({ pathname, search });
}}
errorCallback={error => {
//this.props.history.push("/");
+86 -38
View File
@@ -1247,13 +1247,34 @@
pirates "^4.0.0"
source-map-support "^0.5.9"
"@babel/runtime@7.1.2", "@babel/runtime@7.5.5", "@babel/runtime@7.6.0", "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
"@babel/runtime@7.1.2":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.1.2.tgz#81c89935f4647706fc54541145e6b4ecfef4b8e3"
integrity sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==
dependencies:
regenerator-runtime "^0.12.0"
"@babel/runtime@7.6.0":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205"
integrity sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.6":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.5.tgz#303d8bd440ecd5a491eae6117fd3367698674c5c"
integrity sha512-otddXKhdNn7d0ptoFRHtMLa8LqDxLYwTjB4nYgM1yy5N6gU/MUf8zqyyLltCH3yAVitBzmwK4us+DD0l/MauAg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6"
@@ -4992,6 +5013,11 @@ cfb@^1.1.0:
crc-32 "~1.2.0"
printj "~1.1.2"
chain-function@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.1.tgz#c63045e5b4b663fb86f1c6e186adaf1de402a1cc"
integrity sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg==
chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -5945,10 +5971,10 @@ cornerstone-math@^0.1.8:
resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5"
integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw==
cornerstone-tools@4.12.5:
version "4.12.5"
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.12.5.tgz#9a4a35d9851d6c81f165977aa6ede8cf228d4c80"
integrity sha512-tNcIQ9E2+GbfZDbbPnWmocYPi0OALlYLKQbXa3gc7rdnF8K9DsptnpGzMtiKfoWUjYfsUWrytvI/RgusXRWCTg==
cornerstone-tools@4.15.1:
version "4.15.1"
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.15.1.tgz#f0b1026da9c7758defc088bb2f1e78426a23d91e"
integrity sha512-fJuTUJW/NDSD520jPB++tX//Kq80jPDngChHZrhx2xjj/9dCnexD9kmTX30Z0WUq0a2JEHZ6FlcOX38kSmw1hQ==
dependencies:
"@babel/runtime" "7.1.2"
cornerstone-math "0.1.7"
@@ -6534,10 +6560,10 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
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==
dcmjs@0.16.0:
version "0.16.0"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.16.0.tgz#714755d006c3a49cf65ef13e627ff7a23151d35a"
integrity sha512-41nY3Isg5i5FoSd1s67aUgUHi146r+fb8JuiRvDsxvm7MEkkdjIzMYMtxjsrVwKRW1xRH+x34nxbV9+8cc7WQw==
dependencies:
"@babel/polyfill" "^7.8.3"
"@babel/runtime" "^7.8.4"
@@ -7247,7 +7273,7 @@ dom-converter@^0.2:
dependencies:
utila "~0.4"
dom-helpers@^3.2.1, dom-helpers@^3.3.1, dom-helpers@^3.4.0:
dom-helpers@^3.2.0, dom-helpers@^3.2.1, dom-helpers@^3.3.1, dom-helpers@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
@@ -9110,6 +9136,11 @@ gl-matrix@^3.1.0:
resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.1.0.tgz#f5b2de17d8fed95a79e5025b10cded0ab9ccbed0"
integrity sha512-526NA+3EA+ztAQi0IZpSWiM0fyQXIp7IbRvfJ4wS/TjjQD0uv0fVybXwwqqSOlq33UckivI0yMDlVtboWm3k7A==
gl-matrix@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.3.0.tgz#232eef60b1c8b30a28cbbe75b2caf6c48fd6358b"
integrity sha512-COb7LDz+SXaHtl/h4LeaFcNdJdAQSDeVqjiIihSXNrkWObZLhDI4hIkZC11Aeqp7bcE72clzB0BnDXr2SmslRA==
gl-preserve-state@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/gl-preserve-state/-/gl-preserve-state-1.0.0.tgz#4ef710d62873f1470ed015c6546c37dacddd4198"
@@ -15288,13 +15319,13 @@ prop-types-exact@^1.2.0:
object.assign "^4.1.0"
reflect.ownkeys "^0.2.0"
prop-types-extra@^1.0.1, prop-types-extra@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.0.tgz#32609910ea2dcf190366bacd3490d5a6412a605f"
integrity sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==
prop-types-extra@^1.0.1, prop-types-extra@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b"
integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==
dependencies:
react-is "^16.3.2"
warning "^3.0.0"
warning "^4.0.0"
prop-types@15.6.2:
version "15.6.2"
@@ -15581,27 +15612,15 @@ re-resizable@^4.11.0:
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-4.11.0.tgz#d5df10bda445c4ec0945751a223bf195afb61890"
integrity sha512-dye+7rERqNf/6mDT1iwps+4Gf42420xuZgygF33uX178DxffqcyeuHbBuJ382FIcB5iP6mMZOhfW7kI0uXwb/Q==
react-bootstrap-modal@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/react-bootstrap-modal/-/react-bootstrap-modal-4.2.0.tgz#010b3ca9230a3c147fa5fc6aa39b6971e34151bd"
integrity sha512-2Xx9yvPEnMNhJIAKCg+t3CECF6w3qt2mmICv1Y1vwDBHeuyyf3OvSFcnRM8E0Tn7pywgopL2qyU4yx0vnGKmdA==
dependencies:
classnames "^2.2.6"
dom-helpers "^3.3.1"
prop-types "^15.6.1"
prop-types-extra "^1.1.0"
react-overlays "^0.8.0"
react-transition-group "^2.0.0"
react-codemirror2@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-6.0.0.tgz#180065df57a64026026cde569a9708fdf7656525"
integrity sha512-D7y9qZ05FbUh9blqECaJMdDwKluQiO3A9xB+fssd5jKM7YAXucRuEOlX32mJQumUvHUkHRHqXIPBjm6g0FW0Ag==
react-cornerstone-viewport@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-2.3.8.tgz#7af8360f29bca986ae4e36b4e503269b88ddc52f"
integrity sha512-aiG2uVNrDY6SQx4t/HBxIA3zsMsCwT+6TpcXK9qSSoXhs+X6OTmYEKncWUqL0jtxU1yfh6JTUz8ARTg03gtF+A==
react-cornerstone-viewport@2.3.9:
version "2.3.9"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-2.3.9.tgz#f9761da8e536f0a217137c6ca1a983f5882249f9"
integrity sha512-qrhq8CbX/jq6b93cQjV2qC/mhHOvFBpxzxcHlvHzEQZt/rmRMcaYxCqjpNaNbUmmBu61wNkxesUVsggkPTTcqg==
dependencies:
classnames "^2.2.6"
date-fns "^2.2.1"
@@ -15791,6 +15810,13 @@ react-dropzone@^10.1.7:
file-selector "^0.1.11"
prop-types "^15.7.2"
react-error-boundary@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-2.2.1.tgz#32ed74386a90482060cc2fea948bb7135465c4cb"
integrity sha512-8SZMkJRFUb0JuluHKwuUtkh5vvVWBg3O/bJIWgNMhMJv529aG//TmcphR3cSMhCWjz1vFZDb4taJd6pmwP5mEQ==
dependencies:
"@babel/runtime" "^7.9.6"
react-error-overlay@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.1.tgz#417addb0814a90f3a7082eacba7cee588d00da89"
@@ -15847,7 +15873,12 @@ react-input-autosize@^2.2.2:
dependencies:
prop-types "^15.5.8"
react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0:
react-is@^16.3.2:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0:
version "16.11.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa"
integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==
@@ -16004,7 +16035,19 @@ react-simple-code-editor@^0.9.0:
resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.9.15.tgz#59e3583832e9e98992d3674b2d7673b4cd1c5709"
integrity sha512-M8iKgjBTBZK92tZYgOEfMuR7c3zZ0q0v3QYllSxIPx3SU+w003VofH50txXQSBTu92pSOm2tidON1HbQ1l8BDA==
react-transition-group@^2.0.0, react-transition-group@^2.2.0, react-transition-group@^2.2.1:
react-transition-group@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.0.0.tgz#e882e61fce8622ed981b6e31fc7ef52d43a1a06a"
integrity sha1-6ILmH86GIu2YG24x/H71LUOhoGo=
dependencies:
chain-function "^1.0.0"
classnames "^2.2.5"
dom-helpers "^3.2.0"
loose-envify "^1.3.1"
prop-types "^15.5.8"
warning "^3.0.0"
react-transition-group@^2.2.0, react-transition-group@^2.2.1:
version "2.9.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==
@@ -16024,10 +16067,10 @@ react-transition-group@^4.1.1:
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-vtkjs-viewport@^0.8.3:
version "0.8.4"
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.8.4.tgz#6bd9837d7762b845e77f053fb8fbf4469428d594"
integrity sha512-b90ENJzmStiahxVox+7mMwZij3NVUu//jE07PV2Qtp6E7q/eLXn6ZUwcEt9w5YHN36xLZVcx9b53ztLcXVgh/Q==
react-vtkjs-viewport@^0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/react-vtkjs-viewport/-/react-vtkjs-viewport-0.9.0.tgz#5a7890643e511946f960db6b05142318be2dfe80"
integrity sha512-kjpNr1n+eW0nU736HH07IsSYQXphxfn/RBvKtpBer9tNsl+FyLH8h+uY8KWdv8kVBHkEoaH6/srxB0JBa8l+jA==
dependencies:
date-fns "^2.2.1"
gl-matrix "^3.1.0"
@@ -16390,6 +16433,11 @@ 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.12.0:
version "0.12.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
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"
@@ -19710,7 +19758,7 @@ warning@^3.0.0:
dependencies:
loose-envify "^1.0.0"
warning@^4.0.3:
warning@^4.0.0, warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==