diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.jsx b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.jsx
new file mode 100644
index 000000000..b8d2c72e1
--- /dev/null
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.jsx
@@ -0,0 +1,81 @@
+import React, { useContext } from 'react';
+import PropTypes from 'prop-types';
+import { Machine } from 'xstate';
+import { useMachine } from '@xstate/react';
+import {
+ machineConfiguration,
+ defaultOptions,
+} from './measurementTrackingMachine';
+
+const TrackedMeasurementsContext = React.createContext();
+TrackedMeasurementsContext.displayName = 'TrackedMeasurementsContext';
+const useTrackedMeasurements = () => useContext(TrackedMeasurementsContext);
+
+/**
+ *
+ * @param {*} param0
+ */
+function TrackedMeasurementsContextProvider(
+ UIViewportDialogService,
+ { children }
+) {
+ function promptUser() {
+ // TODO: ... ActiveViewport? Or Study + Series --> Viewport?
+ // Let's just use zero for meow?
+ return new Promise(function(resolve, reject) {
+ const handleSubmit = result => {
+ UIViewportDialogService.hide();
+ resolve(result);
+ };
+
+ UIViewportDialogService.show({
+ viewportIndex: 0,
+ type: 'info',
+ message: 'Would you like to track?',
+ actions: [
+ { type: 'cancel', text: 'No', value: 0 },
+ { type: 'secondary', text: 'No, do not ask again', value: -1 },
+ { type: 'primary', text: 'Yes', value: 1 },
+ ],
+ onSubmit: handleSubmit,
+ });
+ });
+ }
+
+ const machOptions = Object.assign({}, defaultOptions);
+ // Merge services
+ machOptions.services = Object.assign({}, machOptions.services, {
+ shouldTrackPrompt: promptUser,
+ });
+
+ console.log(
+ '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~',
+ machOptions
+ );
+
+ const measurementTrackingMachine = Machine(machineConfiguration, machOptions);
+
+ const [
+ trackedMeasurements,
+ sendTrackedMeasurementsEvent,
+ trackedMeasurementsService,
+ ] = useMachine(measurementTrackingMachine);
+
+ return (
+
+ {children}
+
+ );
+}
+
+TrackedMeasurementsContextProvider.propTypes = {
+ children: PropTypes.oneOf([PropTypes.func, PropTypes.node]),
+};
+
+export {
+ TrackedMeasurementsContext,
+ TrackedMeasurementsContextProvider,
+ useTrackedMeasurements,
+};
diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/index.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/index.js
index ca158c1de..1385a7f84 100644
--- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/index.js
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/index.js
@@ -1,45 +1,5 @@
-import React, { useContext } from 'react';
-import PropTypes from 'prop-types';
-import { useMachine } from '@xstate/react';
-import {
- measurementTrackingMachine,
- defaultOptions,
-} from './measurementTrackingMachine';
-
-const TrackedMeasurementsContext = React.createContext();
-
-TrackedMeasurementsContext.displayName = 'TrackedMeasurementsContext';
-
-const useTrackedMeasurements = () => useContext(TrackedMeasurementsContext);
-
-/**
- *
- * @param {*} param0
- */
-function TrackedMeasurementsContextProvider({ children }) {
- // TODO: Configure `UIViewportNotificationService` for appropriate actions
- const measurementTrackingMachineOptions = Object.assign({}, defaultOptions);
- const [
- trackedMeasurements,
- sendTrackedMeasurementsEvent,
- trackedMeasurementsService,
- ] = useMachine(measurementTrackingMachine, measurementTrackingMachineOptions);
-
- return (
-
- {children}
-
- );
-}
-
-TrackedMeasurementsContextProvider.propTypes = {
- children: PropTypes.oneOf([PropTypes.func, PropTypes.node]),
-};
-
export {
TrackedMeasurementsContext,
TrackedMeasurementsContextProvider,
useTrackedMeasurements,
-};
+} from './TrackedMeasurementsContext.jsx';
diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js
index 21e9f2b35..1ba59ca65 100644
--- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js
+++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/measurementTrackingMachine.js
@@ -27,7 +27,7 @@ const machineConfiguration = {
prompt: {
invoke: {
id: 'shouldTrackPrompt',
- src: () => confirmDialog('Should we start tracking?'),
+ src: 'shouldTrackPrompt',
onDone: {
target: 'validateResponse',
actions: assign({ promptResponse: (ctx, evt) => evt.data }),
@@ -135,6 +135,11 @@ function confirmDialog(msg) {
}
const defaultOptions = {
+ services: {
+ shouldTrackPrompt: () => {
+ return confirmDialog('Should we start tracking?');
+ },
+ },
actions: {
clearContext: assign({
prevTrackedStudy: '',
@@ -186,11 +191,13 @@ const defaultOptions = {
},
};
-const measurementTrackingMachine = Machine(machineConfiguration, defaultOptions);
+// const measurementTrackingMachine = Machine(
+// machineConfiguration,
+// defaultOptions
+// );
// .transition(state, eventArgument).value
// const service = interpret(measurementTrackingMachine).start();
// .send(event): nextState
// .state (getter)
// .onTransition(state => { state.vale })
-export { defaultOptions, machineConfiguration, measurementTrackingMachine };
-export default measurementTrackingMachine;
+export { defaultOptions, machineConfiguration };
diff --git a/extensions/measurement-tracking/src/getContextModule.js b/extensions/measurement-tracking/src/getContextModule.js
index bb0fd4043..573a00fa2 100644
--- a/extensions/measurement-tracking/src/getContextModule.js
+++ b/extensions/measurement-tracking/src/getContextModule.js
@@ -4,12 +4,18 @@ import {
useTrackedMeasurements,
} from './contexts';
-function getContextModule() {
+function getContextModule({ servicesManager }) {
+ const { UIViewportDialogService } = servicesManager.services;
+ const BoundTrackedMeasurementsContextProvider = TrackedMeasurementsContextProvider.bind(
+ null,
+ UIViewportDialogService
+ );
+
return [
{
name: 'TrackedMeasurementsContext',
context: TrackedMeasurementsContext,
- provider: TrackedMeasurementsContextProvider,
+ provider: BoundTrackedMeasurementsContextProvider,
},
];
}
diff --git a/extensions/measurement-tracking/src/getViewportModule.js b/extensions/measurement-tracking/src/getViewportModule.js
index 1f657ec37..b494f418b 100644
--- a/extensions/measurement-tracking/src/getViewportModule.js
+++ b/extensions/measurement-tracking/src/getViewportModule.js
@@ -1,7 +1,7 @@
import React from 'react';
const Component = React.lazy(() => {
- return import('./viewports/OHIFCornerstoneViewport');
+ return import('./viewports/TrackedCornerstoneViewport');
});
const OHIFCornerstoneViewport = props => {
diff --git a/extensions/measurement-tracking/src/viewports/OHIFCornerstoneViewport.js b/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js
similarity index 79%
rename from extensions/measurement-tracking/src/viewports/OHIFCornerstoneViewport.js
rename to extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js
index 8bcda29c8..4b725622a 100644
--- a/extensions/measurement-tracking/src/viewports/OHIFCornerstoneViewport.js
+++ b/extensions/measurement-tracking/src/viewports/TrackedCornerstoneViewport.js
@@ -3,12 +3,16 @@ import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import CornerstoneViewport from 'react-cornerstone-viewport';
import OHIF, { DicomMetadataStore } from '@ohif/core';
-import { ViewportActionBar, useViewportGrid } from '@ohif/ui';
+import {
+ Notification,
+ ViewportActionBar,
+ useViewportGrid,
+ useViewportDialog,
+} from '@ohif/ui';
import debounce from 'lodash.debounce';
import throttle from 'lodash.throttle';
import { useTrackedMeasurements } from './../getContextModule';
-
// const cine = viewportSpecificData.cine;
// isPlaying = cine.isPlaying === true;
@@ -16,7 +20,7 @@ import { useTrackedMeasurements } from './../getContextModule';
const { StackManager } = OHIF.utils;
-function OHIFCornerstoneViewport({
+function TrackedCornerstoneViewport({
children,
dataSource,
displaySet,
@@ -24,6 +28,10 @@ function OHIFCornerstoneViewport({
}) {
const [trackedMeasurements] = useTrackedMeasurements();
const [{ viewports }, dispatchViewportGrid] = useViewportGrid();
+ // viewportIndex, onSubmit
+ const [viewportDialogState, viewportDialogApi] = useViewportDialog();
+ console.warn('VIEWPORT DIALOG', viewportDialogState, viewportDialogApi);
+
const [viewportData, setViewportData] = useState(null);
// TODO: Still needed? Better way than import `OHIF` and destructure?
// Why is this managed by `core`?
@@ -98,7 +106,7 @@ function OHIFCornerstoneViewport({
// );
// TODO: This display contains the meta for all instances.
// That can't be right...
- console.log('DISPLAYSET', displaySet)
+ console.log('DISPLAYSET', displaySet);
// const seriesMeta = DicomMetadataStore.getSeries(this.props.displaySet.StudyInstanceUID, '');
// console.log(seriesMeta);
@@ -145,22 +153,35 @@ function OHIFCornerstoneViewport({
},
}}
/>
-
- {childrenWithProps}
+ {/* TODO: Viewport interface to accept stack or layers of content like this? */}
+
+
+
+ {viewportDialogState.viewportIndex === viewportIndex && (
+
+ )}
+
+ {childrenWithProps}
+
>
);
}
-OHIFCornerstoneViewport.propTypes = {
+TrackedCornerstoneViewport.propTypes = {
displaySet: PropTypes.object.isRequired,
viewportIndex: PropTypes.number.isRequired,
dataSource: PropTypes.object,
@@ -168,7 +189,7 @@ OHIFCornerstoneViewport.propTypes = {
customProps: PropTypes.object,
};
-OHIFCornerstoneViewport.defaultProps = {
+TrackedCornerstoneViewport.defaultProps = {
customProps: {},
};
@@ -207,6 +228,6 @@ async function _getViewportData(dataSource, displaySet) {
};
return viewportData;
-};
+}
-export default OHIFCornerstoneViewport;
+export default TrackedCornerstoneViewport;
diff --git a/platform/core/src/services/UIViewportDialogService/index.js b/platform/core/src/services/UIViewportDialogService/index.js
index 8b0aeddeb..8fdb91437 100644
--- a/platform/core/src/services/UIViewportDialogService/index.js
+++ b/platform/core/src/services/UIViewportDialogService/index.js
@@ -17,7 +17,8 @@ const publicAPI = {
};
const serviceImplementation = {
- _viewports: [],
+ _hide: () => console.warn('hide() NOT IMPLEMENTED'),
+ _show: () => console.warn('show() NOT IMPLEMENTED'),
};
/**
@@ -25,39 +26,21 @@ const serviceImplementation = {
*
* @param {ViewportDialogProps} props { content, contentProps, viewportIndex }
*/
-function _show({ content = null, contentProps = null, viewportIndex }) {
- const viewportIndexImplementation =
- (viewportIndex !== undefined &&
- serviceImplementation._viewports[viewportIndex]) ||
- {};
-
- if (!viewportIndexImplementation._show) {
- console.warn('show() NOT IMPLEMENTED');
- return;
- }
-
- return viewportIndexImplementation._show({
- content,
- contentProps,
+function _show({ viewportIndex, type, message, actions, onSubmit }) {
+ return serviceImplementation._show({
viewportIndex,
+ type,
+ message,
+ actions,
+ onSubmit,
});
}
/**
* Hides/dismisses the viewport dialog, if currently shown
- *
- * @param {*} { viewportIndex }
*/
-function _hide({ viewportIndex }) {
- const viewportIndexImplementation =
- (viewportIndex && serviceImplementation._viewports[viewportIndex]) || {};
-
- if (!viewportIndexImplementation._hide) {
- console.warn('hide() NOT IMPLEMENTED');
- return;
- }
-
- return viewportIndexImplementation._hide();
+function _hide() {
+ return serviceImplementation._hide();
}
/**
@@ -72,22 +55,12 @@ function _hide({ viewportIndex }) {
function setServiceImplementation({
hide: hideImplementation,
show: showImplementation,
- viewportIndex,
}) {
- if (viewportIndex !== undefined) {
- const newImplementations = {};
- if (hideImplementation) {
- newImplementations._hide = hideImplementation;
- }
- if (showImplementation) {
- newImplementations._show = showImplementation;
- }
-
- serviceImplementation._viewports[viewportIndex] = Object.assign(
- {},
- serviceImplementation._viewports[viewportIndex],
- newImplementations
- );
+ if (hideImplementation) {
+ serviceImplementation._hide = hideImplementation;
+ }
+ if (showImplementation) {
+ serviceImplementation._show = showImplementation;
}
}
diff --git a/platform/ui/src/components/Notification/Notification.jsx b/platform/ui/src/components/Notification/Notification.jsx
index 951c9c9ed..f980f2f07 100644
--- a/platform/ui/src/components/Notification/Notification.jsx
+++ b/platform/ui/src/components/Notification/Notification.jsx
@@ -1,9 +1,9 @@
import React from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
-import { Icon } from '@ohif/ui';
+import { Button, Icon } from '@ohif/ui';
-const Notification = ({ type, text, actionButtons }) => {
+const Notification = ({ type, message, actions, onSubmit }) => {
const iconsByType = {
error: {
icon: 'info',
@@ -35,12 +35,30 @@ const Notification = ({ type, text, actionButtons }) => {
const { icon, color } = getIconData();
return (
-
+
- {text}
+ {message}
+
+
+ {actions.map((action, index) => {
+ const isFirst = index === 0;
+ const isPrimary = action.type === 'primary';
+
+ return (
+
+ );
+ })}
-
{actionButtons}
);
};
@@ -51,8 +69,15 @@ Notification.defaultProps = {
Notification.propTypes = {
type: PropTypes.string,
- text: PropTypes.string.isRequired,
- actionButtons: PropTypes.node.isRequired,
+ message: PropTypes.string.isRequired,
+ actions: PropTypes.arrayOf(
+ PropTypes.shape({
+ text: PropTypes.string.isRequired,
+ value: PropTypes.any.isRequired,
+ type: PropTypes.oneOf(['primary', 'secondary', 'cancel']).isRequired,
+ })
+ ).isRequired,
+ onSubmit: PropTypes.func.isRequired,
};
export default Notification;
diff --git a/platform/ui/src/components/Notification/Notification.mdx b/platform/ui/src/components/Notification/Notification.mdx
index 4b379d03e..926eb66e0 100644
--- a/platform/ui/src/components/Notification/Notification.mdx
+++ b/platform/ui/src/components/Notification/Notification.mdx
@@ -5,7 +5,7 @@ route: components/notification
---
import { Playground, Props } from 'docz';
-import { Notification, Button } from '@ohif/ui';
+import { Notification } from '@ohif/ui';
# Notification
@@ -22,23 +22,31 @@ import { Notification } from '@ohif/ui';
{() => {
- const actionButtons = () => {
- return (
-
-
-
-
-
- );
- };
return (
{
+ window.alert(value);
+ }}
/>
);
diff --git a/platform/ui/src/components/Viewport/Viewport.jsx b/platform/ui/src/components/Viewport/Viewport.jsx
index ccf8a067d..404e85790 100644
--- a/platform/ui/src/components/Viewport/Viewport.jsx
+++ b/platform/ui/src/components/Viewport/Viewport.jsx
@@ -4,7 +4,7 @@ import { ViewportActionBar, Notification, Button } from '@ohif/ui';
const Viewport = ({ viewportIndex, onSeriesChange, studyData, children }) => {
return (
-
+
{
{/* TODO: NOTIFICATION API DEFINITION - OHIF-112 */}
-
-
-
-
- }
+ actions={[
+ {
+ type: 'cancel',
+ text: 'No',
+ value: 0,
+ },
+ {
+ type: 'secondary',
+ text: 'No, do not ask again',
+ value: -1,
+ },
+ {
+ type: 'primary',
+ text: 'Yes',
+ value: 1,
+ },
+ ]}
+ onSubmit={value => {
+ window.alert(value);
+ }}
/>
diff --git a/platform/ui/src/contextProviders/ViewportDialogProvider.jsx b/platform/ui/src/contextProviders/ViewportDialogProvider.jsx
index e412188ee..325ed3ea3 100644
--- a/platform/ui/src/contextProviders/ViewportDialogProvider.jsx
+++ b/platform/ui/src/contextProviders/ViewportDialogProvider.jsx
@@ -7,10 +7,20 @@ import React, {
} from 'react';
import PropTypes from 'prop-types';
-const DEFAULT_OPTIONS = {
- content: null,
- contentProps: null,
- customClassName: null,
+const DEFAULT_STATE = {
+ viewportIndex: null,
+ message: undefined,
+ type: 'info', // "error" | "warning" | "info" | "success"
+ actions: undefined, // array of { type, text, value }
+ // dismissable?
+ // blockInteraction (single viewport? allViewports? everything?)
+ // TODO: Buttons --> type/color? text? value?
+ onSubmit: () => {
+ console.log('btn value?');
+ },
+ onDismiss: () => {
+ console.log('dismiss? -1');
+ },
};
const ViewportDialogContext = createContext(null);
@@ -18,42 +28,26 @@ const { Provider } = ViewportDialogContext;
export const useViewportDialog = () => useContext(ViewportDialogContext);
-const ViewportDialogProvider = ({
- children,
- dialog: Dialog,
- service,
- viewportIndex,
-}) => {
- const [options, setOptions] = useState(DEFAULT_OPTIONS);
-
- const show = useCallback((props) => setOptions({ ...options, ...props }), [
- options,
- ]);
-
- const hide = useCallback(() => setOptions(DEFAULT_OPTIONS), []);
+const ViewportDialogProvider = ({ children, service }) => {
+ const [viewportDialogState, setViewportDialogState] = useState(DEFAULT_STATE);
+ const show = useCallback(
+ params => setViewportDialogState({ ...viewportDialogState, ...params }),
+ [viewportDialogState]
+ );
+ const hide = useCallback(() => setViewportDialogState(DEFAULT_STATE), []);
useEffect(() => {
if (service) {
- service.setServiceImplementation({ hide, show, viewportIndex });
+ service.setServiceImplementation({
+ hide,
+ show,
+ });
}
- }, [hide, service, show, viewportIndex]);
-
- const {
- content: ViewportDialogContent,
- contentProps,
- customClassName,
- } = options;
+ }, [hide, service, show]);
return (
-
-
- {ViewportDialogContent && (
-
- )}
- {children}
-
+
+ {children}
);
};
@@ -64,16 +58,9 @@ ViewportDialogProvider.propTypes = {
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]).isRequired,
- /** dialog component */
- dialog: PropTypes.oneOfType([
- PropTypes.arrayOf(PropTypes.node),
- PropTypes.node,
- PropTypes.func,
- ]).isRequired,
service: PropTypes.shape({
setServiceImplementation: PropTypes.func,
}),
- viewportIndex: PropTypes.number,
};
export default ViewportDialogProvider;
diff --git a/platform/ui/src/contextProviders/ViewportDialogProvider.mdx b/platform/ui/src/contextProviders/ViewportDialogProvider.mdx
index ffc0ee55e..34e66c1b8 100644
--- a/platform/ui/src/contextProviders/ViewportDialogProvider.mdx
+++ b/platform/ui/src/contextProviders/ViewportDialogProvider.mdx
@@ -28,19 +28,26 @@ component across all application.
const ViewportNotification = ({ hide }) => {
return (
-
-
-
-
- }
+ actions={[
+ {
+ type: 'cancel',
+ text: 'No',
+ value: 0,
+ },
+ {
+ type: 'secondary',
+ text: 'No, do not ask again',
+ value: -1,
+ },
+ {
+ type: 'primary',
+ text: 'Yes',
+ value: 1,
+ },
+ ]}
+ onSubmit={value => { window.alert(value); }}
/>
);
};
@@ -88,17 +95,26 @@ component across all application.
const ViewportNotification = ({ hide }) => {
return (
-
-
-
-
- }
+ actions={[
+ {
+ type: 'cancel',
+ text: 'No',
+ value: 0,
+ },
+ {
+ type: 'secondary',
+ text: 'No, do not ask again',
+ value: -1,
+ },
+ {
+ type: 'primary',
+ text: 'Yes',
+ value: 1,
+ },
+ ]}
+ onSubmit={value => { window.alert(value); }}
/>
);
};
diff --git a/platform/viewer/src/App.jsx b/platform/viewer/src/App.jsx
index 22e3c23c0..4b7d4bd60 100644
--- a/platform/viewer/src/App.jsx
+++ b/platform/viewer/src/App.jsx
@@ -6,8 +6,10 @@ import {
DialogProvider,
Modal,
ModalProvider,
+ Notification,
SnackbarProvider,
ThemeWrapper,
+ ViewportDialogProvider,
ViewportGridProvider,
} from '@ohif/ui';
// Viewer Project
@@ -48,7 +50,12 @@ function App({ config, defaultExtensions }) {
extensionManager,
servicesManager
);
- const { UIDialogService, UIModalService, UINotificationService } = servicesManager.services;
+ const {
+ UIDialogService,
+ UIModalService,
+ UINotificationService,
+ UIViewportDialogService,
+ } = servicesManager.services;
// A UI Service may need to use the ViewportGrid context
const viewportGridReducer = (state, action) => {
@@ -82,13 +89,15 @@ function App({ config, defaultExtensions }) {
}}
reducer={viewportGridReducer}
>
-
-
-
- {appRoutes}
-
-
-
+
+
+
+
+ {appRoutes}
+
+
+
+
diff --git a/platform/viewer/src/appInit.js b/platform/viewer/src/appInit.js
index 4d236ee73..8ddb6b02e 100644
--- a/platform/viewer/src/appInit.js
+++ b/platform/viewer/src/appInit.js
@@ -6,6 +6,7 @@ import {
UINotificationService,
UIModalService,
UIDialogService,
+ UIViewportDialogService,
MeasurementService,
DisplaySetService,
ToolBarSerivce,
@@ -44,6 +45,7 @@ function appInit(appConfigOrFunc, defaultExtensions) {
UINotificationService,
UIModalService,
UIDialogService,
+ UIViewportDialogService,
MeasurementService,
DisplaySetService,
ToolBarSerivce,