Compare commits

..
Author SHA1 Message Date
ohif-bot b7ab88b0f1 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.8.5
 - @ohif/viewer@4.2.15
2020-08-05 09:48:08 +00:00
James Petts 57a30d7c34 [IDC-1956] - Remove loading %age which is always zero. (#1959)
* Remove loading percent.

* fix: 🐛 Remove % loading on cornerstone viewport
2020-08-05 11:46:42 +02:00
ohif-bot cd90665b0e chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.2.6
 - @ohif/viewer@4.2.14
2020-08-05 09:03:27 +00:00
James Petts 98c657231e IDC-1897 (#1940)
* show warnings for rt.

* Remove unneeded old code
2020-08-05 11:00:47 +02:00
ohif-bot 14da247318 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.6.9
 - @ohif/ui@1.5.3
 - @ohif/viewer@4.2.13
2020-07-23 07:44:17 +00:00
Erik Ziegler 6a77d6c778 chore: Use same config for branch deploys as for deploy previews (#1893) (#1916) 2020-07-23 09:42:42 +02:00
Sinan 8f1785446b docs: Update readme for end to end test commands (#1774) 2020-07-22 21:52:23 +02:00
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
90 changed files with 1782 additions and 498 deletions

No files matched your search

+13
View File
@@ -21,6 +21,19 @@ mv platform/viewer/dist/* .netlify/www/pwa -v
# Build && Move script output # Build && Move script output
# yarn run build:package # yarn run build:package
# Build && Move Docz Output
# Using local yarn install to prevent Gatsby from needing to access
# node_modules above the platform/ui folder
cd platform/ui
yarn install
yarn run build
cd ../..
mkdir -p ./.netlify/www/ui
mv platform/ui/.docz/dist/* .netlify/www/ui -v
# Cache all of the node_module dependencies in
# extensions, modules, and platform packages
yarn run lerna:cache
echo 'Nothing left to see here. Go home, folks.' echo 'Nothing left to see here. Go home, folks.'
# Build using react-scripts # Build using react-scripts
+1 -1
View File
@@ -122,7 +122,7 @@ window.config = {
``` ```
- Install the viewer: - Install the viewer:
`window.OHIFStandaloneViewer.installViewer(window.config);` `window.OHIFViewer.installViewer(window.config);`
This exact setup is demonstrated in this This exact setup is demonstrated in this
[CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our [CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our
+60
View File
@@ -3,6 +3,66 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.8.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.4...@ohif/extension-cornerstone@2.8.5) (2020-08-05)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [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) ## [2.7.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.2...@ohif/extension-cornerstone@2.7.3) (2020-05-12)
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-cornerstone", "name": "@ohif/extension-cornerstone",
"version": "2.7.3", "version": "2.8.5",
"description": "OHIF extension for Cornerstone", "description": "OHIF extension for Cornerstone",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -36,7 +36,7 @@
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "4.15.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.3", "dcmjs": "0.16.0",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
@@ -51,6 +51,6 @@
"classnames": "^2.2.6", "classnames": "^2.2.6",
"lodash.merge": "^4.6.2", "lodash.merge": "^4.6.2",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"react-cornerstone-viewport": "2.3.8" "react-cornerstone-viewport": "4.0.1"
} }
} }
@@ -4,7 +4,6 @@ import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
import OHIF from '@ohif/core'; import OHIF from '@ohif/core';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core'; import cornerstone from 'cornerstone-core';
import debounce from 'lodash.debounce';
const { StackManager } = OHIF.utils; 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 { displaySet } = this.props.viewportData;
const { StudyInstanceUID } = displaySet; const { StudyInstanceUID } = displaySet;
if (currentImageIdIndex > 0) { if (currentImageIdIndex > 0) {
this.props.onNewImage({ this.props.onNewImage({
StudyInstanceUID, StudyInstanceUID,
@@ -227,7 +227,7 @@ class OHIFCornerstoneViewport extends Component {
activeViewportIndex: viewportIndex, activeViewportIndex: viewportIndex,
}); });
} }
}, 700); };
return ( return (
<> <>
@@ -235,7 +235,8 @@ class OHIFCornerstoneViewport extends Component {
viewportIndex={viewportIndex} viewportIndex={viewportIndex}
imageIds={imageIds} imageIds={imageIds}
imageIdIndex={currentImageIdIndex} imageIdIndex={currentImageIdIndex}
onNewImage={debouncedNewImageHandler} onNewImage={newImageHandler}
onNewImageDebounceTime={700}
// ~~ Connected (From REDUX) // ~~ Connected (From REDUX)
// frameRate={frameRate} // frameRate={frameRate}
// isPlaying={false} // isPlaying={false}
+19 -11
View File
@@ -10,6 +10,14 @@ const scroll = cornerstoneTools.import('util/scroll');
const { studyMetadataManager } = OHIF.utils; const { studyMetadataManager } = OHIF.utils;
const { setViewportSpecificData } = OHIF.redux.actions; const { setViewportSpecificData } = OHIF.redux.actions;
const refreshCornerstoneViewports = () => {
cornerstone.getEnabledElements().forEach(enabledElement => {
if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
});
};
const commandsModule = ({ servicesManager }) => { const commandsModule = ({ servicesManager }) => {
const actions = { const actions = {
rotateViewport: ({ viewports, rotation }) => { rotateViewport: ({ viewports, rotation }) => {
@@ -180,10 +188,7 @@ const commandsModule = ({ servicesManager }) => {
measurementApi.syncMeasurementsAndToolData(); measurementApi.syncMeasurementsAndToolData();
// Update images in all active viewports refreshCornerstoneViewports();
cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element);
});
}, },
getNearbyToolData({ element, canvasCoordinates, availableToolTypes }) { getNearbyToolData({ element, canvasCoordinates, availableToolTypes }) {
const nearbyTool = {}; const nearbyTool = {};
@@ -262,23 +267,26 @@ const commandsModule = ({ servicesManager }) => {
StudyInstanceUID, StudyInstanceUID,
SOPInstanceUID, SOPInstanceUID,
frameIndex, frameIndex,
activeViewportIndex activeViewportIndex,
}) => { }) => {
const study = studyMetadataManager.get(StudyInstanceUID); const study = studyMetadataManager.get(StudyInstanceUID);
const displaySet = study.findDisplaySet(ds => { 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.SOPInstanceUID = SOPInstanceUID;
displaySet.frameIndex = frameIndex; displaySet.frameIndex = frameIndex;
window.store.dispatch(setViewportSpecificData(activeViewportIndex, displaySet)); window.store.dispatch(
setViewportSpecificData(activeViewportIndex, displaySet)
);
cornerstone.getEnabledElements().forEach(enabledElement => { refreshCornerstoneViewports();
cornerstone.updateImage(enabledElement.element); },
});
}
}; };
const definitions = { const definitions = {
+3 -1
View File
@@ -39,7 +39,9 @@ export default {
const onNewImageHandler = jumpData => { const onNewImageHandler = jumpData => {
commandsManager.runCommand('jumpToImage', jumpData); commandsManager.runCommand('jumpToImage', jumpData);
}; };
return <OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />; return (
<OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />
);
}; };
return ExtendedOHIFCornerstoneViewport; return ExtendedOHIFCornerstoneViewport;
+19
View File
@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 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) ## [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", "name": "@ohif/extension-dicom-html",
"version": "1.2.2", "version": "1.2.4",
"description": "OHIF extension for rendering structured reports to HTML", "description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -29,7 +29,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"dcmjs": "^0.12.3", "dcmjs": "0.16.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^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. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 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) ## [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 **Note:** Version bump only for package @ohif/extension-dicom-microscopy
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-microscopy", "name": "@ohif/extension-dicom-microscopy",
"version": "0.50.9", "version": "0.51.0",
"description": "OHIF extension for Dicom Microscopy", "description": "OHIF extension for Dicom Microscopy",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -92,8 +92,8 @@ class DicomMicroscopyViewport extends Component {
{this.state.error ? ( {this.state.error ? (
<h2>{JSON.stringify(this.state.error)}</h2> <h2>{JSON.stringify(this.state.error)}</h2>
) : ( ) : (
<div style={style} ref={this.container} /> <div style={style} ref={this.container} />
)} )}
</div> </div>
); );
} }
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 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) # [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", "name": "@ohif/extension-dicom-p10-downloader",
"version": "0.1.0", "version": "0.1.2",
"description": "OHIF extension for downloading DICOM P10 files", "description": "OHIF extension for downloading DICOM P10 files",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -1,16 +1,16 @@
import OHIF from '@ohif/core'; import OHIF from '@ohif/core';
import { import {
save, save,
upload,
getDicomWebClientFromContext, getDicomWebClientFromContext,
getStudyInstanceUIDFromStudies,
getSOPInstanceReferenceFromActiveViewport, getSOPInstanceReferenceFromActiveViewport,
getSOPInstanceReferencesFromViewports, getSOPInstanceReferencesFromViewports,
} from './utils'; } from './utils';
import _downloadAndZip from './downloadAndZip'; import _downloadAndZip, { downloadInstances } from './downloadAndZip';
const { const {
utils: { utils: { Queue },
Queue,
},
} = OHIF; } = OHIF;
export function getCommands(context) { export function getCommands(context) {
@@ -41,6 +41,14 @@ export function getCommands(context) {
listOfUIDs 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 }) { downloadAndZipSeriesOnViewports({ servers, viewports, progress }) {
const dicomWebClient = getDicomWebClientFromContext(context, servers); const dicomWebClient = getDicomWebClientFromContext(context, servers);
const listOfUIDs = getSOPInstanceReferencesFromViewports(viewports); const listOfUIDs = getSOPInstanceReferencesFromViewports(viewports);
@@ -57,6 +65,28 @@ export function getCommands(context) {
listOfUIDs 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 = { const definitions = {
@@ -64,6 +94,11 @@ export function getCommands(context) {
commandFn: queue.bindSafe(actions.downloadAndZip, error), commandFn: queue.bindSafe(actions.downloadAndZip, error),
storeContexts: ['servers'], storeContexts: ['servers'],
}, },
downloadAndZipStudy: {
commandFn: queue.bindSafe(actions.downloadAndZipStudy, error),
storeContexts: ['servers', 'studies'],
options: { progress },
},
downloadAndZipSeriesOnViewports: { downloadAndZipSeriesOnViewports: {
commandFn: queue.bindSafe(actions.downloadAndZipSeriesOnViewports, error), commandFn: queue.bindSafe(actions.downloadAndZipSeriesOnViewports, error),
storeContexts: ['servers', 'viewports'], storeContexts: ['servers', 'viewports'],
@@ -77,6 +112,11 @@ export function getCommands(context) {
storeContexts: ['servers', 'viewports'], storeContexts: ['servers', 'viewports'],
options: { progress }, options: { progress },
}, },
downloadAndUploadStudy: {
commandFn: queue.bindSafe(actions.downloadAndUploadStudy, error),
storeContexts: ['servers', 'studies'],
options: { progress },
},
}; };
return { return {
@@ -79,13 +79,7 @@ async function downloadAndZip(dicomWebClient, listOfUIDs, options) {
if (dicomWebClient instanceof api.DICOMwebClient) { if (dicomWebClient instanceof api.DICOMwebClient) {
const settings = buildSettings(listOfUIDs, options); const settings = buildSettings(listOfUIDs, options);
const { compression } = settings.tasks; const { compression } = settings.tasks;
// Register user-provided progress handler as a task list observer const buffers = await downloadBuffers(settings, dicomWebClient);
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;
});
compression.deferred.resolve(zipAll(buffers, settings)); compression.deferred.resolve(zipAll(buffers, settings));
const url = await compression.deferred.promise; const url = await compression.deferred.promise;
return url; return url;
@@ -93,6 +87,27 @@ async function downloadAndZip(dicomWebClient, listOfUIDs, options) {
throw new Error('A valid DICOM Web Client instance is expected'); 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 * Utils
*/ */
@@ -241,4 +256,4 @@ async function download(
* Exports * 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 { export {
save, save,
upload,
validDicomUid, validDicomUid,
getDicomWebClientFromConfig, getDicomWebClientFromConfig,
getDicomWebClientFromContext, getDicomWebClientFromContext,
getStudyInstanceUIDFromStudies,
getSOPInstanceReferenceFromActiveViewport, getSOPInstanceReferenceFromActiveViewport,
getSOPInstanceReferencesFromViewports, getSOPInstanceReferencesFromViewports,
}; };
+79
View File
@@ -3,6 +3,85 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.5...@ohif/extension-dicom-rt@0.2.6) (2020-08-05)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [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) ## [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)
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-rt", "name": "@ohif/extension-dicom-rt",
"version": "0.1.3", "version": "0.2.6",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.", "description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "4.15.1",
"dcmjs": "^0.12.3", "dcmjs": "0.16.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6" "react-dom": "^16.8.6"
@@ -14,7 +14,7 @@ const OHIFDicomRTStructSopClassHandler = {
id: 'OHIFDicomRTStructSopClassHandler', id: 'OHIFDicomRTStructSopClassHandler',
type: MODULE_TYPES.SOP_CLASS_HANDLER, type: MODULE_TYPES.SOP_CLASS_HANDLER,
sopClassUIDs, sopClassUIDs,
getDisplaySetFromSeries: function ( getDisplaySetFromSeries: function(
series, series,
study, study,
dicomWebClient, dicomWebClient,
@@ -67,13 +67,14 @@ const OHIFDicomRTStructSopClassHandler = {
} }
} }
rtStructDisplaySet.load = function (referencedDisplaySet, studies) { rtStructDisplaySet.load = function(referencedDisplaySet, studies) {
return loadRTStruct( return loadRTStruct(
rtStructDisplaySet, rtStructDisplaySet,
referencedDisplaySet, referencedDisplaySet,
studies studies
).catch(error => { ).catch(error => {
rtStructDisplaySet.isLoaded = false; rtStructDisplaySet.isLoaded = false;
rtStructDisplaySet.loadError = true;
throw new Error(error); throw new Error(error);
}); });
}; };
@@ -17,7 +17,9 @@ const { studyMetadataManager } = utils;
const refreshViewport = () => { const refreshViewport = () => {
cornerstone.getEnabledElements().forEach(enabledElement => { cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element); if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
}); });
}; };
@@ -31,7 +33,18 @@ const refreshViewport = () => {
* @param {number} props.isOpen - isOpen * @param {number} props.isOpen - isOpen
* @returns component * @returns component
*/ */
const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }) => { const RTPanel = ({
studies,
viewports,
activeIndex,
isOpen,
onContourItemClick,
activeContexts = [],
contexts = {},
}) => {
const isVTK = () => activeContexts.includes(contexts.VTK);
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
const [selectedContour, setSelectedContour] = useState(); const [selectedContour, setSelectedContour] = useState();
const DEFAULT_SET_INDEX = 0; const DEFAULT_SET_INDEX = 0;
const DEFAULT_STATE = { const DEFAULT_STATE = {
@@ -65,7 +78,10 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
document.addEventListener('extensiondicomrtrtloaded', updateStructureSets); document.addEventListener('extensiondicomrtrtloaded', updateStructureSets);
return () => { return () => {
document.removeEventListener('extensiondicomrtrtloaded', updateStructureSets); document.removeEventListener(
'extensiondicomrtrtloaded',
updateStructureSets
);
}; };
}, []); }, []);
@@ -78,7 +94,9 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
activeViewport.SeriesInstanceUID activeViewport.SeriesInstanceUID
); );
const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID); const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID
);
const referencedDisplaysets = studyMetadata.getDerivedDatasets({ const referencedDisplaysets = studyMetadata.getDerivedDatasets({
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID, referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
Modality: 'RTSTRUCT', Modality: 'RTSTRUCT',
@@ -89,7 +107,7 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
setState({ setState({
referencedDisplaysets, referencedDisplaysets,
selectedSet: defaultSet, selectedSet: defaultSet,
sets: viewportSets sets: viewportSets,
}); });
} else { } else {
setState(DEFAULT_STATE); setState(DEFAULT_STATE);
@@ -105,7 +123,10 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
setShowSettings(showSettings && !isOpen); setShowSettings(showSettings && !isOpen);
}, [isOpen]); }, [isOpen]);
const toContourItem = ({ ROINumber, ROIName, RTROIObservations, colorArray, visible }, loadedSet) => { const toContourItem = (
{ ROINumber, ROIName, RTROIObservations, colorArray, visible, isSupported },
loadedSet
) => {
let interpretedType = ''; let interpretedType = '';
if (RTROIObservations && RTROIObservations.RTROIInterpretedType) { if (RTROIObservations && RTROIObservations.RTROIInterpretedType) {
interpretedType = `(${RTROIObservations.RTROIInterpretedType})`; interpretedType = `(${RTROIObservations.RTROIInterpretedType})`;
@@ -116,36 +137,45 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
<StructureSetItem <StructureSetItem
key={ROINumber} key={ROINumber}
selected={isSameContour} selected={isSameContour}
isDisabled={!isSupported}
onClick={() => { onClick={() => {
setSelectedContour(isSameContour ? null : ROINumber); setSelectedContour(isSameContour ? null : ROINumber);
const enabledElements = cornerstone.getEnabledElements(); if (isCornerstone()) {
const element = enabledElements[activeIndex].element; const enabledElements = cornerstone.getEnabledElements();
const toolState = cornerstoneTools.getToolState(element, 'stack'); const element = enabledElements[activeIndex].element;
const toolState = cornerstoneTools.getToolState(element, 'stack');
if (!toolState) { if (!toolState) {
return; return;
}
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,
});
} }
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}`} label={`${ROIName} ${interpretedType}`}
index={ROINumber} index={ROINumber}
@@ -153,7 +183,10 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
visible={visible} visible={visible}
onVisibilityChange={() => { onVisibilityChange={() => {
const module = cornerstoneTools.getModule('rtstruct'); const module = cornerstoneTools.getModule('rtstruct');
module.setters.toggleROIContour(state.selectedSet.SeriesInstanceUID, ROINumber); module.setters.toggleROIContour(
state.selectedSet.SeriesInstanceUID,
ROINumber
);
}} }}
/> />
); );
@@ -189,44 +222,70 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
onClick={() => setShowSettings(true)} onClick={() => setShowSettings(true)}
/> />
</div> </div>
{!state.referencedDisplaysets.length && <LoadingIndicator expand height="70px" width="70px" />} {!state.referencedDisplaysets.length && (
{state.referencedDisplaysets.map(displaySet => { <LoadingIndicator expand height="70px" width="70px" />
const { SeriesInstanceUID, metadata, isLoaded } = displaySet; )}
{state.sets &&
state.referencedDisplaysets.map(displaySet => {
const { SeriesInstanceUID, metadata, isLoaded } = displaySet;
const module = cornerstoneTools.getModule('rtstruct'); const module = cornerstoneTools.getModule('rtstruct');
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(viewports[activeIndex].SeriesInstanceUID); const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
viewports[activeIndex].SeriesInstanceUID
);
const loadedSet = sets.find(set => set.SeriesInstanceUID === SeriesInstanceUID); const loadedSet = sets.find(
return ( set => set.SeriesInstanceUID === SeriesInstanceUID
<PanelSection );
key={SeriesInstanceUID} return (
title={metadata.StructureSetLabel} <PanelSection
loading={!isLoaded || !loadedSet} key={SeriesInstanceUID}
visible={isLoaded && loadedSet.visible} title={metadata.StructureSetLabel}
hideVisibleButton={!isLoaded} loading={!isLoaded || !loadedSet}
expanded={isLoaded && loadedSet.SeriesInstanceUID === state.selectedSet.SeriesInstanceUID} visible={isLoaded && loadedSet.visible}
onVisibilityChange={() => { hideVisibleButton={!isLoaded}
const module = cornerstoneTools.getModule('rtstruct'); expanded={
module.setters.toggleStructureSet(SeriesInstanceUID); isLoaded &&
}} loadedSet.SeriesInstanceUID ===
onExpandChange={async () => { state.selectedSet.SeriesInstanceUID
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 }));
} }
}} onVisibilityChange={newVisibility => {
> const module = cornerstoneTools.getModule('rtstruct');
<ScrollableArea>
<TableList headless> if (newVisibility) {
{isLoaded && loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))} module.setters.showStructureSet(loadedSet.SeriesInstanceUID);
</TableList> } else {
</ScrollableArea> module.setters.hideStructureSet(loadedSet.SeriesInstanceUID);
</PanelSection> }
); 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> </div>
); );
}; };
@@ -22,11 +22,6 @@
width: 100%; width: 100%;
} }
.dcmrt-structure-set-item.selected .item-actions {
height: 35px;
visibility: visible;
}
.dcmrt-structure-set-item .item-actions { .dcmrt-structure-set-item .item-actions {
margin-left: -1px; margin-left: -1px;
background-color: var(--ui-gray-darker); background-color: var(--ui-gray-darker);
@@ -47,7 +42,8 @@
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.dcmrt-structure-set-item .item-actions .btnAction:hover, .dcmrt-structure-set-item .item-actions .btnAction:active { .dcmrt-structure-set-item .item-actions .btnAction:hover,
.dcmrt-structure-set-item .item-actions .btnAction:active {
color: var(--text-primary-color); color: var(--text-primary-color);
} }
@@ -86,3 +82,12 @@
.dcmrt-structure-set-item .item-label .eye-icon.--visible { .dcmrt-structure-set-item .item-label .eye-icon.--visible {
color: var(--default-color); color: var(--default-color);
} }
.dcmrt-structure-set-item.isDisabled .item-color-section {
background-color: #e29e4a;
color: #fff;
}
.dcmrt-structure-set-item.isDisabled .item-label {
color: var(--text-disabled-color);
}
@@ -1,6 +1,6 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { TableListItem, Icon } from '@ohif/ui'; import { TableListItem, Icon, Tooltip, OverlayTrigger } from '@ohif/ui';
import './StructureSetItem.css'; import './StructureSetItem.css';
@@ -20,6 +20,7 @@ ColoredCircle.propTypes = {
const StructureSetItem = ({ const StructureSetItem = ({
index, index,
label, label,
isDisabled,
onClick, onClick,
itemClass, itemClass,
color, color,
@@ -28,20 +29,47 @@ const StructureSetItem = ({
selected = false, selected = false,
}) => { }) => {
const [isVisible, setIsVisible] = useState(visible); const [isVisible, setIsVisible] = useState(visible);
return (
<div className={`dcmrt-structure-set-item ${selected && 'selected'}`}> useEffect(() => {
<TableListItem setIsVisible(visible);
key={index} }, [visible]);
itemKey={index}
itemIndex={index} let dcmrtClassNames = `dcmrt-structure-set-item`;
itemClass={itemClass}
itemMeta={<ColoredCircle color={color} />} if (selected) {
itemMetaClass="item-color-section" dcmrtClassNames += ' selected';
onItemClick={onClick} }
>
<div> if (isDisabled) {
<div className="item-label" style={{ marginBottom: 4 }}> dcmrtClassNames += ' isDisabled';
<span>{label}</span> }
const warningIcon = (
<span>
<Icon name="exclamation-triangle" />
</span>
);
const tableListItem = (
<TableListItem
key={index}
itemKey={index}
itemIndex={index}
itemClass={itemClass}
itemMeta={isDisabled ? warningIcon : <ColoredCircle color={color} />}
itemMetaClass="item-color-section"
onItemClick={() => {
if (isDisabled) {
return;
}
onClick();
}}
>
<div>
<div className="item-label" style={{ marginBottom: 4 }}>
<span>{label}</span>
{!isDisabled && (
<Icon <Icon
className={`eye-icon ${isVisible && '--visible'}`} className={`eye-icon ${isVisible && '--visible'}`}
name={isVisible ? 'eye' : 'eye-closed'} name={isVisible ? 'eye' : 'eye-closed'}
@@ -49,37 +77,72 @@ const StructureSetItem = ({
height="20px" height="20px"
onClick={event => { onClick={event => {
event.stopPropagation(); event.stopPropagation();
if (isDisabled) {
return;
}
const newVisibility = !isVisible; const newVisibility = !isVisible;
setIsVisible(newVisibility); setIsVisible(newVisibility);
onVisibilityChange(newVisibility); onVisibilityChange(newVisibility);
}} }}
/> />
</div>
{false && <div className="item-info">{'...'}</div>}
{false && (
<div className="item-actions">
<button
className="btnAction"
onClick={() => console.log('Relabelling...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Relabel
</button>
<button
className="btnAction"
onClick={() => console.log('Editing description...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Description
</button>
</div>
)} )}
</div> </div>
</TableListItem>
{false && <div className="item-info">{'...'}</div>}
{false && (
<div className="item-actions">
<button
className="btnAction"
onClick={() => console.log('Relabelling...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Relabel
</button>
<button
className="btnAction"
onClick={() => console.log('Editing description...')}
>
<span style={{ marginRight: '4px' }}>
<Icon name="edit" width="14px" height="14px" />
</span>
Description
</button>
</div>
)}
</div>
</TableListItem>
);
return (
<div className={dcmrtClassNames}>
<React.Fragment>
{isDisabled ? (
<OverlayTrigger
key={index}
placement="left"
overlay={
<Tooltip
placement="left"
className="in tooltip-warning"
id="tooltip-left"
>
<div className="warningTitle">Unsupported Region</div>
<div className="warningContent">
Contour type currently unsupported.
</div>
</Tooltip>
}
>
<div>{tableListItem}</div>
</OverlayTrigger>
) : (
<React.Fragment>{tableListItem} </React.Fragment>
)}
</React.Fragment>
</div> </div>
); );
}; };
@@ -94,7 +157,7 @@ StructureSetItem.propTypes = {
StructureSetItem.defaultProps = { StructureSetItem.defaultProps = {
itemClass: '', itemClass: '',
onClick: () => { }, onClick: () => {},
}; };
export default StructureSetItem; export default StructureSetItem;
+9 -2
View File
@@ -20,14 +20,21 @@ export default {
preRegistration({ servicesManager, configuration = {} }) { preRegistration({ servicesManager, configuration = {} }) {
init({ servicesManager, configuration }); init({ servicesManager, configuration });
}, },
getPanelModule({ commandsManager }) { getPanelModule({ commandsManager, servicesManager, api }) {
const ExtendedRTPanel = props => { const ExtendedRTPanel = props => {
const { activeContexts } = api.hooks.useAppContext();
const contourItemClickHandler = contourData => { const contourItemClickHandler = contourData => {
commandsManager.runCommand('jumpToImage', contourData); commandsManager.runCommand('jumpToImage', contourData);
}; };
return ( return (
<RTPanel {...props} onContourItemClick={contourItemClickHandler} /> <RTPanel
{...props}
onContourItemClick={contourItemClickHandler}
activeContexts={activeContexts}
contexts={api.contexts}
/>
); );
}; };
+19 -12
View File
@@ -16,7 +16,6 @@ export default async function loadRTStruct(
studies studies
) { ) {
const rtStructModule = cornerstoneTools.getModule('rtstruct'); const rtStructModule = cornerstoneTools.getModule('rtstruct');
// Set here is loading is asynchronous. // Set here is loading is asynchronous.
// If this function throws its set back to false. // If this function throws its set back to false.
rtStructDisplaySet.isLoaded = true; rtStructDisplaySet.isLoaded = true;
@@ -68,7 +67,6 @@ export default async function loadRTStruct(
const rtStructDisplayToolName = TOOL_NAMES.RTSTRUCT_DISPLAY_TOOL; const rtStructDisplayToolName = TOOL_NAMES.RTSTRUCT_DISPLAY_TOOL;
for (let i = 0; i < ROIContourSequence.length; i++) { for (let i = 0; i < ROIContourSequence.length; i++) {
const ROIContour = ROIContourSequence[i]; const ROIContour = ROIContourSequence[i];
const { ReferencedROINumber, ContourSequence } = ROIContour; const { ReferencedROINumber, ContourSequence } = ROIContour;
@@ -76,12 +74,7 @@ export default async function loadRTStruct(
continue; continue;
} }
_setROIContourMetadata( const isSupported = false;
structureSet,
StructureSetROISequence,
RTROIObservationsSequence,
ROIContour
);
for (let c = 0; c < ContourSequence.length; c++) { for (let c = 0; c < ContourSequence.length; c++) {
const { const {
@@ -93,10 +86,12 @@ export default async function loadRTStruct(
if (ContourGeometricType !== 'CLOSED_PLANAR') { if (ContourGeometricType !== 'CLOSED_PLANAR') {
// TODO: Do we want to visualise types other than closed planar? // TODO: Do we want to visualise types other than closed planar?
// We could easily do open planar. // We could easily do open planar and point.
continue; continue;
} }
isSupported = true;
const sopInstanceUID = ContourImageSequence.ReferencedSOPInstanceUID; const sopInstanceUID = ContourImageSequence.ReferencedSOPInstanceUID;
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID); const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
const imageIdSpecificToolData = _getOrCreateImageIdSpecificToolData( const imageIdSpecificToolData = _getOrCreateImageIdSpecificToolData(
@@ -128,6 +123,14 @@ export default async function loadRTStruct(
imageIdSpecificToolData.push(measurementData); imageIdSpecificToolData.push(measurementData);
} }
_setROIContourMetadata(
structureSet,
StructureSetROISequence,
RTROIObservationsSequence,
ROIContour,
isSupported
);
} }
_setToolEnabledIfNotEnabled(rtStructDisplayToolName); _setToolEnabledIfNotEnabled(rtStructDisplayToolName);
@@ -157,7 +160,8 @@ function _setROIContourMetadata(
structureSet, structureSet,
StructureSetROISequence, StructureSetROISequence,
RTROIObservationsSequence, RTROIObservationsSequence,
ROIContour ROIContour,
isSupported
) { ) {
const StructureSetROI = StructureSetROISequence.find( const StructureSetROI = StructureSetROISequence.find(
structureSetROI => structureSetROI =>
@@ -169,6 +173,7 @@ function _setROIContourMetadata(
ROIName: StructureSetROI.ROIName, ROIName: StructureSetROI.ROIName,
ROIGenerationAlgorithm: StructureSetROI.ROIGenerationAlgorithm, ROIGenerationAlgorithm: StructureSetROI.ROIGenerationAlgorithm,
ROIDescription: StructureSetROI.ROIDescription, ROIDescription: StructureSetROI.ROIDescription,
isSupported,
visible: true, visible: true,
}; };
@@ -238,7 +243,7 @@ function _setROIContourRTROIObservations(
function _setToolEnabledIfNotEnabled(toolName) { function _setToolEnabledIfNotEnabled(toolName) {
cornerstone.getEnabledElements().forEach(enabledElement => { cornerstone.getEnabledElements().forEach(enabledElement => {
const { element } = enabledElement; const { element, image } = enabledElement;
const tool = cornerstoneTools.getToolForElement(element, toolName); const tool = cornerstoneTools.getToolForElement(element, toolName);
if (tool.mode !== 'enabled') { if (tool.mode !== 'enabled') {
@@ -246,7 +251,9 @@ function _setToolEnabledIfNotEnabled(toolName) {
cornerstoneTools.setToolEnabled(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. // We should put this as a helper somewhere as we are using it in mutliple places.
function refreshViewport() { function refreshViewport() {
cornerstone.getEnabledElements().forEach(enabledElement => { 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); const StructureSet = getStructureSet(SeriesInstanceUID);
if (StructureSet) { if (StructureSet) {
StructureSet.visible = visible; StructureSet.ROIContours.forEach(ROIContour => {
ROIContour.visible = visible;
});
refreshViewport(); refreshViewport();
} }
@@ -154,15 +158,10 @@ function setToggleROIContour(SeriesInstanceUID, ROINumber) {
* Returns an array of StructureSets which reference the given SeriesInstanceUID. * Returns an array of StructureSets which reference the given SeriesInstanceUID.
* @param {string} SeriesInstanceUID The SeriesInstanceUID to check. * @param {string} SeriesInstanceUID The SeriesInstanceUID to check.
*/ */
function getStructuresSetsWhichReferenceSeriesInstanceUid( function getStructuresSetsWhichReferenceSeriesInstanceUid(SeriesInstanceUID) {
SeriesInstanceUID
) {
const { StructureSets } = state; const { StructureSets } = state;
return StructureSets.filter(StructureSet => return StructureSets.filter(StructureSet =>
structureSetReferencesSeriesInstanceUid( structureSetReferencesSeriesInstanceUid(StructureSet, SeriesInstanceUID)
StructureSet,
SeriesInstanceUID
)
); );
} }
@@ -3,6 +3,47 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 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) ## [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)
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-dicom-segmentation", "name": "@ohif/extension-dicom-segmentation",
"version": "0.2.6", "version": "0.3.3",
"description": "OHIF extension for viewing segmentations in the 2D MPR view", "description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0", "@ohif/core": "^0.50.0",
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "4.15.1",
"dcmjs": "^0.12.2", "dcmjs": "0.16.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6" "react-dom": "^16.8.6"
@@ -8,6 +8,11 @@
flex-direction: column; flex-direction: column;
} }
.dcmseg-segmentation-panel.disabled {
opacity: 0.6;
pointer-events: none;
}
.dcmseg-segmentation-panel h3 { .dcmseg-segmentation-panel h3 {
padding-left: 20px; padding-left: 20px;
margin-top: 0; margin-top: 0;
@@ -49,8 +49,8 @@ const SegmentationPanel = ({
onConfigurationChange, onConfigurationChange,
onDisplaySetLoadFailure, onDisplaySetLoadFailure,
onSelectedSegmentationChange, onSelectedSegmentationChange,
activeContexts, activeContexts = [],
contexts, contexts = {},
}) => { }) => {
const isVTK = () => activeContexts.includes(contexts.VTK); const isVTK = () => activeContexts.includes(contexts.VTK);
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE); const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
@@ -77,7 +77,8 @@ const SegmentationPanel = ({
labelmapList: [], labelmapList: [],
segmentList: [], segmentList: [],
cachedSegmentsProperties: [], cachedSegmentsProperties: [],
isLoading: false isLoading: false,
isDisabled: true
}); });
useEffect(() => { useEffect(() => {
@@ -139,39 +140,45 @@ const SegmentationPanel = ({
const refreshSegmentations = useCallback(() => { const refreshSegmentations = useCallback(() => {
const module = cornerstoneTools.getModule('segmentation'); const module = cornerstoneTools.getModule('segmentation');
const activeViewport = viewports[activeIndex]; const activeViewport = viewports[activeIndex];
const studyMetadata = studyMetadataManager.get(
activeViewport.StudyInstanceUID const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
); if (!isDisabled) {
const firstImageId = studyMetadata.getFirstImageId( const studyMetadata = studyMetadataManager.get(
activeViewport.displaySetInstanceUID activeViewport.StudyInstanceUID
);
const brushStackState = module.state.series[firstImageId];
if (brushStackState) {
const labelmap3D =
brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
const labelmapList = getLabelmapList(
brushStackState,
firstImageId,
activeViewport
); );
const segmentList = getSegmentList( const firstImageId = studyMetadata.getFirstImageId(
labelmap3D, activeViewport.displaySetInstanceUID
firstImageId,
brushStackState
); );
setState(state => ({ const brushStackState = module.state.series[firstImageId];
...state, if (brushStackState) {
brushStackState, const labelmap3D =
selectedSegmentation: brushStackState.activeLabelmapIndex, brushStackState.labelmaps3D[brushStackState.activeLabelmapIndex];
labelmapList, const labelmapList = getLabelmapList(
segmentList, brushStackState,
})); firstImageId,
} else { activeViewport
setState(state => ({ );
...state, const segmentList = getSegmentList(
labelmapList: [], labelmap3D,
segmentList: [], firstImageId,
})); brushStackState
);
setState(state => ({
...state,
brushStackState,
selectedSegmentation: brushStackState.activeLabelmapIndex,
labelmapList,
segmentList,
isDisabled
}));
} else {
setState(state => ({
...state,
labelmapList: [],
segmentList: [],
isDisabled
}));
}
} }
}, [ }, [
viewports, viewports,
@@ -181,7 +188,7 @@ const SegmentationPanel = ({
useEffect(() => { useEffect(() => {
refreshSegmentations(); refreshSegmentations();
}, [viewports, activeIndex, state.selectedSegmentation, activeContexts, state.isLoading]); }, [viewports, activeIndex, isOpen, state.selectedSegmentation, activeContexts, state.isLoading]);
/* Handle open/closed panel behaviour */ /* Handle open/closed panel behaviour */
useEffect(() => { useEffect(() => {
@@ -507,7 +514,7 @@ const SegmentationPanel = ({
); );
} else { } else {
return ( return (
<div className="dcmseg-segmentation-panel"> <div className={`dcmseg-segmentation-panel ${state.isDisabled && 'disabled'}`}>
<Icon <Icon
className="cog-icon" className="cog-icon"
name="cog" name="cog"
+3 -3
View File
@@ -24,7 +24,7 @@ export default {
return toolbarModule; return toolbarModule;
}, },
getPanelModule({ commandsManager, api, servicesManager }) { getPanelModule({ commandsManager, api, servicesManager }) {
const { UINotificationService } = servicesManager; const { UINotificationService } = servicesManager.services;
const ExtendedSegmentationPanel = props => { const ExtendedSegmentationPanel = props => {
const { activeContexts } = api.hooks.useAppContext(); const { activeContexts } = api.hooks.useAppContext();
@@ -46,7 +46,7 @@ export default {
const onSegmentVisibilityChangeHandler = (segmentNumber, visible) => { const onSegmentVisibilityChangeHandler = (segmentNumber, visible) => {
commandsManager.runCommand('setSegmentConfiguration', { commandsManager.runCommand('setSegmentConfiguration', {
segmentNumber, segmentNumber,
visible visible,
}); });
}; };
@@ -55,7 +55,7 @@ export default {
globalOpacity: configuration.fillAlpha, globalOpacity: configuration.fillAlpha,
outlineThickness: configuration.outlineWidth, outlineThickness: configuration.outlineWidth,
renderOutline: configuration.renderOutline, renderOutline: configuration.renderOutline,
visible: configuration.renderFill visible: configuration.renderFill,
}); });
}; };
@@ -41,6 +41,7 @@ export default async function loadSegmentation(
results = _parseSeg(segArrayBuffer, imageIds); results = _parseSeg(segArrayBuffer, imageIds);
} catch (error) { } catch (error) {
segDisplaySet.isLoaded = false; segDisplaySet.isLoaded = false;
segDisplaySet.loadError = true;
reject(error); reject(error);
} }
+94
View File
@@ -3,6 +3,100 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.6.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.6.8...@ohif/extension-vtk@1.6.9) (2020-07-23)
**Note:** Version bump only for package @ohif/extension-vtk
## [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) ## [1.5.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.5.4...@ohif/extension-vtk@1.5.5) (2020-05-12)
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/extension-vtk", "name": "@ohif/extension-vtk",
"version": "1.5.5", "version": "1.6.9",
"description": "OHIF extension for VTK.js", "description": "OHIF extension for VTK.js",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
@@ -35,7 +35,7 @@
"cornerstone-core": "^2.2.8", "cornerstone-core": "^2.2.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "4.15.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2", "dcmjs": "0.16.0",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"i18next": "^17.0.3", "i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1", "i18next-browser-languagedetector": "^3.0.1",
@@ -53,8 +53,8 @@
"react-vtkjs-viewport": "^0.9.0" "react-vtkjs-viewport": "^0.9.0"
}, },
"devDependencies": { "devDependencies": {
"@ohif/core": "^2.9.5", "@ohif/core": "^2.9.13",
"@ohif/ui": "^1.4.4", "@ohif/ui": "^1.5.3",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "4.15.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
+3 -1
View File
@@ -1,3 +1,4 @@
import React from 'react';
import asyncComponent from './asyncComponent.js'; import asyncComponent from './asyncComponent.js';
import commandsModule from './commandsModule.js'; import commandsModule from './commandsModule.js';
import toolbarModule from './toolbarModule.js'; import toolbarModule from './toolbarModule.js';
@@ -16,7 +17,8 @@ const vtkExtension = {
id: 'vtk', id: 'vtk',
getViewportModule({ commandsManager }) { getViewportModule({ commandsManager }) {
return withCommandsManager(OHIFVTKViewport, commandsManager); const ExtendedVTKViewport = props => <OHIFVTKViewport {...props} />;
return withCommandsManager(ExtendedVTKViewport, commandsManager);
}, },
getToolbarModule() { getToolbarModule() {
return toolbarModule; return toolbarModule;
+26
View File
@@ -0,0 +1,26 @@
#!/bin/sh
NODE_MODULES_CACHE="./node_modules"
LERNA_CACHE="$NODE_MODULES_CACHE/lerna-cache"
echo "Running netlify-lerna-cache.sh"
mkdir -p "$NODE_MODULES_CACHE/lerna-cache"
cache_deps() {
PACKAGES=$(ls -1 $1)
for PKG in $PACKAGES
do
PKG_NODE_MODULES="$1/$PKG/node_modules"
if [ -d $PKG_NODE_MODULES ];
then
mv $PKG_NODE_MODULES $LERNA_CACHE/$PKG
echo "Cached node modules for $PKG"
else
echo "Unable to cache node modules for $PKG"
fi
done
}
cache_deps platform
cache_deps extensions
cache_deps modes
+27
View File
@@ -0,0 +1,27 @@
#!/bin/sh
NODE_MODULES_CACHE="./node_modules"
LERNA_CACHE="$NODE_MODULES_CACHE/lerna-cache"
echo "Running netlify-lerna-restore.sh"
mkdir -p "$NODE_MODULES_CACHE/lerna-cache"
echo "$NODE_MODULES_CACHE/lerna-cache/*"
restore_deps() {
PACKAGES=$(ls -1 $1)
for PKG in $PACKAGES
do
PKG_CACHE="$LERNA_CACHE/$PKG"
if [ -d $PKG_CACHE ];
then
mv $PKG_CACHE $1/$PKG/node_modules
echo "Restored node modules for $PKG"
else
echo "Unable to restore cache for $PKG"
fi
done
}
restore_deps platform
restore_deps extensions
restore_deps modes
+6 -7
View File
@@ -8,6 +8,11 @@
# managed by CircleCI and deployed to our Google Hosting # managed by CircleCI and deployed to our Google Hosting
# #
[build]
base = ""
publish = ".netlify/www/"
command = "chmod +x .netlify/build-deploy-preview.sh && .netlify/build-deploy-preview.sh"
# NODE_VERSION in root `.nvmrc` takes priority # NODE_VERSION in root `.nvmrc` takes priority
# YARN_FLAGS: https://www.netlify.com/docs/build-gotchas/#yarn # YARN_FLAGS: https://www.netlify.com/docs/build-gotchas/#yarn
[build.environment] [build.environment]
@@ -16,19 +21,13 @@
YARN_VERSION = "1.17.3" YARN_VERSION = "1.17.3"
RUBY_VERSION = "2.6.2" RUBY_VERSION = "2.6.2"
YARN_FLAGS = "--no-ignore-optional --pure-lockfile" YARN_FLAGS = "--no-ignore-optional --pure-lockfile"
NETLIFY_RESTORE = "1"
# Production context: all deploys from the Production branch set in your site's # Production context: all deploys from the Production branch set in your site's
# deploy contexts will inherit these settings. # deploy contexts will inherit these settings.
[context.production] [context.production]
ignore = "exit 0" # Never build production; We'll let our CI do that ignore = "exit 0" # Never build production; We'll let our CI do that
# Deploy Preview context: all deploys generated from a pull/merge request will
# inherit these settings.
[context.deploy-preview]
base = ""
publish = ".netlify/www/"
command = "chmod +x .netlify/build-deploy-preview.sh && .netlify/build-deploy-preview.sh"
[[headers]] [[headers]]
# Define which paths this specific [[headers]] block will cover. # Define which paths this specific [[headers]] block will cover.
for = "/*" for = "/*"
+3
View File
@@ -34,6 +34,9 @@
"see-changed": "lerna changed", "see-changed": "lerna changed",
"docs:publish": "chmod +x ./build-and-publish-docs.sh && ./build-and-publish-docs.sh", "docs:publish": "chmod +x ./build-and-publish-docs.sh && ./build-and-publish-docs.sh",
"release": "yarn run lerna:version && yarn run lerna:publish", "release": "yarn run lerna:version && yarn run lerna:publish",
"lerna:cache": "[ $NETLIFY_RESTORE = 1 ] && ./netlify-lerna-cache.sh || :",
"lerna:restore": "[ $NETLIFY_RESTORE = 1 ] && ./netlify-lerna-restore.sh || :",
"preinstall": "yarn lerna:restore",
"lerna:version": "npx lerna version prerelease --force-publish", "lerna:version": "npx lerna version prerelease --force-publish",
"lerna:publish": "lerna publish from-package --canary --dist-tag canary", "lerna:publish": "lerna publish from-package --canary --dist-tag canary",
"link-list": "npm ls --depth=0 --link=true" "link-list": "npm ls --depth=0 --link=true"
+79
View File
@@ -3,6 +3,85 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 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) ## [2.9.5](https://github.com/OHIF/Viewers/compare/@ohif/core@2.9.4...@ohif/core@2.9.5) (2020-05-12)
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/core", "name": "@ohif/core",
"version": "2.9.5", "version": "2.9.13",
"description": "Generic business logic for web-based medical imaging applications", "description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team", "author": "OHIF Core Team",
"license": "MIT", "license": "MIT",
@@ -39,7 +39,7 @@
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"ajv": "^6.10.0", "ajv": "^6.10.0",
"dcmjs": "^0.12.2", "dcmjs": "0.16.0",
"dicomweb-client": "^0.6.0", "dicomweb-client": "^0.6.0",
"immer": "6.0.2", "immer": "6.0.2",
"isomorphic-base64": "^1.0.2", "isomorphic-base64": "^1.0.2",
@@ -13,7 +13,7 @@ const findMostRecentStructuredReport = studies => {
// Skip series that may not have instances yet // Skip series that may not have instances yet
// This can happen if we have retrieved just the initial // This can happen if we have retrieved just the initial
// details about the series via QIDO-RS, but not the full metadata // details about the series via QIDO-RS, but not the full metadata
if (!series.instances.length) { if (!series.instances || !series.instances.length) {
return; return;
} }
@@ -79,7 +79,7 @@ export class StudyMetadata extends Metadata {
Object.defineProperty(this, 'studyInstanceUID', { Object.defineProperty(this, 'studyInstanceUID', {
configurable: false, configurable: false,
enumerable: false, enumerable: false,
get: function () { get: function() {
return this.getStudyInstanceUID(); return this.getStudyInstanceUID();
}, },
}); });
@@ -135,6 +135,7 @@ export class StudyMetadata extends Metadata {
study, study,
sopClassUIDs sopClassUIDs
); );
if (displaySet) { if (displaySet) {
displaySet.sopClassModule = true; displaySet.sopClassModule = true;
@@ -215,6 +216,14 @@ export class StudyMetadata extends Metadata {
// TODO -> Get x Modality by referencedSeriesInstanceUid, FoR, etc. // 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. * Returns a list of derived datasets in the study, filtered by the given filter.
* @param {object} filter An object containing search filters * @param {object} filter An object containing search filters
@@ -263,7 +272,7 @@ export class StudyMetadata extends Metadata {
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter( filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
displaySet => displaySet =>
displaySet.ReferencedFrameOfReferenceUID === displaySet.ReferencedFrameOfReferenceUID ===
ReferencedFrameOfReferenceUID referencedFrameOfReferenceUID
); );
} }
@@ -251,7 +251,9 @@ export default class MeasurementApi {
this.syncMeasurementsAndToolData(); this.syncMeasurementsAndToolData();
cornerstone.getEnabledElements().forEach(enabledElement => { cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element); if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
}); });
// Let others know that the measurements are updated // 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 user from '../../user';
import getImageAttributes from '../lib/getImageAttributes'; import getImageAttributes from '../lib/getImageAttributes';
import getLabel from '../lib/getLabel'; import getLabel from '../lib/getLabel';
import refreshCornerstoneViewports from '../lib/refreshCornerstoneViewports';
export default function({ eventData, tool, toolGroupId, toolGroup }) { export default function({ eventData, tool, toolGroupId, toolGroup }) {
const measurementApi = MeasurementApi.Instance; 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 // TODO: This is very hacky, but will work for now
cornerstone.getEnabledElements().forEach(enabledElement => { refreshCornerstoneViewports();
cornerstone.updateImage(enabledElement.element);
});
// TODO: Notify about the last activated measurement // TODO: Notify about the last activated measurement
@@ -1,6 +1,7 @@
import cornerstone from 'cornerstone-core'; import cornerstone from 'cornerstone-core';
import { MeasurementApi } from '../classes'; import { MeasurementApi } from '../classes';
import log from '../../log'; import log from '../../log';
import refreshCornerstoneViewports from '../lib/refreshCornerstoneViewports';
export default function({ eventData, tool, toolGroupId, toolGroup }) { export default function({ eventData, tool, toolGroupId, toolGroup }) {
log.info('CornerstoneToolsMeasurementRemoved'); log.info('CornerstoneToolsMeasurementRemoved');
@@ -37,9 +38,7 @@ export default function({ eventData, tool, toolGroupId, toolGroup }) {
} }
// TODO: This is very hacky, but will work for now // TODO: This is very hacky, but will work for now
cornerstone.getEnabledElements().forEach(enabledElement => { refreshCornerstoneViewports();
cornerstone.updateImage(enabledElement.element);
});
if (MeasurementApi.isToolIncluded(tool)) { if (MeasurementApi.isToolIncluded(tool)) {
// TODO: Notify that viewer suffered changes // TODO: Notify that viewer suffered changes
@@ -4,6 +4,7 @@ import log from '../../log';
import user from '../../user'; import user from '../../user';
import getImageAttributes from '../lib/getImageAttributes'; import getImageAttributes from '../lib/getImageAttributes';
import getLabel from '../lib/getLabel'; import getLabel from '../lib/getLabel';
import refreshCornerstoneViewports from '../lib/refreshCornerstoneViewports';
export default function handleSingleMeasurementAdded({ eventData, tool }) { export default function handleSingleMeasurementAdded({ eventData, tool }) {
const measurementApi = MeasurementApi.Instance; const measurementApi = MeasurementApi.Instance;
@@ -39,9 +40,7 @@ export default function handleSingleMeasurementAdded({ eventData, tool }) {
} }
// TODO: This is very hacky, but will work for now // TODO: This is very hacky, but will work for now
cornerstone.getEnabledElements().forEach(enabledElement => { refreshCornerstoneViewports();
cornerstone.updateImage(enabledElement.element);
});
// TODO: Notify about the last activated measurement // TODO: Notify about the last activated measurement
@@ -1,6 +1,7 @@
import cornerstone from 'cornerstone-core'; import cornerstone from 'cornerstone-core';
import { MeasurementApi } from '../classes'; import { MeasurementApi } from '../classes';
import log from '../../log'; import log from '../../log';
import refreshCornerstoneViewports from '../lib/refreshCornerstoneViewports';
export default function handleSingleMeasurementRemoved({ export default function handleSingleMeasurementRemoved({
eventData, eventData,
@@ -35,9 +36,7 @@ export default function handleSingleMeasurementRemoved({
}); });
// TODO: This is very hacky, but will work for now // TODO: This is very hacky, but will work for now
cornerstone.getEnabledElements().forEach(enabledElement => { refreshCornerstoneViewports();
cornerstone.updateImage(enabledElement.element);
});
if (MeasurementApi.isToolIncluded(tool)) { if (MeasurementApi.isToolIncluded(tool)) {
// TODO: Notify that viewer suffered changes // TODO: Notify that viewer suffered changes
+4 -3
View File
@@ -6,11 +6,12 @@ const defaultState = {
const servers = (state = defaultState, action) => { const servers = (state = defaultState, action) => {
switch (action.type) { switch (action.type) {
case 'SET_STUDY_DATA': case 'SET_STUDY_DATA': {
const updatedStudyData = cloneDeep(state).studyData; const updatedStudyData = cloneDeep(state.studyData);
updatedStudyData[action.StudyInstanceUID] = action.data; updatedStudyData[action.StudyInstanceUID] = cloneDeep(action.data);
return Object.assign({}, state, { studyData: updatedStudyData }); return Object.assign({}, state, { studyData: updatedStudyData });
}
default: default:
return state; return state;
} }
@@ -28,13 +28,14 @@ const serviceImplementation = {
/** /**
* Show a new UI modal; * 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({ function _show({
content = null, content = null,
contentProps = null, contentProps = null,
shouldCloseOnEsc = false, shouldCloseOnEsc = false,
isOpen = true, isOpen = true,
onClose = null,
closeButton = true, closeButton = true,
title = null, title = null,
customClassName = null, customClassName = null,
@@ -44,6 +45,7 @@ function _show({
contentProps, contentProps,
shouldCloseOnEsc, shouldCloseOnEsc,
isOpen, isOpen,
onClose,
closeButton, closeButton,
title, title,
customClassName, customClassName,
@@ -82,15 +82,20 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
// For each type, see if any are loaded, if not load the most recent. // For each type, see if any are loaded, if not load the most recent.
Object.keys(displaySetsPerModality).forEach(key => { Object.keys(displaySetsPerModality).forEach(key => {
const displaySets = displaySetsPerModality[key]; const displaySets = displaySetsPerModality[key];
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded); const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
if (isLoaded) { if (isLoaded) {
return; return;
} }
if (displaySets.some(displaySet => displaySet.loadError)) {
return;
}
// find most recent and load it. // find most recent and load it.
let recentDateTime = 0; let recentDateTime = 0;
let recentDisplaySet; let recentDisplaySet = displaySets[0];
displaySets.forEach(displaySet => { displaySets.forEach(displaySet => {
const dateTime = Number( const dateTime = Number(
@@ -102,6 +107,8 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
} }
}); });
recentDisplaySet.isLoading = true;
promises.push(recentDisplaySet.load(referencedDisplaySet, studies)); promises.push(recentDisplaySet.load(referencedDisplaySet, studies));
}); });
+38
View File
@@ -3,6 +3,44 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.5.3](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.5.2...@ohif/ui@1.5.3) (2020-07-23)
**Note:** Version bump only for package @ohif/ui
## [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) ## [1.4.4](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.4.3...@ohif/ui@1.4.4) (2020-05-04)
+25
View File
@@ -0,0 +1,25 @@
const path = require('path');
// ~~ Plugins
const PnpWebpackPlugin = require(`pnp-webpack-plugin`); // Required until Webpack@5
exports.onCreateWebpackConfig = args => {
args.actions.setWebpackConfig({
node: { fs: 'empty' },
resolve: {
plugins: [PnpWebpackPlugin],
// Note the '..' in the path because docz gatsby project lives in the '.docz' directory
modules: [
// platform/ui
path.resolve(__dirname, '../node_modules'),
// .docz
'node_modules',
],
// resolve: {
// symlinks: true,
// },
},
resolveLoader: {
plugins: [PnpWebpackPlugin.moduleLoader(module)],
},
});
};
+6 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/ui", "name": "@ohif/ui",
"version": "1.4.4", "version": "1.5.3",
"description": "A set of React components for Medical Imaging Viewers", "description": "A set of React components for Medical Imaging Viewers",
"author": "OHIF Contributors", "author": "OHIF Contributors",
"license": "MIT", "license": "MIT",
@@ -41,20 +41,24 @@
"@ohif/i18n": "^0.2.3", "@ohif/i18n": "^0.2.3",
"browser-detect": "^0.2.28", "browser-detect": "^0.2.28",
"classnames": "2.2.6", "classnames": "2.2.6",
"dom-helpers": "^3.3.1",
"i18next": "^17.0.3", "i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1", "i18next-browser-languagedetector": "^3.0.1",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"lodash.isequal": "4.5.0", "lodash.isequal": "4.5.0",
"moment": "2.24.0", "moment": "2.24.0",
"prop-types": "15.6.2", "prop-types": "15.6.2",
"react-bootstrap-modal": "4.2.0", "prop-types-extra": "^1.1.1",
"react-dates": "21.2.1", "react-dates": "21.2.1",
"react-dnd": "9.4.0", "react-dnd": "9.4.0",
"react-dnd-html5-backend": "^9.4.0", "react-dnd-html5-backend": "^9.4.0",
"react-dnd-touch-backend": "^9.4.0", "react-dnd-touch-backend": "^9.4.0",
"react-draggable": "^4.1.0", "react-draggable": "^4.1.0",
"react-error-boundary": "^2.2.1",
"react-i18next": "^10.11.0", "react-i18next": "^10.11.0",
"react-modal": "^3.11.1", "react-modal": "^3.11.1",
"react-overlays": "^0.8.0",
"react-transition-group": "2.0.0",
"react-with-direction": "1.3.0" "react-with-direction": "1.3.0"
}, },
"devDependencies": { "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 { TabComponents, TabFooter } from './tabComponents';
import { HotkeyField } from './customForm'; import { HotkeyField } from './customForm';
import { LanguageSwitcher } from './languageSwitcher'; import { LanguageSwitcher } from './languageSwitcher';
import { Checkbox } from './checkbox'; import { Checkbox } from './checkbox';
import { CineDialog } from './cineDialog'; import { CineDialog } from './cineDialog';
import { ViewportDownloadForm } from './content/viewportDownloadForm'; import { ViewportDownloadForm } from './content/viewportDownloadForm';
@@ -25,8 +24,10 @@ import {
} from './studyList'; } from './studyList';
import { ToolbarSection } from './toolbarSection'; import { ToolbarSection } from './toolbarSection';
import { Tooltip } from './tooltip'; import { Tooltip } from './tooltip';
import { ErrorBoundary } from './errorBoundary';
export { export {
ErrorBoundary,
ContextMenu, ContextMenu,
Checkbox, Checkbox,
CineDialog, CineDialog,
@@ -32,6 +32,7 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
contentProps: null, contentProps: null,
shouldCloseOnEsc: false, shouldCloseOnEsc: false,
isOpen: true, isOpen: true,
onClose: null,
closeButton: true, closeButton: true,
title: null, title: null,
customClassName: '', customClassName: '',
@@ -73,6 +74,7 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
content: ModalContent, content: ModalContent,
contentProps, contentProps,
isOpen, isOpen,
onClose,
title, title,
customClassName, customClassName,
shouldCloseOnEsc, shouldCloseOnEsc,
@@ -88,7 +90,13 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
isOpen={isOpen} isOpen={isOpen}
title={title} title={title}
closeButton={closeButton} closeButton={closeButton}
onClose={hide} onClose={() => {
if (onClose) {
onClose();
}
hide();
}}
> >
<ModalContent {...contentProps} show={show} hide={hide} /> <ModalContent {...contentProps} show={show} hide={hide} />
</Modal> </Modal>
@@ -51,3 +51,9 @@ button.close
position: relative; position: relative;
display: inline-block; display: inline-block;
vertical-align: middle; 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, Tooltip,
AboutContent, AboutContent,
OHIFModal, OHIFModal,
ErrorBoundary
} from './components'; } from './components';
import { useDebounce, useMedia } from './hooks'; import { useDebounce, useMedia } from './hooks';
@@ -123,6 +124,7 @@ export {
DialogProvider, DialogProvider,
withDialog, withDialog,
useDialog, useDialog,
ErrorBoundary,
// Hooks // Hooks
useDebounce, useDebounce,
useMedia, useMedia,
+193
View File
@@ -3,6 +3,199 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.2.15](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.14...@ohif/viewer@4.2.15) (2020-08-05)
**Note:** Version bump only for package @ohif/viewer
## [4.2.14](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.13...@ohif/viewer@4.2.14) (2020-08-05)
**Note:** Version bump only for package @ohif/viewer
## [4.2.13](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.2.12...@ohif/viewer@4.2.13) (2020-07-23)
**Note:** Version bump only for package @ohif/viewer
## [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) ## [3.11.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.11.8...@ohif/viewer@3.11.9) (2020-05-12)
+3 -3
View File
@@ -115,13 +115,13 @@ Commands to run the tests:
```js ```js
// Open Cypress Dashboard that provides insight into what happened when your tests ran // Open Cypress Dashboard that provides insight into what happened when your tests ran
yarn run cy yarn test:e2e
// Run all tests using Electron browser headless // Run all tests using Electron browser headless
yarn run cy:run yarn test:e2e:local
// Run all tests in CI mode // Run all tests in CI mode
yarn run cy:run:ci yarn run test:e2e:ci
``` ```
### Contributing ### Contributing
@@ -5,17 +5,18 @@ describe('OHIF Routes', function() {
cy.openStudyList(); cy.openStudyList();
}); });
it('checks TEST json url study route', function() { // TODO -> Bring back when testJSON is hosted again.
cy.visit( // it('checks TEST json url study route', function() {
'/viewer?url=https://ohif-viewer.s3.eu-central-1.amazonaws.com/JSON/testJSON.json' // cy.visit(
); // '/viewer?url=https://ohif-viewer.s3.eu-central-1.amazonaws.com/JSON/testJSON.json'
// );
cy.server(); // cy.server();
cy.route('GET', '**/MRStudy/**').as('getTESTStudy'); // cy.route('GET', '**/ MRStudy; /**').as('getTESTStudy');
cy.wait('@getTESTStudy.all'); // cy.wait('@getTESTStudy.all');
cy.get('@getTESTStudy').should($route => { // cy.get('@getTESTStudy').should($route => {
expect($route.status).to.be.eq(200); // expect($route.status).to.be.eq(200);
}); // });
}); // });
}); });
@@ -22,7 +22,7 @@ describe('OHIF Study List', function() {
//Wait result list to be displayed //Wait result list to be displayed
cy.waitStudyList(); cy.waitStudyList();
cy.get('@searchResult').should($list => { cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(2); expect($list.length).to.be.eq(3);
expect($list).to.contain('Juno'); expect($list).to.contain('Juno');
}); });
}); });
@@ -38,12 +38,12 @@ describe('OHIF Study List', function() {
}); });
it('searches Accession with exact string', function() { it('searches Accession with exact string', function() {
cy.get('@AccessionNumber').type('fpcben98890'); cy.get('@AccessionNumber').type('0000481914');
//Wait result list to be displayed //Wait result list to be displayed
cy.waitStudyList(); cy.waitStudyList();
cy.get('@searchResult').should($list => { cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(1); 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 //Wait result list to be displayed
cy.waitStudyList(); cy.waitStudyList();
cy.get('@searchResult').should($list => { 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'); expect($list).to.contain('MR');
}); });
}); });
@@ -127,7 +128,7 @@ describe('OHIF Study List', function() {
//Wait result list to be displayed //Wait result list to be displayed
cy.waitStudyList(); cy.waitStudyList();
cy.get('@searchResult').should($list => { cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(2); expect($list.length).to.be.eq(3);
expect($list).to.contain('Juno'); expect($list).to.contain('Juno');
}); });
}); });
@@ -137,7 +138,7 @@ describe('OHIF Study List', function() {
//Wait result list to be displayed //Wait result list to be displayed
cy.waitStudyList(); cy.waitStudyList();
cy.get('@searchResult').should($list => { 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'); expect($list).to.contain('ProstateX-0000');
}); });
}); });
@@ -147,18 +148,19 @@ describe('OHIF Study List', function() {
//Wait result list to be displayed //Wait result list to be displayed
cy.waitStudyList(); cy.waitStudyList();
cy.get('@searchResult').should($list => { 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'); expect($list).to.contain('MR');
}); });
}); });
it('searches Accession with exact string', function() { it('searches Accession with exact string', function() {
cy.get('@accessionModalityDescription').type('fpcben98890'); cy.get('@accessionModalityDescription').type('0000481914');
//Wait result list to be displayed //Wait result list to be displayed
cy.waitStudyList(); cy.waitStudyList();
cy.get('@searchResult').should($list => { cy.get('@searchResult').should($list => {
expect($list.length).to.be.eq(1); 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', () => { describe('OHIF Microscopy Extension', () => {
before(() => { before(() => {
cy.openStudyModality('SM'); cy.openStudyModality('SM');
cy.expectMinimumThumbnails(6); cy.expectMinimumThumbnails(2);
}); });
it('checks if series thumbnails are being displayed', () => { it('checks if series thumbnails are being displayed', () => {
@@ -5,13 +5,15 @@ describe('Visual Regression - OHIF Routes', function() {
cy.openStudyList(); cy.openStudyList();
}); });
/*
// TODO -> Bring back when testJSON is hosted again.
it('checks TEST json url study route', function() { it('checks TEST json url study route', function() {
cy.visit( cy.visit(
'/viewer?url=https://ohif-viewer.s3.eu-central-1.amazonaws.com/JSON/testJSON.json' '/viewer?url=https://ohif-viewer.s3.eu-central-1.amazonaws.com/JSON/testJSON.json'
); );
cy.server(); cy.server();
cy.route('GET', '**/TESTStudy/**').as('getTESTStudy'); cy.route('GET', '**/ TESTStudy; /**').as('getTESTStudy');
cy.wait('@getTESTStudy.all'); cy.wait('@getTESTStudy.all');
cy.get('@getTESTStudy').should($route => { cy.get('@getTESTStudy').should($route => {
@@ -20,4 +22,5 @@ describe('Visual Regression - OHIF Routes', function() {
cy.percyCanvasSnapshot('TEST json study route'); cy.percyCanvasSnapshot('TEST json study route');
}); });
*/
}); });
+1 -1
View File
@@ -109,7 +109,7 @@ Cypress.Commands.add('waitStudyList', () => {
Cypress.Commands.add('waitVTKLoading', () => { Cypress.Commands.add('waitVTKLoading', () => {
// Wait for start loading // Wait for start loading
cy.get('[data-cy="viewprt-grid"]', { timeout: 10000 }).should($grid => { cy.get('[data-cy="viewprt-grid"]', { timeout: 20000 }).should($grid => {
expect($grid).to.contain.text('Loading'); expect($grid).to.contain.text('Loading');
}); });
+11 -11
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ohif/viewer", "name": "@ohif/viewer",
"version": "3.11.9", "version": "4.2.15",
"description": "OHIF Viewer", "description": "OHIF Viewer",
"author": "OHIF Contributors", "author": "OHIF Contributors",
"license": "MIT", "license": "MIT",
@@ -48,18 +48,18 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"@ohif/core": "^2.9.5", "@ohif/core": "^2.9.13",
"@ohif/extension-cornerstone": "^2.7.3", "@ohif/extension-cornerstone": "^2.8.5",
"@ohif/extension-dicom-html": "^1.2.2", "@ohif/extension-dicom-html": "^1.2.4",
"@ohif/extension-dicom-microscopy": "^0.50.9", "@ohif/extension-dicom-microscopy": "^0.51.0",
"@ohif/extension-dicom-p10-downloader": "^0.1.0", "@ohif/extension-dicom-p10-downloader": "^0.1.2",
"@ohif/extension-dicom-pdf": "^1.0.3", "@ohif/extension-dicom-pdf": "^1.0.3",
"@ohif/extension-dicom-rt": "^0.1.3", "@ohif/extension-dicom-rt": "^0.2.6",
"@ohif/extension-dicom-segmentation": "^0.2.6", "@ohif/extension-dicom-segmentation": "^0.3.3",
"@ohif/extension-lesion-tracker": "^0.2.0", "@ohif/extension-lesion-tracker": "^0.2.0",
"@ohif/extension-vtk": "^1.5.5", "@ohif/extension-vtk": "^1.6.9",
"@ohif/i18n": "^0.52.8", "@ohif/i18n": "^0.52.8",
"@ohif/ui": "^1.4.4", "@ohif/ui": "^1.5.3",
"@tanem/react-nprogress": "^1.1.25", "@tanem/react-nprogress": "^1.1.25",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"core-js": "^3.2.1", "core-js": "^3.2.1",
@@ -67,7 +67,7 @@
"cornerstone-math": "^0.1.8", "cornerstone-math": "^0.1.8",
"cornerstone-tools": "4.15.1", "cornerstone-tools": "4.15.1",
"cornerstone-wado-image-loader": "^3.1.0", "cornerstone-wado-image-loader": "^3.1.0",
"dcmjs": "^0.12.2", "dcmjs": "0.16.0",
"dicom-parser": "^1.8.3", "dicom-parser": "^1.8.3",
"dicomweb-client": "^0.4.4", "dicomweb-client": "^0.4.4",
"hammerjs": "^2.0.8", "hammerjs": "^2.0.8",
+1
View File
@@ -15,6 +15,7 @@ window.config = {
imageRendering: 'wadors', imageRendering: 'wadors',
thumbnailRendering: 'wadors', thumbnailRendering: 'wadors',
enableStudyLazyLoad: true, enableStudyLazyLoad: true,
supportsFuzzyMatching: true,
}, },
], ],
}, },
+1 -2
View File
@@ -9,11 +9,10 @@ window.config = function(props) {
httpErrorHandler: error => { httpErrorHandler: error => {
// This is 429 when rejected from the public idc sandbox too often. // This is 429 when rejected from the public idc sandbox too often.
console.warn(error.status); console.warn(error.status);
// Could use services manager here to bring up a dialog/modal if needed. // Could use services manager here to bring up a dialog/modal if needed.
console.warn('test, navigate to https://ohif.org/'); console.warn('test, navigate to https://ohif.org/');
window.location = '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', imageRendering: 'wadors',
thumbnailRendering: 'wadors', thumbnailRendering: 'wadors',
enableStudyLazyLoad: true, enableStudyLazyLoad: true,
supportsFuzzyMatching: true,
}, },
], ],
}, },
@@ -11,6 +11,7 @@ window.config = {
qidoSupportsIncludeField: true, qidoSupportsIncludeField: true,
imageRendering: 'wadors', imageRendering: 'wadors',
thumbnailRendering: 'wadors', thumbnailRendering: 'wadors',
supportsFuzzyMatching: true,
}, },
], ],
}, },
+56 -43
View File
@@ -13,6 +13,7 @@ import {
ModalProvider, ModalProvider,
DialogProvider, DialogProvider,
OHIFModal, OHIFModal,
ErrorBoundary
} from '@ohif/ui'; } from '@ohif/ui';
import { import {
@@ -71,6 +72,14 @@ let extensionManager;
// TODO[react] Use a provider when the whole tree is React // TODO[react] Use a provider when the whole tree is React
window.store = store; window.store = store;
window.ohif = window.ohif || {};
window.ohif.app = {
commandsManager,
hotkeysManager,
servicesManager,
extensionManager,
};
class App extends Component { class App extends Component {
static propTypes = { static propTypes = {
config: PropTypes.oneOfType([ config: PropTypes.oneOfType([
@@ -159,53 +168,57 @@ class App extends Component {
if (this._userManager) { if (this._userManager) {
return ( return (
<Provider store={store}> <ErrorBoundary context='App'>
<AppProvider config={this._appConfig}> <Provider store={store}>
<I18nextProvider i18n={i18n}> <AppProvider config={this._appConfig}>
<OidcProvider store={store} userManager={this._userManager}> <I18nextProvider i18n={i18n}>
<UserManagerContext.Provider value={this._userManager}> <OidcProvider store={store} userManager={this._userManager}>
<Router basename={routerBasename}> <UserManagerContext.Provider value={this._userManager}>
<WhiteLabelingContext.Provider value={whiteLabeling}> <Router basename={routerBasename}>
<SnackbarProvider service={UINotificationService}> <WhiteLabelingContext.Provider value={whiteLabeling}>
<DialogProvider service={UIDialogService}> <SnackbarProvider service={UINotificationService}>
<ModalProvider <DialogProvider service={UIDialogService}>
modal={OHIFModal} <ModalProvider
service={UIModalService} modal={OHIFModal}
> service={UIModalService}
<OHIFStandaloneViewer >
userManager={this._userManager} <OHIFStandaloneViewer
/> userManager={this._userManager}
</ModalProvider> />
</DialogProvider> </ModalProvider>
</SnackbarProvider> </DialogProvider>
</WhiteLabelingContext.Provider> </SnackbarProvider>
</Router> </WhiteLabelingContext.Provider>
</UserManagerContext.Provider> </Router>
</OidcProvider> </UserManagerContext.Provider>
</I18nextProvider> </OidcProvider>
</AppProvider> </I18nextProvider>
</Provider> </AppProvider>
</Provider>
</ErrorBoundary>
); );
} }
return ( return (
<Provider store={store}> <ErrorBoundary context='App'>
<AppProvider config={this._appConfig}> <Provider store={store}>
<I18nextProvider i18n={i18n}> <AppProvider config={this._appConfig}>
<Router basename={routerBasename}> <I18nextProvider i18n={i18n}>
<WhiteLabelingContext.Provider value={whiteLabeling}> <Router basename={routerBasename}>
<SnackbarProvider service={UINotificationService}> <WhiteLabelingContext.Provider value={whiteLabeling}>
<DialogProvider service={UIDialogService}> <SnackbarProvider service={UINotificationService}>
<ModalProvider modal={OHIFModal} service={UIModalService}> <DialogProvider service={UIDialogService}>
<OHIFStandaloneViewer /> <ModalProvider modal={OHIFModal} service={UIModalService}>
</ModalProvider> <OHIFStandaloneViewer />
</DialogProvider> </ModalProvider>
</SnackbarProvider> </DialogProvider>
</WhiteLabelingContext.Provider> </SnackbarProvider>
</Router> </WhiteLabelingContext.Provider>
</I18nextProvider> </Router>
</AppProvider> </I18nextProvider>
</Provider> </AppProvider>
</Provider>
</ErrorBoundary>
); );
} }
+20 -6
View File
@@ -5,7 +5,7 @@ import { Route, Switch } from 'react-router-dom';
import { NProgress } from '@tanem/react-nprogress'; import { NProgress } from '@tanem/react-nprogress';
import { CSSTransition } from 'react-transition-group'; import { CSSTransition } from 'react-transition-group';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ViewerbaseDragDropContext } from '@ohif/ui'; import { ViewerbaseDragDropContext, ErrorBoundary } from '@ohif/ui';
import { SignoutCallbackComponent } from 'redux-oidc'; import { SignoutCallbackComponent } from 'redux-oidc';
import asyncComponent from './components/AsyncComponent.js'; import asyncComponent from './components/AsyncComponent.js';
import * as RoutesUtil from './routes/routesUtil'; import * as RoutesUtil from './routes/routesUtil';
@@ -52,10 +52,13 @@ class OHIFStandaloneViewer extends Component {
const { appConfig = {} } = this.context; const { appConfig = {} } = this.context;
const userNotLoggedIn = userManager && (!user || user.expired); const userNotLoggedIn = userManager && (!user || user.expired);
if (userNotLoggedIn) { if (userNotLoggedIn) {
const pathname = this.props.location.pathname; const { pathname, search } = this.props.location;
if (pathname !== '/callback') { if (pathname !== '/callback') {
sessionStorage.setItem('ohif-redirect-to', pathname); sessionStorage.setItem(
'ohif-redirect-to',
JSON.stringify({ pathname, search })
);
} }
return ( return (
@@ -103,12 +106,21 @@ class OHIFStandaloneViewer extends Component {
userManager.removeUser().then(() => { userManager.removeUser().then(() => {
if (targetLinkUri !== null) { if (targetLinkUri !== null) {
const ohifRedirectTo = {
pathname: new URL(targetLinkUri).pathname,
};
sessionStorage.setItem( sessionStorage.setItem(
'ohif-redirect-to', 'ohif-redirect-to',
new URL(targetLinkUri).pathname JSON.stringify(ohifRedirectTo)
); );
} else { } else {
sessionStorage.setItem('ohif-redirect-to', '/'); const ohifRedirectTo = {
pathname: '/',
};
sessionStorage.setItem(
'ohif-redirect-to',
JSON.stringify(ohifRedirectTo)
);
} }
if (loginHint !== null) { if (loginHint !== null) {
@@ -191,8 +203,10 @@ class OHIFStandaloneViewer extends Component {
{match === null ? ( {match === null ? (
<></> <></>
) : ( ) : (
<ErrorBoundary context={match.url}>
<Component match={match} location={this.props.location} /> <Component match={match} location={this.props.location} />
)} </ErrorBoundary>
)}
</CSSTransition> </CSSTransition>
)} )}
</Route> </Route>
@@ -329,7 +329,9 @@ const mapDispatchToProps = (dispatch, ownProps) => {
measurementApi.syncMeasurementsAndToolData(); measurementApi.syncMeasurementsAndToolData();
cornerstone.getEnabledElements().forEach(enabledElement => { cornerstone.getEnabledElements().forEach(enabledElement => {
cornerstone.updateImage(enabledElement.element); if (enabledElement.image) {
cornerstone.updateImage(enabledElement.element);
}
}); });
// Needs to update viewports.layout state to set layout // 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, studies,
viewportData, viewportData,
children, children,
isStudyLoaded,
} = props; } = props;
const rowSize = 100 / numRows; const rowSize = 100 / numRows;
@@ -37,21 +38,23 @@ const ViewportGrid = function(props) {
const snackbar = useSnackbarContext(); const snackbar = useSnackbarContext();
useEffect(() => { useEffect(() => {
viewportData.forEach(displaySet => { if (isStudyLoaded) {
const promises = loadAndCacheDerivedDisplaySets(displaySet, studies); viewportData.forEach(displaySet => {
const promises = loadAndCacheDerivedDisplaySets(displaySet, studies);
promises.forEach(promise => { promises.forEach(promise => {
promise.catch(error => { promise.catch(error => {
snackbar.show({ snackbar.show({
title: 'Error loading derived display set:', title: 'Error loading derived display set:',
message: error.message, message: error.message,
type: 'error', type: 'error',
autoClose: false, autoClose: false,
});
}); });
}); });
}); });
}); }
}, [studies, viewportData]); }, [studies, viewportData, isStudyLoaded, snackbar]);
const getViewportPanes = () => const getViewportPanes = () =>
layout.viewports.map((layout, viewportIndex) => { layout.viewports.map((layout, viewportIndex) => {
@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import './ViewportPane.css'; import './ViewportPane.css';
const ViewportPane = function(props) { const ViewportPane = function (props) {
const { children, onDrop, viewportIndex, className: propClassName } = props; const { children, onDrop, viewportIndex, className: propClassName } = props;
const [{ hovered, highlighted }, drop] = useDrop({ const [{ hovered, highlighted }, drop] = useDrop({
accept: 'thumbnail', accept: 'thumbnail',
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js'; import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js';
import OHIF from '@ohif/core'; import OHIF from '@ohif/core';
const { clearViewportSpecificData } = OHIF.redux.actions; const { clearViewportSpecificData, setStudyData } = OHIF.redux.actions;
const isActive = a => a.active === true; const isActive = a => a.active === true;
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
@@ -14,6 +14,9 @@ const mapStateToProps = (state, ownProps) => {
}; };
const mapDispatchToProps = dispatch => { const mapDispatchToProps = dispatch => {
return { return {
setStudyData: (StudyInstanceUID, data) => {
dispatch(setStudyData(StudyInstanceUID, data));
},
clearViewportSpecificData: () => { clearViewportSpecificData: () => {
dispatch(clearViewportSpecificData()); dispatch(clearViewportSpecificData());
}, },
@@ -2,8 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { MODULE_TYPES } from '@ohif/core'; import OHIF, { MODULE_TYPES, DICOMSR } from '@ohif/core';
import OHIF, { DICOMSR } from '@ohif/core';
import { withDialog } from '@ohif/ui'; import { withDialog } from '@ohif/ui';
import moment from 'moment'; import moment from 'moment';
import ConnectedHeader from './ConnectedHeader.js'; import ConnectedHeader from './ConnectedHeader.js';
@@ -11,6 +10,7 @@ import ToolbarRow from './ToolbarRow.js';
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js'; import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
import ConnectedViewerMain from './ConnectedViewerMain.js'; import ConnectedViewerMain from './ConnectedViewerMain.js';
import SidePanel from './../components/SidePanel.js'; import SidePanel from './../components/SidePanel.js';
import ErrorBoundaryDialog from './../components/ErrorBoundaryDialog';
import { extensionManager } from './../App.js'; import { extensionManager } from './../App.js';
// Contexts // Contexts
@@ -26,6 +26,7 @@ class Viewer extends Component {
PropTypes.shape({ PropTypes.shape({
StudyInstanceUID: PropTypes.string.isRequired, StudyInstanceUID: PropTypes.string.isRequired,
StudyDate: PropTypes.string, StudyDate: PropTypes.string,
PatientID: PropTypes.string,
displaySets: PropTypes.arrayOf( displaySets: PropTypes.arrayOf(
PropTypes.shape({ PropTypes.shape({
displaySetInstanceUID: PropTypes.string.isRequired, displaySetInstanceUID: PropTypes.string.isRequired,
@@ -256,42 +257,44 @@ class Viewer extends Component {
</WhiteLabelingContext.Consumer> </WhiteLabelingContext.Consumer>
{/* TOOLBAR */} {/* TOOLBAR */}
<ToolbarRow <ErrorBoundaryDialog context="ToolbarRow">
isLeftSidePanelOpen={this.state.isLeftSidePanelOpen} <ToolbarRow
isRightSidePanelOpen={this.state.isRightSidePanelOpen} isLeftSidePanelOpen={this.state.isLeftSidePanelOpen}
selectedLeftSidePanel={ isRightSidePanelOpen={this.state.isRightSidePanelOpen}
this.state.isLeftSidePanelOpen selectedLeftSidePanel={
? this.state.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];
} }
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); const isOpen = updatedState[openKey];
}} const prevSelectedPanel = updatedState[selectedKey];
studies={this.props.studies} // 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} />*/} {/*<ConnectedStudyLoadingMonitor studies={this.props.studies} />*/}
{/*<StudyPrefetcher studies={this.props.studies} />*/} {/*<StudyPrefetcher studies={this.props.studies} />*/}
@@ -299,37 +302,46 @@ class Viewer extends Component {
{/* VIEWPORTS + SIDEPANELS */} {/* VIEWPORTS + SIDEPANELS */}
<div className="FlexboxLayout"> <div className="FlexboxLayout">
{/* LEFT */} {/* LEFT */}
<SidePanel from="left" isOpen={this.state.isLeftSidePanelOpen}> <ErrorBoundaryDialog context="LeftSidePanel">
{VisiblePanelLeft ? ( <SidePanel from="left" isOpen={this.state.isLeftSidePanelOpen}>
<VisiblePanelLeft {VisiblePanelLeft ? (
viewports={this.props.viewports} <VisiblePanelLeft
studies={this.props.studies} viewports={this.props.viewports}
activeIndex={this.props.activeViewportIndex} studies={this.props.studies}
/> activeIndex={this.props.activeViewportIndex}
) : ( />
) : (
<ConnectedStudyBrowser <ConnectedStudyBrowser
studies={this.state.thumbnails} studies={this.state.thumbnails}
studyMetadata={this.props.studies} studyMetadata={this.props.studies}
/> />
)} )}
</SidePanel> </SidePanel>
</ErrorBoundaryDialog>
{/* MAIN */} {/* MAIN */}
<div className={classNames('main-content')}> <div className={classNames('main-content')}>
<ConnectedViewerMain studies={this.props.studies} /> <ErrorBoundaryDialog context="ViewerMain">
<ConnectedViewerMain
studies={this.props.studies}
isStudyLoaded={this.props.isStudyLoaded}
/>
</ErrorBoundaryDialog>
</div> </div>
{/* RIGHT */} {/* RIGHT */}
<SidePanel from="right" isOpen={this.state.isRightSidePanelOpen}> <ErrorBoundaryDialog context="RightSidePanel">
{VisiblePanelRight && ( <SidePanel from="right" isOpen={this.state.isRightSidePanelOpen}>
<VisiblePanelRight {VisiblePanelRight && (
isOpen={this.state.isRightSidePanelOpen} <VisiblePanelRight
viewports={this.props.viewports} isOpen={this.state.isRightSidePanelOpen}
studies={this.props.studies} viewports={this.props.viewports}
activeIndex={this.props.activeViewportIndex} studies={this.props.studies}
/> activeIndex={this.props.activeViewportIndex}
)} />
</SidePanel> )}
</SidePanel>
</ErrorBoundaryDialog>
</div> </div>
</> </>
); );
@@ -349,7 +361,7 @@ export default withDialog(Viewer);
* @param {Study[]} studies * @param {Study[]} studies
* @param {DisplaySet[]} studies[].displaySets * @param {DisplaySet[]} studies[].displaySets
*/ */
const _mapStudiesToThumbnails = function (studies) { const _mapStudiesToThumbnails = function(studies) {
return studies.map(study => { return studies.map(study => {
const { StudyInstanceUID } = study; const { StudyInstanceUID } = study;
@@ -150,6 +150,7 @@ class ViewerMain extends Component {
<div className="ViewerMain"> <div className="ViewerMain">
{this.state.displaySets.length && ( {this.state.displaySets.length && (
<ConnectedViewportGrid <ConnectedViewportGrid
isStudyLoaded={this.props.isStudyLoaded}
studies={this.props.studies} studies={this.props.studies}
viewportData={viewportData} viewportData={viewportData}
setViewportData={this.setViewportData} 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 { metadata, studies, utils, log } from '@ohif/core';
import usePrevious from '../customHooks/usePrevious'; import usePrevious from '../customHooks/usePrevious';
@@ -7,13 +7,13 @@ import PropTypes from 'prop-types';
import { extensionManager } from './../App.js'; import { extensionManager } from './../App.js';
import { useSnackbarContext } from '@ohif/ui'; import { useSnackbarContext } from '@ohif/ui';
// Contexts
import AppContext from '../context/AppContext';
const { OHIFStudyMetadata, OHIFSeriesMetadata } = metadata; const { OHIFStudyMetadata, OHIFSeriesMetadata } = metadata;
const { retrieveStudiesMetadata, deleteStudyMetadataPromise } = studies; const { retrieveStudiesMetadata, deleteStudyMetadataPromise } = studies;
const { studyMetadataManager, makeCancelable } = utils; const { studyMetadataManager, makeCancelable } = utils;
// Contexts
import AppContext from '../context/AppContext';
const _promoteToFront = (list, value, searchMethod) => { const _promoteToFront = (list, value, searchMethod) => {
let response = [...list]; let response = [...list];
let promoted = false; let promoted = false;
@@ -125,9 +125,14 @@ const _addSeriesToStudy = (studyMetadata, series) => {
studyMetadata.createAndAddDisplaySetsForSeries( studyMetadata.createAndAddDisplaySetsForSeries(
sopClassHandlerModules, sopClassHandlerModules,
seriesMetadata, seriesMetadata
); );
study.displaySets = studyMetadata.getDisplaySets(); study.displaySets = studyMetadata.getDisplaySets();
study.derivedDisplaySets = studyMetadata.getDerivedDatasets({
Modality: series.Modality,
});
_updateStudyMetadataManager(study, studyMetadata); _updateStudyMetadataManager(study, studyMetadata);
}; };
@@ -147,6 +152,10 @@ const _updateStudyDisplaySets = (study, studyMetadata) => {
study.displaySets = studyMetadata.createDisplaySets(sopClassHandlerModules); study.displaySets = studyMetadata.createDisplaySets(sopClassHandlerModules);
} }
if (study.derivedDisplaySets) {
studyMetadata._addDerivedDisplaySets(study.derivedDisplaySets);
}
studyMetadata.setDisplaySets(study.displaySets); studyMetadata.setDisplaySets(study.displaySets);
}; };
@@ -154,11 +163,21 @@ const _sortStudyDisplaySet = (study, studyMetadata) => {
studyMetadata.sortDisplaySets(study.displaySets); studyMetadata.sortDisplaySets(study.displaySets);
}; };
const _thinStudyData = study => {
return {
StudyInstanceUID: study.StudyInstanceUID,
series: study.series.map(item => ({
SeriesInstanceUID: item.SeriesInstanceUID,
})),
};
};
function ViewerRetrieveStudyData({ function ViewerRetrieveStudyData({
server, server,
studyInstanceUIDs, studyInstanceUIDs,
seriesInstanceUIDs, seriesInstanceUIDs,
clearViewportSpecificData, clearViewportSpecificData,
setStudyData,
}) { }) {
// hooks // hooks
const [error, setError] = useState(false); const [error, setError] = useState(false);
@@ -220,6 +239,7 @@ function ViewerRetrieveStudyData({
if (Array.isArray(studiesData) && studiesData.length > 0) { if (Array.isArray(studiesData) && studiesData.length > 0) {
// Map studies to new format, update metadata manager? // Map studies to new format, update metadata manager?
const studies = studiesData.map(study => { const studies = studiesData.map(study => {
setStudyData(study.StudyInstanceUID, _thinStudyData(study));
const studyMetadata = new OHIFStudyMetadata( const studyMetadata = new OHIFStudyMetadata(
study, study,
study.StudyInstanceUID study.StudyInstanceUID
@@ -265,11 +285,13 @@ function ViewerRetrieveStudyData({
return loadNextSeries(); return loadNextSeries();
}; };
const concurrentRequestsAllowed = maxConcurrentMetadataRequests || studyMetadata.getSeriesCount(); const concurrentRequestsAllowed =
maxConcurrentMetadataRequests || studyMetadata.getSeriesCount();
const promises = Array(concurrentRequestsAllowed) const promises = Array(concurrentRequestsAllowed)
.fill(null) .fill(null)
.map(loadNextSeries); .map(loadNextSeries);
await Promise.all(promises);
return await Promise.all(promises);
}; };
const loadStudies = async () => { const loadStudies = async () => {
@@ -310,7 +332,7 @@ function ViewerRetrieveStudyData({
} }
}; };
const purgeCancellablePromises = () => { const purgeCancellablePromises = useCallback(() => {
for (let studyInstanceUIDs in cancelableStudiesPromises) { for (let studyInstanceUIDs in cancelableStudiesPromises) {
if ('cancel' in cancelableStudiesPromises[studyInstanceUIDs]) { if ('cancel' in cancelableStudiesPromises[studyInstanceUIDs]) {
cancelableStudiesPromises[studyInstanceUIDs].cancel(); cancelableStudiesPromises[studyInstanceUIDs].cancel();
@@ -324,7 +346,7 @@ function ViewerRetrieveStudyData({
studyMetadataManager.remove(studyInstanceUIDs); studyMetadataManager.remove(studyInstanceUIDs);
} }
} }
}; });
const prevStudyInstanceUIDs = usePrevious(studyInstanceUIDs); const prevStudyInstanceUIDs = usePrevious(studyInstanceUIDs);
@@ -338,7 +360,7 @@ function ViewerRetrieveStudyData({
studyMetadataManager.purge(); studyMetadataManager.purge();
purgeCancellablePromises(); purgeCancellablePromises();
} }
}, [studyInstanceUIDs]); }, [prevStudyInstanceUIDs, purgeCancellablePromises, studyInstanceUIDs]);
useEffect(() => { useEffect(() => {
cancelableSeriesPromises = {}; cancelableSeriesPromises = {};
@@ -368,6 +390,7 @@ ViewerRetrieveStudyData.propTypes = {
seriesInstanceUIDs: PropTypes.array, seriesInstanceUIDs: PropTypes.array,
server: PropTypes.object, server: PropTypes.object,
clearViewportSpecificData: PropTypes.func.isRequired, clearViewportSpecificData: PropTypes.func.isRequired,
setStudyData: PropTypes.func.isRequired,
}; };
export default ViewerRetrieveStudyData; export default ViewerRetrieveStudyData;
@@ -1,49 +1,44 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Modal from 'react-bootstrap-modal';
import DicomUploader from './DicomUploader'; import DicomUploader from './DicomUploader';
import { withTranslation } from 'react-i18next'; import { withTranslation } from 'react-i18next';
import { servicesManager } from './../App.js';
class DicomFileUploaderModal extends Component { function DicomFileUploaderModal({
static propTypes = { isOpen = false,
url: PropTypes.string, onClose,
retrieveAuthHeaderFunction: PropTypes.func, url,
onClose: PropTypes.func, retrieveAuthHeaderFunction,
}; t,
}) {
const { UIModalService } = servicesManager.services;
state = { const showDicomStorePickerModal = () => {
uploaded: false, if (!UIModalService) {
}; return
render() {
if (!this.props.url) {
return null;
} }
return ( UIModalService.show({
<Modal content: DicomUploader,
show={this.props.isOpen} title: t('Upload DICOM Files'),
onHide={this.props.onClose} contentProps: {
aria-labelledby="ModalHeader" url,
className="modal fade themed in" retrieveAuthHeaderFunction
backdrop={false} },
size={'md'} onClose,
keyboard={true} });
> };
<Modal.Header closeButton>
<Modal.Title> return (
{this.props.t('Upload DICOM Files')} <React.Fragment>{isOpen && showDicomStorePickerModal()}</React.Fragment>
</Modal.Title> );
</Modal.Header>
<Modal.Body>
<DicomUploader
url={this.props.url}
retrieveAuthHeaderFunction={this.props.retrieveAuthHeaderFunction}
/>
</Modal.Body>
</Modal>
);
}
} }
DicomFileUploaderModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
retrieveAuthHeaderFunction: PropTypes.func.isRequired,
onClose: PropTypes.func,
url: PropTypes.string,
};
export default withTranslation('Common')(DicomFileUploaderModal); export default withTranslation('Common')(DicomFileUploaderModal);
@@ -36,6 +36,7 @@ function DicomStorePickerModal({
user, user,
url, url,
}, },
onClose
}); });
} }
}; };
+4 -2
View File
@@ -14,9 +14,11 @@ class CallbackPage extends Component {
<CallbackComponent <CallbackComponent
userManager={this.props.userManager} userManager={this.props.userManager}
successCallback={() => { 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 => { errorCallback={error => {
//this.props.history.push("/"); //this.props.history.push("/");
+48 -30
View File
@@ -1247,7 +1247,7 @@
pirates "^4.0.0" pirates "^4.0.0"
source-map-support "^0.5.9" 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", "@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.9.6":
version "7.5.5" version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
@@ -4992,6 +4992,11 @@ cfb@^1.1.0:
crc-32 "~1.2.0" crc-32 "~1.2.0"
printj "~1.1.2" 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: chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3" version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -6534,10 +6539,10 @@ dateformat@^3.0.0:
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
dcmjs@^0.12.2: dcmjs@0.16.0:
version "0.12.2" version "0.16.0"
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.12.2.tgz#1c16e19c27ff43202abc7cd25c5ecd6d8bf7672e" resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.16.0.tgz#714755d006c3a49cf65ef13e627ff7a23151d35a"
integrity sha512-B7cEVCfDi3mOLKoBYUMYy+6COjLZhtKaesNpq5XNA4IbEH4EJYVweJfiI9gsR5hypv9LAPg72xG/xrbqjy8TgQ== integrity sha512-41nY3Isg5i5FoSd1s67aUgUHi146r+fb8JuiRvDsxvm7MEkkdjIzMYMtxjsrVwKRW1xRH+x34nxbV9+8cc7WQw==
dependencies: dependencies:
"@babel/polyfill" "^7.8.3" "@babel/polyfill" "^7.8.3"
"@babel/runtime" "^7.8.4" "@babel/runtime" "^7.8.4"
@@ -7247,7 +7252,7 @@ dom-converter@^0.2:
dependencies: dependencies:
utila "~0.4" 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" version "3.4.0"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==
@@ -15293,13 +15298,13 @@ prop-types-exact@^1.2.0:
object.assign "^4.1.0" object.assign "^4.1.0"
reflect.ownkeys "^0.2.0" reflect.ownkeys "^0.2.0"
prop-types-extra@^1.0.1, prop-types-extra@^1.1.0: prop-types-extra@^1.0.1, prop-types-extra@^1.1.1:
version "1.1.0" version "1.1.1"
resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.0.tgz#32609910ea2dcf190366bacd3490d5a6412a605f" resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b"
integrity sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg== integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==
dependencies: dependencies:
react-is "^16.3.2" react-is "^16.3.2"
warning "^3.0.0" warning "^4.0.0"
prop-types@15.6.2: prop-types@15.6.2:
version "15.6.2" version "15.6.2"
@@ -15586,30 +15591,19 @@ re-resizable@^4.11.0:
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-4.11.0.tgz#d5df10bda445c4ec0945751a223bf195afb61890" resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-4.11.0.tgz#d5df10bda445c4ec0945751a223bf195afb61890"
integrity sha512-dye+7rERqNf/6mDT1iwps+4Gf42420xuZgygF33uX178DxffqcyeuHbBuJ382FIcB5iP6mMZOhfW7kI0uXwb/Q== 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: react-codemirror2@^6.0.0:
version "6.0.0" version "6.0.0"
resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-6.0.0.tgz#180065df57a64026026cde569a9708fdf7656525" resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-6.0.0.tgz#180065df57a64026026cde569a9708fdf7656525"
integrity sha512-D7y9qZ05FbUh9blqECaJMdDwKluQiO3A9xB+fssd5jKM7YAXucRuEOlX32mJQumUvHUkHRHqXIPBjm6g0FW0Ag== integrity sha512-D7y9qZ05FbUh9blqECaJMdDwKluQiO3A9xB+fssd5jKM7YAXucRuEOlX32mJQumUvHUkHRHqXIPBjm6g0FW0Ag==
react-cornerstone-viewport@2.3.8: react-cornerstone-viewport@4.0.1:
version "2.3.8" version "4.0.1"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-2.3.8.tgz#7af8360f29bca986ae4e36b4e503269b88ddc52f" resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-4.0.1.tgz#6a7ec4c01b0626c4ad11552368f3dfa6cd67bdd5"
integrity sha512-aiG2uVNrDY6SQx4t/HBxIA3zsMsCwT+6TpcXK9qSSoXhs+X6OTmYEKncWUqL0jtxU1yfh6JTUz8ARTg03gtF+A== integrity sha512-47YhFsLS5vCWjiOKS9FwLxtA738HIHuNfdn3EJw6GyWD9jwL5HJQ/Ue1mKATjyHRvb3AyJTJuC/lGl0bsqKbYw==
dependencies: dependencies:
classnames "^2.2.6" classnames "^2.2.6"
date-fns "^2.2.1" date-fns "^2.2.1"
lodash.debounce "^4.0.8"
prop-types "^15.7.2" prop-types "^15.7.2"
react-resize-detector "^4.2.1" react-resize-detector "^4.2.1"
@@ -15796,6 +15790,13 @@ react-dropzone@^10.1.7:
file-selector "^0.1.11" file-selector "^0.1.11"
prop-types "^15.7.2" 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: react-error-overlay@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.1.tgz#417addb0814a90f3a7082eacba7cee588d00da89" resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.1.tgz#417addb0814a90f3a7082eacba7cee588d00da89"
@@ -15852,7 +15853,12 @@ react-input-autosize@^2.2.2:
dependencies: dependencies:
prop-types "^15.5.8" 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" version "16.11.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa"
integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw== integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==
@@ -16009,7 +16015,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" resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.9.15.tgz#59e3583832e9e98992d3674b2d7673b4cd1c5709"
integrity sha512-M8iKgjBTBZK92tZYgOEfMuR7c3zZ0q0v3QYllSxIPx3SU+w003VofH50txXQSBTu92pSOm2tidON1HbQ1l8BDA== 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" version "2.9.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==
@@ -19715,7 +19733,7 @@ warning@^3.0.0:
dependencies: dependencies:
loose-envify "^1.0.0" loose-envify "^1.0.0"
warning@^4.0.3: warning@^4.0.0, warning@^4.0.3:
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==