feat: Add new annotate tool using new dialog service (#1211)
* Testing dialog * Refactor modal and add dialog component to simple dialog * Use existent dialog * Expect event from getText callback (annotation) * Bump cornerstone-tools version * Use simple dialog * bump cornersotne version * CR Update: Update dialgo method names and default values * CR Update: Bump overlay and show only if theres at least one overlay prop set to true * CR Update: Use percentage over view height in draggable area * Fix broken test vtk checks WWWC tool * Comment test (non-deterministic)
This commit is contained in:
parent
1334ba8eb0
commit
8fd3af1e13
@ -32,7 +32,7 @@
|
|||||||
"@ohif/ui": "^0.50.0",
|
"@ohif/ui": "^0.50.0",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.2.8",
|
||||||
"cornerstone-math": "^0.1.8",
|
"cornerstone-math": "^0.1.8",
|
||||||
"cornerstone-tools": "^4.0.9",
|
"cornerstone-tools": "^4.8.0",
|
||||||
"cornerstone-wado-image-loader": "^3.0.0",
|
"cornerstone-wado-image-loader": "^3.0.0",
|
||||||
"dcmjs": "^0.6.1",
|
"dcmjs": "^0.6.1",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import cornerstone from 'cornerstone-core';
|
|||||||
import csTools from 'cornerstone-tools';
|
import csTools from 'cornerstone-tools';
|
||||||
import initCornerstoneTools from './initCornerstoneTools.js';
|
import initCornerstoneTools from './initCornerstoneTools.js';
|
||||||
import queryString from 'query-string';
|
import queryString from 'query-string';
|
||||||
|
import { SimpleDialog } from '@ohif/ui';
|
||||||
|
|
||||||
function fallbackMetaDataProvider(type, imageId) {
|
function fallbackMetaDataProvider(type, imageId) {
|
||||||
if (!imageId.includes('wado?requestType=WADO')) {
|
if (!imageId.includes('wado?requestType=WADO')) {
|
||||||
@ -29,6 +30,28 @@ cornerstone.metaData.addProvider(fallbackMetaDataProvider, -1);
|
|||||||
* @param {Object|Array} configuration.csToolsConfig
|
* @param {Object|Array} configuration.csToolsConfig
|
||||||
*/
|
*/
|
||||||
export default function init({ servicesManager, configuration = {} }) {
|
export default function init({ servicesManager, configuration = {} }) {
|
||||||
|
const { UIDialogService } = servicesManager.services;
|
||||||
|
const callInputDialog = (data, event, callback) => {
|
||||||
|
let dialogId = UIDialogService.create({
|
||||||
|
content: SimpleDialog.InputDialog,
|
||||||
|
defaultPosition: {
|
||||||
|
x: (event && event.currentPoints.canvas.x) || 0,
|
||||||
|
y: (event && event.currentPoints.canvas.y) || 0,
|
||||||
|
},
|
||||||
|
showOverlay: true,
|
||||||
|
contentProps: {
|
||||||
|
title: 'Enter your annotation',
|
||||||
|
label: 'New label',
|
||||||
|
measurementData: data ? { description: data.text } : {},
|
||||||
|
onClose: () => UIDialogService.dismiss({ id: dialogId }),
|
||||||
|
onSubmit: value => {
|
||||||
|
callback(value);
|
||||||
|
UIDialogService.dismiss({ id: dialogId });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const { csToolsConfig } = configuration;
|
const { csToolsConfig } = configuration;
|
||||||
const { StackManager } = OHIF.utils;
|
const { StackManager } = OHIF.utils;
|
||||||
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
|
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
|
||||||
@ -62,7 +85,6 @@ export default function init({ servicesManager, configuration = {} }) {
|
|||||||
ZoomTouchPinchTool,
|
ZoomTouchPinchTool,
|
||||||
// Annotations
|
// Annotations
|
||||||
EraserTool,
|
EraserTool,
|
||||||
ArrowAnnotateTool,
|
|
||||||
BidirectionalTool,
|
BidirectionalTool,
|
||||||
LengthTool,
|
LengthTool,
|
||||||
AngleTool,
|
AngleTool,
|
||||||
@ -85,7 +107,6 @@ export default function init({ servicesManager, configuration = {} }) {
|
|||||||
ZoomTouchPinchTool,
|
ZoomTouchPinchTool,
|
||||||
// Annotations
|
// Annotations
|
||||||
EraserTool,
|
EraserTool,
|
||||||
ArrowAnnotateTool,
|
|
||||||
BidirectionalTool,
|
BidirectionalTool,
|
||||||
LengthTool,
|
LengthTool,
|
||||||
AngleTool,
|
AngleTool,
|
||||||
@ -99,6 +120,15 @@ export default function init({ servicesManager, configuration = {} }) {
|
|||||||
|
|
||||||
tools.forEach(tool => csTools.addTool(tool));
|
tools.forEach(tool => csTools.addTool(tool));
|
||||||
|
|
||||||
|
csTools.addTool(csTools.ArrowAnnotateTool, {
|
||||||
|
configuration: {
|
||||||
|
getTextCallback: (callback, eventDetails) =>
|
||||||
|
callInputDialog(null, eventDetails, callback),
|
||||||
|
changeTextCallback: (data, eventDetails, callback) =>
|
||||||
|
callInputDialog(data, eventDetails, callback),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
|
csTools.setToolActive('Pan', { mouseButtonMask: 4 });
|
||||||
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
|
csTools.setToolActive('Zoom', { mouseButtonMask: 2 });
|
||||||
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
|
csTools.setToolActive('Wwwc', { mouseButtonMask: 1 });
|
||||||
|
|||||||
@ -54,7 +54,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ohif/core": "^1.12.0",
|
"@ohif/core": "^1.12.0",
|
||||||
"@ohif/ui": "^0.64.2",
|
"@ohif/ui": "^0.64.2",
|
||||||
"cornerstone-tools": "^4.0.9",
|
"cornerstone-tools": "^4.8.0",
|
||||||
"cornerstone-wado-image-loader": "^3.0.0",
|
"cornerstone-wado-image-loader": "^3.0.0",
|
||||||
"dcmjs": "^0.6.1",
|
"dcmjs": "^0.6.1",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.2.8",
|
||||||
"cornerstone-tools": "^4.0.9",
|
"cornerstone-tools": "^4.8.0",
|
||||||
"cornerstone-wado-image-loader": "^3.0.0",
|
"cornerstone-wado-image-loader": "^3.0.0",
|
||||||
"dicom-parser": "^1.8.3"
|
"dicom-parser": "^1.8.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,11 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* A UI Element
|
* A UI Element Position
|
||||||
*
|
|
||||||
* @typedef {ReactElement|HTMLElement} DialogContent
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A UI Position
|
|
||||||
*
|
*
|
||||||
* @typedef {Object} ElementPosition
|
* @typedef {Object} ElementPosition
|
||||||
* @property {number} top -
|
* @property {number} top -
|
||||||
@ -18,16 +12,16 @@
|
|||||||
* UI Dialog
|
* UI Dialog
|
||||||
*
|
*
|
||||||
* @typedef {Object} DialogProps
|
* @typedef {Object} DialogProps
|
||||||
* @property {string} id -
|
* @property {string} id The dialog id.
|
||||||
* @property {DialogContent} content -
|
* @property {ReactElement|HTMLElement} content The dialog content.
|
||||||
* @property {boolean} isDraggable -
|
* @property {Object} contentProps The dialog content props.
|
||||||
* @property {ElementPosition} defaultPosition -
|
* @property {boolean} [isDraggable=true] Controls if dialog content is draggable or not.
|
||||||
* @property {ElementPosition} position -
|
* @property {boolean} [showOverlay=false] Controls dialog overlay.
|
||||||
* @property {Function} onSubmit -
|
* @property {ElementPosition} defaultPosition Specifies the `x` and `y` that the dragged item should start at.
|
||||||
* @property {Function} onClose -
|
* @property {ElementPosition} position If this property is present, the item becomes 'controlled' and is not responsive to user input.
|
||||||
* @property {Function} onStart -
|
* @property {Function} onStart Called when dragging starts. If `false` is returned any handler, the action will cancel.
|
||||||
* @property {Function} onStop -
|
* @property {Function} onStop Called when dragging stops.
|
||||||
* @property {Function} onDrag -
|
* @property {Function} onDrag Called while dragging.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const uiDialogServicePublicAPI = {
|
const uiDialogServicePublicAPI = {
|
||||||
@ -51,29 +45,29 @@ function createUIDialogService() {
|
|||||||
/**
|
/**
|
||||||
* Show a new UI dialog;
|
* Show a new UI dialog;
|
||||||
*
|
*
|
||||||
* @param {DialogProps} props { id, content, onSubmit, onClose, onStart, onDrag, onStop, isDraggable, defaultPosition, position }
|
* @param {DialogProps} props { id, content, contentProps, onStart, onDrag, onStop, isDraggable, showOverlay, defaultPosition, position }
|
||||||
*/
|
*/
|
||||||
function create({
|
function create({
|
||||||
id,
|
id,
|
||||||
content,
|
content,
|
||||||
onSubmit,
|
contentProps,
|
||||||
onClose,
|
|
||||||
onStart,
|
onStart,
|
||||||
onDrag,
|
onDrag,
|
||||||
onStop,
|
onStop,
|
||||||
isDraggable,
|
isDraggable = true,
|
||||||
|
showOverlay = false,
|
||||||
defaultPosition,
|
defaultPosition,
|
||||||
position,
|
position,
|
||||||
}) {
|
}) {
|
||||||
return uiDialogServiceImplementation._create({
|
return uiDialogServiceImplementation._create({
|
||||||
id,
|
id,
|
||||||
content,
|
content,
|
||||||
onSubmit,
|
contentProps,
|
||||||
onClose,
|
|
||||||
onStart,
|
onStart,
|
||||||
onDrag,
|
onDrag,
|
||||||
onStop,
|
onStop,
|
||||||
isDraggable,
|
isDraggable,
|
||||||
|
showOverlay,
|
||||||
defaultPosition,
|
defaultPosition,
|
||||||
position,
|
position,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,18 +1,14 @@
|
|||||||
/**
|
|
||||||
* A UI Element
|
|
||||||
*
|
|
||||||
* @typedef {ReactElement|HTMLElement} Modal
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UI Modal
|
* UI Modal
|
||||||
*
|
*
|
||||||
* @typedef {Object} ModalProps
|
* @typedef {Object} ModalProps
|
||||||
* @property {boolean} [shouldCloseOnEsc=false] -
|
* @property {ReactElement|HTMLElement} [content=null] Modal content.
|
||||||
* @property {boolean} [isOpen=true] -
|
* @property {Object} [contentProps=null] Modal content props.
|
||||||
* @property {boolean} [closeButton=true] -
|
* @property {boolean} [shouldCloseOnEsc=false] Modal is dismissible via the esc key.
|
||||||
* @property {string} [title=null] - 'Modal Title'
|
* @property {boolean} [isOpen=true] Make the Modal visible or hidden.
|
||||||
* @property {string} [customClassName=null] - '.ModalClass'
|
* @property {boolean} [closeButton=true] Should the modal body render the close button.
|
||||||
|
* @property {string} [title=null] Should the modal render the title independently of the body content.
|
||||||
|
* @property {string} [customClassName=null] The custom class to style the modal.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const uiModalServicePublicAPI = {
|
const uiModalServicePublicAPI = {
|
||||||
@ -34,20 +30,26 @@ function createUIModalService() {
|
|||||||
/**
|
/**
|
||||||
* Show a new UI modal;
|
* Show a new UI modal;
|
||||||
*
|
*
|
||||||
* @param {Modal} component React component
|
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, closeButton, title, customClassName }
|
||||||
* @param {ModalProps} props { shouldCloseOnEsc, isOpen, closeButton, title, customClassName }
|
|
||||||
*/
|
*/
|
||||||
function show(
|
function show({
|
||||||
component,
|
content = null,
|
||||||
props = {
|
contentProps = null,
|
||||||
shouldCloseOnEsc: false,
|
shouldCloseOnEsc = false,
|
||||||
isOpen: true,
|
isOpen = true,
|
||||||
closeButton: true,
|
closeButton = true,
|
||||||
title: null,
|
title = null,
|
||||||
customClassName: null,
|
customClassName = null,
|
||||||
}
|
}) {
|
||||||
) {
|
return uiModalServiceImplementation._show({
|
||||||
return uiModalServiceImplementation._show(component, props);
|
content,
|
||||||
|
contentProps,
|
||||||
|
shouldCloseOnEsc,
|
||||||
|
isOpen,
|
||||||
|
closeButton,
|
||||||
|
title,
|
||||||
|
customClassName,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component, useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { TextInput } from '@ohif/ui';
|
||||||
|
|
||||||
import './SimpleDialog.styl';
|
import './SimpleDialog.styl';
|
||||||
|
|
||||||
@ -21,6 +22,31 @@ class SimpleDialog extends Component {
|
|||||||
rootClass: '',
|
rootClass: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static InputDialog = ({ onSubmit, defaultValue, title, label, onClose }) => {
|
||||||
|
const [value, setValue] = useState(defaultValue);
|
||||||
|
|
||||||
|
const onSubmitHandler = () => {
|
||||||
|
onSubmit(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="InputDialog">
|
||||||
|
<SimpleDialog
|
||||||
|
headerTitle={title}
|
||||||
|
onClose={onClose}
|
||||||
|
onConfirm={onSubmitHandler}
|
||||||
|
>
|
||||||
|
<TextInput
|
||||||
|
type="text"
|
||||||
|
value={value}
|
||||||
|
onChange={event => setValue(event.target.value)}
|
||||||
|
label={label}
|
||||||
|
/>
|
||||||
|
</SimpleDialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
@import './../../design/styles/common/button.styl'
|
@import './../../design/styles/common/button.styl'
|
||||||
@import './../../design/styles/common/global.styl'
|
@import './../../design/styles/common/global.styl'
|
||||||
|
|
||||||
|
.InputDialog
|
||||||
|
.simpleDialog
|
||||||
|
position: relative
|
||||||
|
|
||||||
.simpleDialog
|
.simpleDialog
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
|
|||||||
@ -34,49 +34,45 @@ const DialogProvider = ({ children, service }) => {
|
|||||||
}, [create, dismiss, service]);
|
}, [create, dismiss, service]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a dialog and return its id.
|
* UI Dialog
|
||||||
*
|
*
|
||||||
* @returns id
|
* @typedef {Object} DialogProps
|
||||||
|
* @property {string} id The dialog id.
|
||||||
|
* @property {DialogContent} content The dialog content.
|
||||||
|
* @property {Object} contentProps The dialog content props.
|
||||||
|
* @property {boolean} isDraggable Controls if dialog content is draggable or not.
|
||||||
|
* @property {boolean} showOverlay Controls dialog overlay.
|
||||||
|
* @property {ElementPosition} defaultPosition Specifies the `x` and `y` that the dragged item should start at.
|
||||||
|
* @property {ElementPosition} position If this property is present, the item becomes 'controlled' and is not responsive to user input.
|
||||||
|
* @property {Function} onStart Called when dragging starts. If `false` is returned any handler, the action will cancel.
|
||||||
|
* @property {Function} onStop Called when dragging stops.
|
||||||
|
* @property {Function} onDrag Called while dragging.
|
||||||
*/
|
*/
|
||||||
const create = useCallback(
|
|
||||||
({
|
/**
|
||||||
id,
|
* Creates a new dialog and return its id.
|
||||||
content,
|
*
|
||||||
onSubmit,
|
* @param {DialogProps} props The dialog props.
|
||||||
onClose,
|
* @returns The new dialog id.
|
||||||
onDrag,
|
*/
|
||||||
onStop,
|
const create = useCallback(props => {
|
||||||
isDraggable,
|
const { id } = props;
|
||||||
defaultPosition,
|
|
||||||
position,
|
|
||||||
}) => {
|
|
||||||
let dialogId = id;
|
let dialogId = id;
|
||||||
if (!dialogId) {
|
if (!dialogId) {
|
||||||
dialogId = utils.guid();
|
dialogId = utils.guid();
|
||||||
}
|
}
|
||||||
|
|
||||||
const newDialog = {
|
setDialogs(dialogs => [...dialogs, { ...props, id: dialogId }]);
|
||||||
id: dialogId,
|
|
||||||
content,
|
|
||||||
onSubmit,
|
|
||||||
onClose,
|
|
||||||
onDrag,
|
|
||||||
onStop,
|
|
||||||
isDraggable,
|
|
||||||
defaultPosition,
|
|
||||||
position,
|
|
||||||
};
|
|
||||||
|
|
||||||
setDialogs(dialogs => [...dialogs, newDialog]);
|
|
||||||
|
|
||||||
return dialogId;
|
return dialogId;
|
||||||
},
|
}, []);
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dismisses the dialog with a given id.
|
* Dismisses the dialog with a given id.
|
||||||
*
|
*
|
||||||
|
* @param {Object} props -
|
||||||
|
* @property {string} props.id The dialog id.
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
const dismiss = useCallback(({ id }) => {
|
const dismiss = useCallback(({ id }) => {
|
||||||
@ -92,41 +88,40 @@ const DialogProvider = ({ children, service }) => {
|
|||||||
setDialogs([]);
|
setDialogs([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if there are no dialogs present.
|
||||||
|
*
|
||||||
|
* @returns True if no dialogs are present.
|
||||||
|
*/
|
||||||
|
const isEmpty = () => dialogs && dialogs.length < 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the dialog to the foreground if clicked.
|
* Moves the dialog to the foreground if clicked.
|
||||||
*
|
*
|
||||||
|
* @param {string} id The dialog id.
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
const _reorder = id => {
|
const _bringToFront = id => {
|
||||||
setDialogs(dialogs => [
|
setDialogs(dialogs => {
|
||||||
...dialogs.filter(dialog => dialog.id !== id),
|
const topDialog = dialogs.find(dialog => dialog.id === id);
|
||||||
dialogs.find(dialog => dialog.id === id),
|
return topDialog
|
||||||
]);
|
? [...dialogs.filter(dialog => dialog.id !== id), topDialog]
|
||||||
};
|
: [];
|
||||||
|
|
||||||
const _updateLastDialogPosition = dialogId => {
|
|
||||||
const draggableItemBounds = document
|
|
||||||
.querySelector(`#draggableItem-${dialogId}`)
|
|
||||||
.getBoundingClientRect();
|
|
||||||
setLastDialogPosition({
|
|
||||||
x: draggableItemBounds.x,
|
|
||||||
y: draggableItemBounds.y,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const renderDialogs = () =>
|
||||||
<DialogContext.Provider value={{ create, dismiss, dismissAll, dialogs }}>
|
dialogs.map(dialog => {
|
||||||
<div className="DraggableArea">
|
|
||||||
{dialogs.map(dialog => {
|
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
content: Dialog,
|
content: DialogContent,
|
||||||
position /* Position of the dialog. {{x: 0, y: 0}} */,
|
contentProps,
|
||||||
|
position,
|
||||||
defaultPosition,
|
defaultPosition,
|
||||||
isDraggable = true,
|
isDraggable = true,
|
||||||
onStart = () => {},
|
onStart,
|
||||||
onStop = () => {},
|
onStop,
|
||||||
onDrag = () => {},
|
onDrag,
|
||||||
} = dialog;
|
} = dialog;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -139,45 +134,96 @@ const DialogProvider = ({ children, service }) => {
|
|||||||
onStart={event => {
|
onStart={event => {
|
||||||
const e = event || window.event;
|
const e = event || window.event;
|
||||||
const target = e.target || e.srcElement;
|
const target = e.target || e.srcElement;
|
||||||
const BLACKLIST = ['SVG', 'BUTTON', 'PATH', 'INPUT'];
|
const BLACKLIST = [
|
||||||
|
'SVG',
|
||||||
|
'BUTTON',
|
||||||
|
'PATH',
|
||||||
|
'INPUT',
|
||||||
|
'SPAN',
|
||||||
|
'LABEL',
|
||||||
|
];
|
||||||
if (BLACKLIST.includes(target.tagName.toUpperCase())) {
|
if (BLACKLIST.includes(target.tagName.toUpperCase())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onStart(event);
|
if (validCallback(onStart)) {
|
||||||
|
return onStart(event);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onStop={event => {
|
onStop={event => {
|
||||||
onStop(event);
|
|
||||||
setIsDragging(false);
|
setIsDragging(false);
|
||||||
return;
|
|
||||||
|
if (validCallback(onStop)) {
|
||||||
|
return onStop(event);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onDrag={event => {
|
onDrag={event => {
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
_reorder(id);
|
_bringToFront(id);
|
||||||
_updateLastDialogPosition(id);
|
_updateLastDialogPosition(id);
|
||||||
onDrag(event);
|
|
||||||
|
if (validCallback(onDrag)) {
|
||||||
|
return onDrag(event);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id={`draggableItem-${id}`}
|
id={`draggableItem-${id}`}
|
||||||
className={classNames(
|
className={classNames('DraggableItem', isDragging && 'dragging')}
|
||||||
'DraggableItem',
|
|
||||||
isDragging && 'dragging'
|
|
||||||
)}
|
|
||||||
style={{ zIndex: '999', position: 'absolute' }}
|
style={{ zIndex: '999', position: 'absolute' }}
|
||||||
onClick={() => _reorder(id)}
|
onClick={() => _bringToFront(id)}
|
||||||
>
|
>
|
||||||
<Dialog {...dialog} />
|
<DialogContent {...dialog} {...contentProps} />
|
||||||
</div>
|
</div>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
);
|
);
|
||||||
})}
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the last dialog position to be used as the new default position.
|
||||||
|
*
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
const _updateLastDialogPosition = dialogId => {
|
||||||
|
const draggableItemBounds = document
|
||||||
|
.querySelector(`#draggableItem-${dialogId}`)
|
||||||
|
.getBoundingClientRect();
|
||||||
|
setLastDialogPosition({
|
||||||
|
x: draggableItemBounds.x,
|
||||||
|
y: draggableItemBounds.y,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const validCallback = callback => callback && typeof callback === 'function';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogContext.Provider value={{ create, dismiss, dismissAll, isEmpty }}>
|
||||||
|
<div className="DraggableArea">
|
||||||
|
{dialogs.some(dialog => dialog.showOverlay) ? (
|
||||||
|
<div className="Overlay active">{renderDialogs()}</div>
|
||||||
|
) : (
|
||||||
|
renderDialogs()
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
</DialogContext.Provider>
|
</DialogContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* High Order Component to use the dialog methods through a Class Component
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export const withDialog = Component => {
|
||||||
|
return function WrappedComponent(props) {
|
||||||
|
const { create, dismiss, dismissAll, isEmpty } = useDialog();
|
||||||
|
return (
|
||||||
|
<Component {...props} dialog={{ create, dismiss, dismissAll, isEmpty }} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
DialogProvider.defaultProps = {
|
DialogProvider.defaultProps = {
|
||||||
service: null,
|
service: null,
|
||||||
};
|
};
|
||||||
@ -193,16 +239,4 @@ DialogProvider.propTypes = {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* High Order Component to use the dialog methods through a Class Component
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const withDialog = Component => {
|
|
||||||
return function WrappedComponent(props) {
|
|
||||||
const { create, dismiss, dismissAll } = useDialog();
|
|
||||||
return <Component {...props} dialog={{ create, dismiss, dismissAll }} />;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DialogProvider;
|
export default DialogProvider;
|
||||||
|
|||||||
@ -6,7 +6,17 @@
|
|||||||
div
|
div
|
||||||
cursor: grabbing !important
|
cursor: grabbing !important
|
||||||
|
|
||||||
.DraggableArea
|
.DraggableArea, .Overlay
|
||||||
width: 100vw
|
width: 100%
|
||||||
height: 100vh
|
height: 100%
|
||||||
position: absolute
|
position: absolute
|
||||||
|
|
||||||
|
.Overlay.active
|
||||||
|
position: fixed
|
||||||
|
z-index: 999
|
||||||
|
left: 0
|
||||||
|
top: 0
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
overflow: auto
|
||||||
|
background: rgba(0,0,0,.1)
|
||||||
|
|||||||
@ -13,14 +13,28 @@ const { Provider } = ModalContext;
|
|||||||
|
|
||||||
export const useModal = () => useContext(ModalContext);
|
export const useModal = () => useContext(ModalContext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI Modal
|
||||||
|
*
|
||||||
|
* @typedef {Object} ModalProps
|
||||||
|
* @property {ReactElement|HTMLElement} [content=null] Modal content.
|
||||||
|
* @property {Object} [contentProps=null] Modal content props.
|
||||||
|
* @property {boolean} [shouldCloseOnEsc=false] Modal is dismissible via the esc key.
|
||||||
|
* @property {boolean} [isOpen=true] Make the Modal visible or hidden.
|
||||||
|
* @property {boolean} [closeButton=true] Should the modal body render the close button.
|
||||||
|
* @property {string} [title=null] Should the modal render the title independently of the body content.
|
||||||
|
* @property {string} [customClassName=null] The custom class to style the modal.
|
||||||
|
*/
|
||||||
|
|
||||||
const ModalProvider = ({ children, modal: Modal, service }) => {
|
const ModalProvider = ({ children, modal: Modal, service }) => {
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_OPTIONS = {
|
||||||
component: null /* The component instance inside the modal. */,
|
content: null,
|
||||||
shouldCloseOnEsc: false /* Modal is dismissible via the esc key. */,
|
contentProps: null,
|
||||||
isOpen: true /* Make the Modal visible or hidden. */,
|
shouldCloseOnEsc: false,
|
||||||
closeButton: true /* Should the modal body render the close button. */,
|
isOpen: true,
|
||||||
title: null /* Should the modal render the title independently of the body content. */,
|
closeButton: true,
|
||||||
customClassName: '' /* The custom class to style the modal. */,
|
title: null,
|
||||||
|
customClassName: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
||||||
@ -39,13 +53,12 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
|
|||||||
/**
|
/**
|
||||||
* Show the modal and override its configuration props.
|
* Show the modal and override its configuration props.
|
||||||
*
|
*
|
||||||
|
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, closeButton, title, customClassName }
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
const show = useCallback(
|
const show = useCallback(props => setOptions({ ...options, ...props }), [
|
||||||
(component, props = {}) =>
|
options,
|
||||||
setOptions(Object.assign({}, options, props, { component })),
|
]);
|
||||||
[options]
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the modal and set its properties to default.
|
* Hide the modal and set its properties to default.
|
||||||
@ -56,23 +69,28 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
|
|||||||
DEFAULT_OPTIONS,
|
DEFAULT_OPTIONS,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { component: Component } = options;
|
const {
|
||||||
|
content: ModalContent,
|
||||||
|
contentProps,
|
||||||
|
isOpen,
|
||||||
|
title,
|
||||||
|
customClassName,
|
||||||
|
shouldCloseOnEsc,
|
||||||
|
closeButton,
|
||||||
|
} = options;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider value={{ show, hide }}>
|
<Provider value={{ show, hide }}>
|
||||||
{options.component && (
|
{ModalContent && (
|
||||||
<Modal
|
<Modal
|
||||||
className={classNames(
|
className={classNames(customClassName, ModalContent.className)}
|
||||||
options.customClassName,
|
shouldCloseOnEsc={shouldCloseOnEsc}
|
||||||
options.component.className
|
isOpen={isOpen}
|
||||||
)}
|
title={title}
|
||||||
shouldCloseOnEsc={options.keyboard}
|
closeButton={closeButton}
|
||||||
isOpen={options.isOpen}
|
|
||||||
title={options.title}
|
|
||||||
closeButton={options.closeButton}
|
|
||||||
onClose={hide}
|
onClose={hide}
|
||||||
>
|
>
|
||||||
<Component {...options} show={show} hide={hide} />
|
<ModalContent {...contentProps} show={show} hide={hide} />
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
@ -80,6 +98,18 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Higher Order Component to use the modal methods through a Class Component.
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const withModal = Component => {
|
||||||
|
return function WrappedComponent(props) {
|
||||||
|
const { show, hide } = useModal();
|
||||||
|
return <Component {...props} modal={{ show, hide }} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
ModalProvider.defaultProps = {
|
ModalProvider.defaultProps = {
|
||||||
service: null,
|
service: null,
|
||||||
};
|
};
|
||||||
@ -99,18 +129,6 @@ ModalProvider.propTypes = {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Higher Order Component to use the modal methods through a Class Component.
|
|
||||||
*
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const withModal = Component => {
|
|
||||||
return function WrappedComponent(props) {
|
|
||||||
const { show, hide } = useModal();
|
|
||||||
return <Component {...props} modal={{ show, hide }} />;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ModalProvider;
|
export default ModalProvider;
|
||||||
|
|
||||||
export const ModalConsumer = ModalContext.Consumer;
|
export const ModalConsumer = ModalContext.Consumer;
|
||||||
|
|||||||
@ -81,6 +81,7 @@ describe('OHIF VTK Extension', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* TODO: Non-deterministic behavior (const expectedText = 'W: 350 L: -1044';)
|
||||||
it('checks WWWC tool', () => {
|
it('checks WWWC tool', () => {
|
||||||
cy.get('@wwwcBtn').click();
|
cy.get('@wwwcBtn').click();
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ describe('OHIF VTK Extension', () => {
|
|||||||
.trigger('mousemove', 'top', { which: 1 })
|
.trigger('mousemove', 'top', { which: 1 })
|
||||||
.trigger('mouseup', { which: 1 })
|
.trigger('mouseup', { which: 1 })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
const expectedText = 'W: 350 L: -1044';
|
const expectedText = 'W: 350 L: 40';
|
||||||
cy.get('.ViewportOverlay > div.bottom-right.overlay-element').should(
|
cy.get('.ViewportOverlay > div.bottom-right.overlay-element').should(
|
||||||
'contains.text',
|
'contains.text',
|
||||||
expectedText
|
expectedText
|
||||||
@ -102,7 +103,7 @@ describe('OHIF VTK Extension', () => {
|
|||||||
// Visual comparison
|
// Visual comparison
|
||||||
cy.screenshot('VTK WWWC tool - Canvas should be bright');
|
cy.screenshot('VTK WWWC tool - Canvas should be bright');
|
||||||
cy.percyCanvasSnapshot('VTK WWWC tool - Canvas should be bright');
|
cy.percyCanvasSnapshot('VTK WWWC tool - Canvas should be bright');
|
||||||
});
|
}); */
|
||||||
|
|
||||||
it('checks Rotate tool', () => {
|
it('checks Rotate tool', () => {
|
||||||
cy.get('@rotateBtn').click();
|
cy.get('@rotateBtn').click();
|
||||||
|
|||||||
@ -58,7 +58,7 @@
|
|||||||
"core-js": "^3.2.1",
|
"core-js": "^3.2.1",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.2.8",
|
||||||
"cornerstone-math": "^0.1.8",
|
"cornerstone-math": "^0.1.8",
|
||||||
"cornerstone-tools": "^4.0.9",
|
"cornerstone-tools": "^4.8.0",
|
||||||
"cornerstone-wado-image-loader": "^3.0.0",
|
"cornerstone-wado-image-loader": "^3.0.0",
|
||||||
"dcmjs": "^0.6.1",
|
"dcmjs": "^0.6.1",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
getResetLabellingAndContextMenu,
|
getResetLabellingAndContextMenu,
|
||||||
} from './labelingFlowCallbacks.js';
|
} from './labelingFlowCallbacks.js';
|
||||||
import throttle from 'lodash.throttle';
|
import throttle from 'lodash.throttle';
|
||||||
|
import { SimpleDialog } from '@ohif/ui';
|
||||||
|
|
||||||
// TODO: This only works because we have a hard dependency on this extension
|
// TODO: This only works because we have a hard dependency on this extension
|
||||||
// We need to decouple and make stuff like this possible w/o bundling this at
|
// We need to decouple and make stuff like this possible w/o bundling this at
|
||||||
@ -35,6 +36,28 @@ const MEASUREMENT_ACTION_MAP = {
|
|||||||
* @param {*} configuration
|
* @param {*} configuration
|
||||||
*/
|
*/
|
||||||
export default function init({ servicesManager, configuration = {} }) {
|
export default function init({ servicesManager, configuration = {} }) {
|
||||||
|
const { UIDialogService } = servicesManager.services;
|
||||||
|
const callInputDialog = (data, event, callback) => {
|
||||||
|
let dialogId = UIDialogService.create({
|
||||||
|
content: SimpleDialog.InputDialog,
|
||||||
|
defaultPosition: {
|
||||||
|
x: (event && event.currentPoints.canvas.x) || 0,
|
||||||
|
y: (event && event.currentPoints.canvas.y) || 0,
|
||||||
|
},
|
||||||
|
showOverlay: true,
|
||||||
|
contentProps: {
|
||||||
|
title: 'Enter your annotation',
|
||||||
|
label: 'New label',
|
||||||
|
defaultValue: data ? data.text : '',
|
||||||
|
onClose: () => UIDialogService.dismiss({ id: dialogId }),
|
||||||
|
onSubmit: value => {
|
||||||
|
callback(value);
|
||||||
|
UIDialogService.dismiss({ id: dialogId });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// If these tools were already added by a different extension, we want to replace
|
// If these tools were already added by a different extension, we want to replace
|
||||||
// them with the same tools that have an alternative configuration. By passing in
|
// them with the same tools that have an alternative configuration. By passing in
|
||||||
// our custom `getMeasurementLocationCallback`, we can...
|
// our custom `getMeasurementLocationCallback`, we can...
|
||||||
@ -90,6 +113,10 @@ export default function init({ servicesManager, configuration = {} }) {
|
|||||||
csTools.addTool(csTools.ArrowAnnotateTool, {
|
csTools.addTool(csTools.ArrowAnnotateTool, {
|
||||||
configuration: {
|
configuration: {
|
||||||
getMeasurementLocationCallback: toolLabellingFlowCallback,
|
getMeasurementLocationCallback: toolLabellingFlowCallback,
|
||||||
|
getTextCallback: (callback, eventDetails) =>
|
||||||
|
callInputDialog(null, eventDetails, callback),
|
||||||
|
changeTextCallback: (data, eventDetails, callback) =>
|
||||||
|
callInputDialog(data, eventDetails, callback),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -36,18 +36,14 @@ class Header extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadOptions() {
|
loadOptions() {
|
||||||
const {
|
const { t, user, userManager, modal } = this.props;
|
||||||
t,
|
|
||||||
user,
|
|
||||||
userManager,
|
|
||||||
modal: { show },
|
|
||||||
} = this.props;
|
|
||||||
this.options = [
|
this.options = [
|
||||||
{
|
{
|
||||||
title: t('About'),
|
title: t('About'),
|
||||||
icon: { name: 'info' },
|
icon: { name: 'info' },
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
show(AboutContent, {
|
modal.show({
|
||||||
|
content: AboutContent,
|
||||||
title: t('OHIF Viewer - About'),
|
title: t('OHIF Viewer - About'),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -57,7 +53,8 @@ class Header extends Component {
|
|||||||
name: 'user',
|
name: 'user',
|
||||||
},
|
},
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
show(ConnectedUserPreferencesForm, {
|
modal.show({
|
||||||
|
content: ConnectedUserPreferencesForm,
|
||||||
title: t('User Preferences'),
|
title: t('User Preferences'),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -308,7 +308,8 @@ function _handleBuiltIn(button) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.behavior === 'DOWNLOAD_SCREEN_SHOT') {
|
if (options.behavior === 'DOWNLOAD_SCREEN_SHOT') {
|
||||||
modal.show(ConnectedViewportDownloadForm, {
|
modal.show({
|
||||||
|
content: ConnectedViewportDownloadForm,
|
||||||
title: t('Download High Quality Image'),
|
title: t('Download High Quality Image'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
36
yarn.lock
36
yarn.lock
@ -1100,34 +1100,13 @@
|
|||||||
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.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":
|
||||||
version "7.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.1.2.tgz#81c89935f4647706fc54541145e6b4ecfef4b8e3"
|
|
||||||
integrity sha512-Y3SCjmhSupzFB6wcv1KmmFucH6gDVnI30WjOcicV10ju0cZjak3Jcs67YLIXBrmZYw1xCrVeJPbycFwrqNyxpg==
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime "^0.12.0"
|
|
||||||
|
|
||||||
"@babel/runtime@7.6.0":
|
|
||||||
version "7.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205"
|
|
||||||
integrity sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime "^0.13.2"
|
|
||||||
|
|
||||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5":
|
|
||||||
version "7.5.5"
|
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==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.2"
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
"@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3":
|
|
||||||
version "7.7.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a"
|
|
||||||
integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime "^0.13.2"
|
|
||||||
|
|
||||||
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0":
|
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0":
|
||||||
version "7.6.0"
|
version "7.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6"
|
||||||
@ -5993,10 +5972,10 @@ cornerstone-math@^0.1.8:
|
|||||||
resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5"
|
resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5"
|
||||||
integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw==
|
integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw==
|
||||||
|
|
||||||
cornerstone-tools@^4.0.9:
|
cornerstone-tools@^4.8.0:
|
||||||
version "4.6.2"
|
version "4.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.6.2.tgz#f46eac15ac027ed8649eced46ab0ed6f71ada982"
|
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.8.0.tgz#1972546e13e9a09b8aa25a3541ffc345283eec01"
|
||||||
integrity sha512-YlUBkMr0B1PPA/mKPgqyedvOYHNXXLyDmH3I59ipDRi2Huuw64hOxwxPiknibPHD9mBv6SpHN1ybD9i5KOTExQ==
|
integrity sha512-PO7/jYbVwc+ddF9JW/o/JXqNPFZV7y1PUTzlRATC9FeXLXNKIkSFBCF5SA1EGJoyjzRYGDBgTZuTYhfm96AlAA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "7.1.2"
|
"@babel/runtime" "7.1.2"
|
||||||
cornerstone-math "0.1.7"
|
cornerstone-math "0.1.7"
|
||||||
@ -16657,11 +16636,6 @@ regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
|
|||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||||
|
|
||||||
regenerator-runtime@^0.12.0:
|
|
||||||
version "0.12.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
|
|
||||||
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
|
|
||||||
|
|
||||||
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2:
|
regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.2:
|
||||||
version "0.13.3"
|
version "0.13.3"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user