diff --git a/platform/core/src/index.js b/platform/core/src/index.js index 6c1d70d71..1a2106f7d 100644 --- a/platform/core/src/index.js +++ b/platform/core/src/index.js @@ -23,8 +23,6 @@ import { UINotificationService, UIModalService, UIDialogService, - UIContextMenuService, - UILabellingFlowService, } from './services'; const OHIF = { @@ -55,8 +53,6 @@ const OHIF = { UINotificationService, UIModalService, UIDialogService, - UIContextMenuService, - UILabellingFlowService, }; export { @@ -86,8 +82,6 @@ export { UINotificationService, UIModalService, UIDialogService, - UIContextMenuService, - UILabellingFlowService, }; export { OHIF }; diff --git a/platform/core/src/index.test.js b/platform/core/src/index.test.js index d823c3d3e..3026ffd50 100644 --- a/platform/core/src/index.test.js +++ b/platform/core/src/index.test.js @@ -13,8 +13,6 @@ describe('Top level exports', () => { 'UINotificationService', 'UIModalService', 'UIDialogService', - 'UIContextMenuService', - 'UILabellingFlowService', // 'utils', 'studies', diff --git a/platform/core/src/services/UIContextMenuService/index.js b/platform/core/src/services/UIContextMenuService/index.js deleted file mode 100644 index dc27b774c..000000000 --- a/platform/core/src/services/UIContextMenuService/index.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * UI Context Menu - * - * @typedef {Object} ContextMenuProps - * @property {Event} event The event with tool information. - */ - -const name = 'UIContextMenuService'; - -const publicAPI = { - name, - hide: _hide, - show: _show, - setServiceImplementation, -}; - -const serviceImplementation = { - _show: () => console.warn('show() NOT IMPLEMENTED'), - _hide: () => console.warn('hide() NOT IMPLEMENTED'), -}; - -/** - * Show a new UI ContextMenu dialog; - * - * @param {ContextMenuProps} props { event } - */ -function _show({ event }) { - return serviceImplementation._show({ - event, - }); -} - -/** - * Hide a UI ContextMenu dialog; - * - */ -function _hide() { - return serviceImplementation._hide(); -} - -/** - * - * - * @param {*} { - * show: showImplementation, - * hide: hideImplementation, - * } - */ -function setServiceImplementation({ - show: showImplementation, - hide: hideImplementation, -}) { - if (showImplementation) { - serviceImplementation._show = showImplementation; - } - if (hideImplementation) { - serviceImplementation._hide = hideImplementation; - } -} - -export default { - name, - create: ({ configuration = {} }) => { - return publicAPI; - }, -}; diff --git a/platform/core/src/services/UILabellingFlowService/index.js b/platform/core/src/services/UILabellingFlowService/index.js deleted file mode 100644 index 7f374b1e7..000000000 --- a/platform/core/src/services/UILabellingFlowService/index.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * UI Labelling Flow - * - * @typedef {Object} LabellingFlowProps - * @property {Object} defaultPosition The position of the labelling dialog. - * @property {boolean} centralize conditional to center the labelling dialog. - * @property {Object} props The labelling props. - * - */ - -const name = 'UILabellingFlowService'; - -const publicAPI = { - name, - show: _show, - hide: _hide, - setServiceImplementation, -}; - -const serviceImplementation = { - _show: () => console.warn('show() NOT IMPLEMENTED'), - _hide: () => console.warn('hide() NOT IMPLEMENTED'), -}; - -/** - * Hide a UI LabellingFlow dialog; - * - */ -function _hide() { - return serviceImplementation._hide(); -} - -/** - * Show a new UI LabellingFlow dialog; - * - * @param {LabellingFlowProps} props { defaultPosition, centralize, props } - */ -function _show({ defaultPosition, centralize, props }) { - return serviceImplementation._show({ - defaultPosition, - centralize, - props, - }); -} - -/** - * - * - * @param {*} { - * show: showImplementation, - * hide: hideImplementation, - * } - */ -function setServiceImplementation({ - show: showImplementation, - hide: hideImplementation, -}) { - if (showImplementation) { - serviceImplementation._show = showImplementation; - } - if (hideImplementation) { - serviceImplementation._hide = hideImplementation; - } -} - -export default { - name, - create: ({ configuration = {} }) => { - return publicAPI; - }, -}; diff --git a/platform/core/src/services/index.js b/platform/core/src/services/index.js index f2e8ff864..9ac507c50 100644 --- a/platform/core/src/services/index.js +++ b/platform/core/src/services/index.js @@ -2,14 +2,10 @@ import ServicesManager from './ServicesManager.js'; import UINotificationService from './UINotificationService'; import UIModalService from './UIModalService'; import UIDialogService from './UIDialogService'; -import UIContextMenuService from './UIContextMenuService'; -import UILabellingFlowService from './UILabellingFlowService'; export { UINotificationService, UIModalService, UIDialogService, - UIContextMenuService, - UILabellingFlowService, ServicesManager, }; diff --git a/platform/viewer/src/connectedComponents/ToolContextMenu.css b/platform/ui/src/components/contextMenu/ContextMenu.css similarity index 65% rename from platform/viewer/src/connectedComponents/ToolContextMenu.css rename to platform/ui/src/components/contextMenu/ContextMenu.css index 66fa17495..4facf3c41 100644 --- a/platform/viewer/src/connectedComponents/ToolContextMenu.css +++ b/platform/ui/src/components/contextMenu/ContextMenu.css @@ -1,20 +1,19 @@ -.ToolContextMenu { +.ContextMenu { position: relative; background-color: white; - border: 1px solid white; border-radius: 5px; z-index: 1000; display: block; width: 170px; } -.ToolContextMenu > ul { +.ContextMenu>ul { list-style-type: none; padding-left: 0; margin: 0; } -.ToolContextMenu > ul > li > button { +.ContextMenu>ul>li>button { padding: 10px; font-size: 14px; border: none; @@ -25,10 +24,10 @@ background: none; } -.ToolContextMenu > ul > li > button:hover { +.ContextMenu>ul>li>button:hover { color: #16202b; } -.ToolContextMenu > ul > li > button:active { +.ContextMenu>ul>li>button:active { color: #79f9fe; } diff --git a/platform/ui/src/components/contextMenu/ContextMenu.js b/platform/ui/src/components/contextMenu/ContextMenu.js new file mode 100644 index 000000000..9dc5a174f --- /dev/null +++ b/platform/ui/src/components/contextMenu/ContextMenu.js @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import './ContextMenu.css'; + +const ContextMenu = ({ items, onClick }) => { + return ( +
+ +
+ ); +}; + +ContextMenu.propTypes = { + items: PropTypes.array.isRequired, + onClick: PropTypes.func.isRequired, +}; + +export default ContextMenu; diff --git a/platform/ui/src/components/contextMenu/index.js b/platform/ui/src/components/contextMenu/index.js new file mode 100644 index 000000000..5cb9a3ad5 --- /dev/null +++ b/platform/ui/src/components/contextMenu/index.js @@ -0,0 +1 @@ +export { default as ContextMenu } from './ContextMenu.js'; diff --git a/platform/ui/src/components/index.js b/platform/ui/src/components/index.js index 81ae9dfe4..5daca57b1 100644 --- a/platform/ui/src/components/index.js +++ b/platform/ui/src/components/index.js @@ -17,6 +17,7 @@ import { RoundedButtonGroup } from './roundedButtonGroup'; import { SelectTree } from './selectTree'; import { SimpleDialog } from './simpleDialog'; import { OHIFModal } from './ohifModal'; +import { ContextMenu } from './contextMenu'; import { PageToolbar, StudyList, @@ -27,6 +28,7 @@ import { ToolbarSection } from './toolbarSection'; import { Tooltip } from './tooltip'; export { + ContextMenu, Checkbox, CineDialog, ViewportDownloadForm, diff --git a/platform/ui/src/contextProviders/ContextMenuProvider.js b/platform/ui/src/contextProviders/ContextMenuProvider.js deleted file mode 100644 index 1d1ee22c3..000000000 --- a/platform/ui/src/contextProviders/ContextMenuProvider.js +++ /dev/null @@ -1,147 +0,0 @@ -import React, { - createContext, - useContext, - useEffect, - useCallback, -} from 'react'; -import PropTypes from 'prop-types'; - -import { useDialog } from '@ohif/ui'; -import { useLabellingFlow } from '@ohif/ui'; - -const ContextMenuContext = createContext(null); -const { Provider } = ContextMenuContext; - -export const useContextMenu = () => useContext(ContextMenuContext); - -const ContextMenuProvider = ({ - children, - service, - contextMenuComponent: ContextMenuComponent, - onDelete, -}) => { - const { create, dismiss } = useDialog(); - const { show: showLabellingFlow } = useLabellingFlow(); - - /** - * Sets the implementation of a context menu service that can be used by extensions. - * - * @returns void - */ - useEffect(() => { - if (service) { - service.setServiceImplementation({ - show, - hide, - }); - } - }, [hide, service, show]); - - const hide = useCallback(() => dismiss({ id: 'context-menu' }), [dismiss]); - - /** - * Show the context menu and override its configuration props. - * - * @param {ContextMenuProps} props { eventData, isTouchEvent, onClose, visible } - * @returns void - */ - const show = useCallback( - ({ event }) => { - hide(); - create({ - id: 'context-menu', - isDraggable: false, - preservePosition: false, - content: ContextMenuComponent, - contentProps: { - eventData: event, - onDelete: (nearbyToolData, eventData) => - onDelete(nearbyToolData, eventData), - onClose: () => dismiss({ id: 'context-menu' }), - onSetLabel: (eventData, measurementData) => - showLabellingFlow({ - event: eventData, - centralize: true, - props: { - measurementData, - skipAddLabelButton: true, - editLocation: true, - }, - }), - onSetDescription: (eventData, measurementData) => - showLabellingFlow({ - event: eventData, - centralize: false, - defaultPosition: _getDefaultPosition(eventData), - props: { - measurementData, - editDescriptionOnDialog: true, - }, - }), - }, - defaultPosition: _getDefaultPosition(event), - }); - }, - [ContextMenuComponent, create, dismiss, hide, onDelete, showLabellingFlow] - ); - - const _getDefaultPosition = event => ({ - x: (event && event.currentPoints.client.x) || 0, - y: (event && event.currentPoints.client.y) || 0, - }); - - return ( - - {children} - - ); -}; - -/** - * Higher Order Component to use the context menu methods through a Class Component. - * - * @returns - */ -export const withContextMenu = Component => { - return function WrappedComponent(props) { - const { show, hide } = useContextMenu(); - return ( - - ); - }; -}; - -ContextMenuProvider.defaultProps = { - service: null, -}; - -ContextMenuProvider.propTypes = { - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - ]).isRequired, - service: PropTypes.shape({ - setServiceImplementation: PropTypes.func, - }), - contextMenuComponent: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - PropTypes.func, - ]).isRequired, - onDelete: PropTypes.func.isRequired, -}; - -export default ContextMenuProvider; - -export const ContextMenuConsumer = ContextMenuContext.Consumer; diff --git a/platform/ui/src/contextProviders/LabellingFlowProvider.js b/platform/ui/src/contextProviders/LabellingFlowProvider.js deleted file mode 100644 index a5c266d8f..000000000 --- a/platform/ui/src/contextProviders/LabellingFlowProvider.js +++ /dev/null @@ -1,136 +0,0 @@ -import React, { - createContext, - useContext, - useEffect, - useCallback, -} from 'react'; -import PropTypes from 'prop-types'; - -import { useDialog } from './DialogProvider'; - -const LabellingFlowContext = createContext(null); -const { Provider } = LabellingFlowContext; - -export const useLabellingFlow = () => useContext(LabellingFlowContext); - -const LabellingFlowProvider = ({ - children, - service, - labellingComponent: LabellingComponent, - onUpdateLabelling, -}) => { - const { create, dismiss } = useDialog(); - - /** - * Sets the implementation of a labelling flow service that can be used by extensions. - * - * @returns void - */ - useEffect(() => { - if (service) { - service.setServiceImplementation({ - show, - hide, - }); - } - }, [hide, service, show]); - - const hide = useCallback(() => dismiss({ id: 'labelling' }), [dismiss]); - - const show = useCallback( - ({ centralize, defaultPosition, props }) => { - hide(); - create({ - id: 'labelling', - centralize, - isDraggable: false, - showOverlay: true, - content: LabellingComponent, - defaultPosition, - contentProps: { - visible: true, - measurementData: props.measurementData, - labellingDoneCallback: () => dismiss({ id: 'labelling' }), - updateLabelling: labellingData => - _updateLabellingHandler(labellingData, props.measurementData), - ...props, - }, - }); - }, - [LabellingComponent, _updateLabellingHandler, create, dismiss, hide] - ); - - const _updateLabellingHandler = useCallback( - (labellingData, measurementData) => { - const { location, description, response } = labellingData; - - if (location) { - measurementData.location = location; - } - - measurementData.description = description || ''; - - if (response) { - measurementData.response = response; - } - - onUpdateLabelling(labellingData, measurementData); - }, - [onUpdateLabelling] - ); - - return ( - - {children} - - ); -}; - -/** - * Higher Order Component to use the labelling flow methods through a Class Component. - * - * @returns - */ -export const withLabellingFlow = Component => { - return function WrappedComponent(props) { - const { show, hide } = useLabellingFlow(); - return ( - - ); - }; -}; - -LabellingFlowProvider.defaultProps = { - service: null, -}; - -LabellingFlowProvider.propTypes = { - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - ]).isRequired, - service: PropTypes.shape({ - setServiceImplementation: PropTypes.func, - }), - labellingComponent: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - PropTypes.func, - ]).isRequired, - onUpdateLabelling: PropTypes.func.isRequired, -}; - -export default LabellingFlowProvider; - -export const LabellingFlowConsumer = LabellingFlowContext.Consumer; diff --git a/platform/ui/src/contextProviders/index.js b/platform/ui/src/contextProviders/index.js index 821b6ce9e..e4ca06143 100644 --- a/platform/ui/src/contextProviders/index.js +++ b/platform/ui/src/contextProviders/index.js @@ -18,15 +18,3 @@ export { withDialog, useDialog, } from './DialogProvider.js'; -export { - default as ContextMenuProvider, - withContextMenu, - useContextMenu, - ContextMenuConsumer, -} from './ContextMenuProvider.js'; -export { - default as LabellingFlowProvider, - withLabellingFlow, - useLabellingFlow, - LabellingFlowConsumer, -} from './LabellingFlowProvider.js'; diff --git a/platform/ui/src/index.js b/platform/ui/src/index.js index 1672d4b13..88e3aee77 100644 --- a/platform/ui/src/index.js +++ b/platform/ui/src/index.js @@ -1,4 +1,5 @@ import { + ContextMenu, Checkbox, CineDialog, ViewportDownloadForm, @@ -60,14 +61,6 @@ import { ModalConsumer, useModal, withModal, - ContextMenuProvider, - ContextMenuConsumer, - useContextMenu, - withContextMenu, - LabellingFlowProvider, - LabellingFlowConsumer, - useLabellingFlow, - withLabellingFlow, } from './contextProviders'; export { @@ -80,6 +73,7 @@ export { TextArea, TextInput, CineDialog, + ContextMenu, ViewportDownloadForm, ExpandableToolMenu, Icon, @@ -125,14 +119,6 @@ export { DialogProvider, withDialog, useDialog, - ContextMenuProvider, - ContextMenuConsumer, - useContextMenu, - withContextMenu, - LabellingFlowProvider, - LabellingFlowConsumer, - useLabellingFlow, - withLabellingFlow, // Hooks useDebounce, useMedia, diff --git a/platform/viewer/src/App.js b/platform/viewer/src/App.js index 9f01c3e29..801e6df21 100644 --- a/platform/viewer/src/App.js +++ b/platform/viewer/src/App.js @@ -8,9 +8,6 @@ import { hot } from 'react-hot-loader/root'; import OHIFCornerstoneExtension from '@ohif/extension-cornerstone'; -import ToolContextMenu from './connectedComponents/ToolContextMenu'; -import LabellingFlow from './components/Labelling/LabellingFlow'; - import { SnackbarProvider, ModalProvider, @@ -18,11 +15,6 @@ import { OHIFModal, } from '@ohif/ui'; -import { - LabellingFlowProvider, - ContextMenuProvider, -} from './appCustomProviders'; - import { CommandsManager, ExtensionManager, @@ -31,8 +23,6 @@ import { UINotificationService, UIModalService, UIDialogService, - UIContextMenuService, - UILabellingFlowService, utils, redux as reduxOHIF, } from '@ohif/core'; @@ -135,13 +125,7 @@ class App extends Component { } = this._appConfig; this.initUserManager(oidc); - _initServices([ - UINotificationService, - UIModalService, - UIDialogService, - UIContextMenuService, - UILabellingFlowService, - ]); + _initServices([UINotificationService, UIModalService, UIDialogService]); _initExtensions( [...defaultExtensions, ...extensions], cornerstoneExtensionConfig @@ -161,9 +145,7 @@ class App extends Component { const { UINotificationService, UIDialogService, - UILabellingFlowService, UIModalService, - UIContextMenuService, } = servicesManager.services; if (this._userManager) { @@ -181,21 +163,9 @@ class App extends Component { modal={OHIFModal} service={UIModalService} > - - - - - + @@ -218,19 +188,7 @@ class App extends Component { - - - - - + diff --git a/platform/viewer/src/appCustomProviders/ContextMenuProvider/ContextMenuProvider.js b/platform/viewer/src/appCustomProviders/ContextMenuProvider/ContextMenuProvider.js deleted file mode 100644 index 4cd75d9f8..000000000 --- a/platform/viewer/src/appCustomProviders/ContextMenuProvider/ContextMenuProvider.js +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { ContextMenuProvider } from '@ohif/ui'; - -const CustomContextMenuProvider = ({ - children, - service, - contextMenuComponent, - commandsManager, -}) => { - const onDeleteHandler = (nearbyToolData, eventData) => { - const element = eventData.element; - commandsManager.runCommand('removeToolState', { - element, - toolType: nearbyToolData.toolType, - tool: nearbyToolData.tool, - }); - }; - - return ( - - {children} - - ); -}; - -CustomContextMenuProvider.defaultProps = { - service: null, -}; - -CustomContextMenuProvider.propTypes = { - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - ]).isRequired, - service: PropTypes.shape({ - setServiceImplementation: PropTypes.func, - }), - contextMenuComponent: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - PropTypes.func, - ]).isRequired, - commandsManager: PropTypes.object.isRequired, -}; - -export default CustomContextMenuProvider; diff --git a/platform/viewer/src/appCustomProviders/LabellingFlowProvider/LabellingFlowProvider.js b/platform/viewer/src/appCustomProviders/LabellingFlowProvider/LabellingFlowProvider.js deleted file mode 100644 index 097a0350f..000000000 --- a/platform/viewer/src/appCustomProviders/LabellingFlowProvider/LabellingFlowProvider.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { LabellingFlowProvider } from '@ohif/ui'; - -const CustomLabellingFlowProvider = ({ - children, - service, - labellingComponent, - commandsManager, -}) => { - const onUpdateLabellingHandler = (labellingData, measurementData) => { - commandsManager.runCommand( - 'updateTableWithNewMeasurementData', - measurementData - ); - }; - - return ( - - {children} - - ); -}; - -CustomLabellingFlowProvider.defaultProps = { - service: null, -}; - -CustomLabellingFlowProvider.propTypes = { - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - ]).isRequired, - service: PropTypes.shape({ - setServiceImplementation: PropTypes.func, - }), - labellingComponent: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - PropTypes.func, - ]).isRequired, - commandsManager: PropTypes.object.isRequired, -}; - -export default CustomLabellingFlowProvider; diff --git a/platform/viewer/src/appCustomProviders/index.js b/platform/viewer/src/appCustomProviders/index.js deleted file mode 100644 index f66c8e462..000000000 --- a/platform/viewer/src/appCustomProviders/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export { - default as LabellingFlowProvider, -} from './LabellingFlowProvider/LabellingFlowProvider.js'; -export { - default as ContextMenuProvider, -} from './ContextMenuProvider/ContextMenuProvider.js'; diff --git a/platform/viewer/src/appExtensions/MeasurementsPanel/index.js b/platform/viewer/src/appExtensions/MeasurementsPanel/index.js index cfa471964..41d1f2f99 100644 --- a/platform/viewer/src/appExtensions/MeasurementsPanel/index.js +++ b/platform/viewer/src/appExtensions/MeasurementsPanel/index.js @@ -2,6 +2,8 @@ import React from 'react'; import ConnectedMeasurementTable from './ConnectedMeasurementTable.js'; import init from './init.js'; +import LabellingFlow from '../../components/Labelling/LabellingFlow'; + export default { /** * Only required property. Should be a unique value across all extensions. @@ -13,32 +15,51 @@ export default { }, getPanelModule({ servicesManager, commandsManager }) { - const { UILabellingFlowService, UINotificationService } = servicesManager.services; + const { UINotificationService, UIDialogService } = servicesManager.services; + + const showLabellingDialog = (props, measurementData) => { + if (!UIDialogService) { + console.warn('Unable to show dialog; no UI Dialog Service available.'); + return; + } + + UIDialogService.dismiss({ id: 'labelling' }); + UIDialogService.create({ + id: 'labelling', + centralize: true, + isDraggable: false, + showOverlay: true, + content: LabellingFlow, + contentProps: { + measurementData, + labellingDoneCallback: () => + UIDialogService.dismiss({ id: 'labelling' }), + updateLabelling: ({ location, description, response }) => { + measurementData.location = location || measurementData.location; + measurementData.description = description || ''; + measurementData.response = response || measurementData.response; + + commandsManager.runCommand( + 'updateTableWithNewMeasurementData', + measurementData + ); + }, + ...props, + }, + }); + }; + const ExtendedConnectedMeasurementTable = () => ( { - if (UILabellingFlowService) { - UILabellingFlowService.show({ - centralize: true, - props: { - skipAddLabelButton: true, - editLocation: true, - measurementData: tool, - }, - }); - } - }} - onEditDescription={tool => { - if (UILabellingFlowService) { - UILabellingFlowService.show({ - centralize: true, - props: { - editDescriptionOnDialog: true, - measurementData: tool, - }, - }); - } - }} + onRelabel={tool => + showLabellingDialog( + { editLocation: true, skipAddLabelButton: true }, + tool + ) + } + onEditDescription={tool => + showLabellingDialog({ editDescriptionOnDialog: true }, tool) + } onSaveComplete={message => { if (UINotificationService) { UINotificationService.show(message); diff --git a/platform/viewer/src/appExtensions/MeasurementsPanel/init.js b/platform/viewer/src/appExtensions/MeasurementsPanel/init.js index 0c299b81a..924081e9e 100644 --- a/platform/viewer/src/appExtensions/MeasurementsPanel/init.js +++ b/platform/viewer/src/appExtensions/MeasurementsPanel/init.js @@ -3,6 +3,9 @@ import cornerstone from 'cornerstone-core'; import csTools from 'cornerstone-tools'; import throttle from 'lodash.throttle'; +import LabellingFlow from '../../components/Labelling/LabellingFlow'; +import ToolContextMenu from '../../connectedComponents/ToolContextMenu'; + const { onAdded, onRemoved, @@ -29,10 +32,7 @@ export default function init({ commandsManager, configuration, }) { - const { - UIContextMenuService, - UILabellingFlowService, - } = servicesManager.services; + const { UIDialogService } = servicesManager.services; // TODO: MEASUREMENT_COMPLETED (not present in initial implementation) const onMeasurementsChanged = (action, event) => { @@ -46,21 +46,108 @@ export default function init({ 'labelmapModified' ); - const onRightClick = event => { - if (UIContextMenuService) { - UIContextMenuService.show({ event: event.detail }); + const _getDefaultPosition = event => ({ + x: (event && event.currentPoints.client.x) || 0, + y: (event && event.currentPoints.client.y) || 0, + }); + + const _updateLabellingHandler = (labellingData, measurementData) => { + const { location, description, response } = labellingData; + + if (location) { + measurementData.location = location; } + + measurementData.description = description || ''; + + if (response) { + measurementData.response = response; + } + + commandsManager.runCommand( + 'updateTableWithNewMeasurementData', + measurementData + ); + }; + + const showLabellingDialog = (props, contentProps, measurementData) => { + if (!UIDialogService) { + console.warn('Unable to show dialog; no UI Dialog Service available.'); + return; + } + + UIDialogService.create({ + id: 'labelling', + isDraggable: false, + showOverlay: true, + content: LabellingFlow, + contentProps: { + measurementData, + labellingDoneCallback: () => + UIDialogService.dismiss({ id: 'labelling' }), + updateLabelling: labellingData => + _updateLabellingHandler(labellingData, measurementData), + ...contentProps, + }, + ...props, + }); + }; + + const onRightClick = event => { + if (!UIDialogService) { + console.warn('Unable to show dialog; no UI Dialog Service available.'); + return; + } + + UIDialogService.dismiss({ id: 'context-menu' }); + UIDialogService.create({ + id: 'context-menu', + isDraggable: false, + preservePosition: false, + defaultPosition: _getDefaultPosition(event.detail), + content: ToolContextMenu, + contentProps: { + eventData: event.detail, + onDelete: (nearbyToolData, eventData) => { + const element = eventData.element; + commandsManager.runCommand('removeToolState', { + element, + toolType: nearbyToolData.toolType, + tool: nearbyToolData.tool, + }); + }, + onClose: () => UIDialogService.dismiss({ id: 'context-menu' }), + onSetLabel: (eventData, measurementData) => { + showLabellingDialog( + { centralize: true, isDraggable: false }, + { skipAddLabelButton: true, editLocation: true }, + measurementData + ); + }, + onSetDescription: (eventData, measurementData) => { + showLabellingDialog( + { defaultPosition: _getDefaultPosition(eventData) }, + { editDescriptionOnDialog: true }, + measurementData + ); + }, + }, + }); }; const onTouchPress = event => { - if (UIContextMenuService) { - UIContextMenuService.show({ - event: event.detail, - props: { - isTouchEvent: true, - }, - }); + if (!UIDialogService) { + console.warn('Unable to show dialog; no UI Dialog Service available.'); + return; } + + UIDialogService.create({ + eventData: event.detail, + content: ToolContextMenu, + contentProps: { + isTouchEvent: true, + }, + }); }; const onTouchStart = () => resetLabelligAndContextMenu(); @@ -68,10 +155,13 @@ export default function init({ const onMouseClick = () => resetLabelligAndContextMenu(); const resetLabelligAndContextMenu = () => { - if (UILabellingFlowService && UIContextMenuService) { - UILabellingFlowService.hide(); - UIContextMenuService.hide(); + if (!UIDialogService) { + console.warn('Unable to show dialog; no UI Dialog Service available.'); + return; } + + UIDialogService.dismiss({ id: 'context-menu' }); + UIDialogService.dismiss({ id: 'labelling' }); }; // TODO: This makes scrolling painfully slow diff --git a/platform/viewer/src/connectedComponents/ToolContextMenu.js b/platform/viewer/src/connectedComponents/ToolContextMenu.js index 18bb57684..3974ad396 100644 --- a/platform/viewer/src/connectedComponents/ToolContextMenu.js +++ b/platform/viewer/src/connectedComponents/ToolContextMenu.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { commandsManager } from './../App.js'; -import './ToolContextMenu.css'; +import { ContextMenu } from '@ohif/ui'; const toolTypes = [ 'Angle', @@ -24,11 +24,13 @@ const ToolContextMenu = ({ }) => { const defaultDropdownItems = [ { + label: 'Delete measurement', actionType: 'Delete', action: ({ nearbyToolData, eventData }) => onDelete(nearbyToolData, eventData), }, { + label: 'Relabel', actionType: 'setLabel', action: ({ nearbyToolData, eventData }) => { const { tool: measurementData } = nearbyToolData; @@ -51,7 +53,10 @@ const ToolContextMenu = ({ availableToolTypes: toolTypes, }); - // Annotate tools for touch events already have a press handle to edit it, has a better UX for deleting it + /* + * Annotate tools for touch events already have a press handle to edit it, + * has a better UX for deleting it. + */ if ( isTouchEvent && nearbyToolData && @@ -63,21 +68,10 @@ const ToolContextMenu = ({ let dropdownItems = []; if (nearbyToolData) { defaultDropdownItems.forEach(item => { - item.params = { - eventData, - nearbyToolData, - }; - - if (item.actionType === 'Delete') { - item.text = 'Delete measurement'; - } - - if (item.actionType === 'setLabel') { - item.text = 'Relabel'; - } + item.params = { eventData, nearbyToolData }; if (item.actionType === 'setDescription') { - item.text = `${ + item.label = `${ nearbyToolData.tool.description ? 'Edit' : 'Add' } Description`; } @@ -89,7 +83,7 @@ const ToolContextMenu = ({ return dropdownItems; }; - const itemOnClickHandler = (action, params, onClose) => { + const onClickHandler = ({ action, params }) => { action(params); if (onClose) { onClose(); @@ -99,23 +93,9 @@ const ToolContextMenu = ({ const dropdownItems = getDropdownItems(eventData, isTouchEvent); return ( - dropdownItems.length && - eventData && ( -
-
    - {dropdownItems.map(({ params, action, text, actionType }) => ( -
  • - -
  • - ))} -
-
- ) +
+ ; +
); }; @@ -123,6 +103,9 @@ ToolContextMenu.propTypes = { isTouchEvent: PropTypes.bool.isRequired, eventData: PropTypes.object, onClose: PropTypes.func, + onSetDescription: PropTypes.func, + onSetLabel: PropTypes.func, + onDelete: PropTypes.func, }; ToolContextMenu.defaultProps = {