get ViewportDialog to display as a part of machine transition
This commit is contained in:
parent
a4fc5e21b2
commit
00f8d56a4e
@ -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 (
|
||||||
|
<TrackedMeasurementsContext.Provider
|
||||||
|
value={[trackedMeasurements, sendTrackedMeasurementsEvent]}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</TrackedMeasurementsContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackedMeasurementsContextProvider.propTypes = {
|
||||||
|
children: PropTypes.oneOf([PropTypes.func, PropTypes.node]),
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
TrackedMeasurementsContext,
|
||||||
|
TrackedMeasurementsContextProvider,
|
||||||
|
useTrackedMeasurements,
|
||||||
|
};
|
||||||
@ -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 (
|
|
||||||
<TrackedMeasurementsContext.Provider
|
|
||||||
value={[trackedMeasurements, sendTrackedMeasurementsEvent]}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</TrackedMeasurementsContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TrackedMeasurementsContextProvider.propTypes = {
|
|
||||||
children: PropTypes.oneOf([PropTypes.func, PropTypes.node]),
|
|
||||||
};
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
TrackedMeasurementsContext,
|
TrackedMeasurementsContext,
|
||||||
TrackedMeasurementsContextProvider,
|
TrackedMeasurementsContextProvider,
|
||||||
useTrackedMeasurements,
|
useTrackedMeasurements,
|
||||||
};
|
} from './TrackedMeasurementsContext.jsx';
|
||||||
|
|||||||
@ -27,7 +27,7 @@ const machineConfiguration = {
|
|||||||
prompt: {
|
prompt: {
|
||||||
invoke: {
|
invoke: {
|
||||||
id: 'shouldTrackPrompt',
|
id: 'shouldTrackPrompt',
|
||||||
src: () => confirmDialog('Should we start tracking?'),
|
src: 'shouldTrackPrompt',
|
||||||
onDone: {
|
onDone: {
|
||||||
target: 'validateResponse',
|
target: 'validateResponse',
|
||||||
actions: assign({ promptResponse: (ctx, evt) => evt.data }),
|
actions: assign({ promptResponse: (ctx, evt) => evt.data }),
|
||||||
@ -135,6 +135,11 @@ function confirmDialog(msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
|
services: {
|
||||||
|
shouldTrackPrompt: () => {
|
||||||
|
return confirmDialog('Should we start tracking?');
|
||||||
|
},
|
||||||
|
},
|
||||||
actions: {
|
actions: {
|
||||||
clearContext: assign({
|
clearContext: assign({
|
||||||
prevTrackedStudy: '',
|
prevTrackedStudy: '',
|
||||||
@ -186,11 +191,13 @@ const defaultOptions = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const measurementTrackingMachine = Machine(machineConfiguration, defaultOptions);
|
// const measurementTrackingMachine = Machine(
|
||||||
|
// machineConfiguration,
|
||||||
|
// defaultOptions
|
||||||
|
// );
|
||||||
// .transition(state, eventArgument).value
|
// .transition(state, eventArgument).value
|
||||||
// const service = interpret(measurementTrackingMachine).start();
|
// const service = interpret(measurementTrackingMachine).start();
|
||||||
// .send(event): nextState
|
// .send(event): nextState
|
||||||
// .state (getter)
|
// .state (getter)
|
||||||
// .onTransition(state => { state.vale })
|
// .onTransition(state => { state.vale })
|
||||||
export { defaultOptions, machineConfiguration, measurementTrackingMachine };
|
export { defaultOptions, machineConfiguration };
|
||||||
export default measurementTrackingMachine;
|
|
||||||
|
|||||||
@ -4,12 +4,18 @@ import {
|
|||||||
useTrackedMeasurements,
|
useTrackedMeasurements,
|
||||||
} from './contexts';
|
} from './contexts';
|
||||||
|
|
||||||
function getContextModule() {
|
function getContextModule({ servicesManager }) {
|
||||||
|
const { UIViewportDialogService } = servicesManager.services;
|
||||||
|
const BoundTrackedMeasurementsContextProvider = TrackedMeasurementsContextProvider.bind(
|
||||||
|
null,
|
||||||
|
UIViewportDialogService
|
||||||
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'TrackedMeasurementsContext',
|
name: 'TrackedMeasurementsContext',
|
||||||
context: TrackedMeasurementsContext,
|
context: TrackedMeasurementsContext,
|
||||||
provider: TrackedMeasurementsContextProvider,
|
provider: BoundTrackedMeasurementsContextProvider,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const Component = React.lazy(() => {
|
const Component = React.lazy(() => {
|
||||||
return import('./viewports/OHIFCornerstoneViewport');
|
return import('./viewports/TrackedCornerstoneViewport');
|
||||||
});
|
});
|
||||||
|
|
||||||
const OHIFCornerstoneViewport = props => {
|
const OHIFCornerstoneViewport = props => {
|
||||||
|
|||||||
@ -3,12 +3,16 @@ import PropTypes from 'prop-types';
|
|||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
import CornerstoneViewport from 'react-cornerstone-viewport';
|
import CornerstoneViewport from 'react-cornerstone-viewport';
|
||||||
import OHIF, { DicomMetadataStore } from '@ohif/core';
|
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 debounce from 'lodash.debounce';
|
||||||
import throttle from 'lodash.throttle';
|
import throttle from 'lodash.throttle';
|
||||||
import { useTrackedMeasurements } from './../getContextModule';
|
import { useTrackedMeasurements } from './../getContextModule';
|
||||||
|
|
||||||
|
|
||||||
// const cine = viewportSpecificData.cine;
|
// const cine = viewportSpecificData.cine;
|
||||||
|
|
||||||
// isPlaying = cine.isPlaying === true;
|
// isPlaying = cine.isPlaying === true;
|
||||||
@ -16,7 +20,7 @@ import { useTrackedMeasurements } from './../getContextModule';
|
|||||||
|
|
||||||
const { StackManager } = OHIF.utils;
|
const { StackManager } = OHIF.utils;
|
||||||
|
|
||||||
function OHIFCornerstoneViewport({
|
function TrackedCornerstoneViewport({
|
||||||
children,
|
children,
|
||||||
dataSource,
|
dataSource,
|
||||||
displaySet,
|
displaySet,
|
||||||
@ -24,6 +28,10 @@ function OHIFCornerstoneViewport({
|
|||||||
}) {
|
}) {
|
||||||
const [trackedMeasurements] = useTrackedMeasurements();
|
const [trackedMeasurements] = useTrackedMeasurements();
|
||||||
const [{ viewports }, dispatchViewportGrid] = useViewportGrid();
|
const [{ viewports }, dispatchViewportGrid] = useViewportGrid();
|
||||||
|
// viewportIndex, onSubmit
|
||||||
|
const [viewportDialogState, viewportDialogApi] = useViewportDialog();
|
||||||
|
console.warn('VIEWPORT DIALOG', viewportDialogState, viewportDialogApi);
|
||||||
|
|
||||||
const [viewportData, setViewportData] = useState(null);
|
const [viewportData, setViewportData] = useState(null);
|
||||||
// TODO: Still needed? Better way than import `OHIF` and destructure?
|
// TODO: Still needed? Better way than import `OHIF` and destructure?
|
||||||
// Why is this managed by `core`?
|
// Why is this managed by `core`?
|
||||||
@ -98,7 +106,7 @@ function OHIFCornerstoneViewport({
|
|||||||
// );
|
// );
|
||||||
// TODO: This display contains the meta for all instances.
|
// TODO: This display contains the meta for all instances.
|
||||||
// That can't be right...
|
// That can't be right...
|
||||||
console.log('DISPLAYSET', displaySet)
|
console.log('DISPLAYSET', displaySet);
|
||||||
// const seriesMeta = DicomMetadataStore.getSeries(this.props.displaySet.StudyInstanceUID, '');
|
// const seriesMeta = DicomMetadataStore.getSeries(this.props.displaySet.StudyInstanceUID, '');
|
||||||
// console.log(seriesMeta);
|
// console.log(seriesMeta);
|
||||||
|
|
||||||
@ -145,22 +153,35 @@ function OHIFCornerstoneViewport({
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<CornerstoneViewport
|
{/* TODO: Viewport interface to accept stack or layers of content like this? */}
|
||||||
viewportIndex={viewportIndex}
|
<div className="relative flex flex-row w-full h-full">
|
||||||
imageIds={imageIds}
|
<CornerstoneViewport
|
||||||
imageIdIndex={currentImageIdIndex}
|
viewportIndex={viewportIndex}
|
||||||
// TODO: ViewportGrid Context?
|
imageIds={imageIds}
|
||||||
isActive={true} // todo
|
imageIdIndex={currentImageIdIndex}
|
||||||
isStackPrefetchEnabled={true} // todo
|
// TODO: ViewportGrid Context?
|
||||||
isPlaying={false}
|
isActive={true} // todo
|
||||||
frameRate={24}
|
isStackPrefetchEnabled={true} // todo
|
||||||
/>
|
isPlaying={false}
|
||||||
{childrenWithProps}
|
frameRate={24}
|
||||||
|
/>
|
||||||
|
<div style={{ position: 'absolute' }}>
|
||||||
|
{viewportDialogState.viewportIndex === viewportIndex && (
|
||||||
|
<Notification
|
||||||
|
message={viewportDialogState.message}
|
||||||
|
type={viewportDialogState.type}
|
||||||
|
actions={viewportDialogState.actions}
|
||||||
|
onSubmit={viewportDialogState.onSubmit}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{childrenWithProps}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OHIFCornerstoneViewport.propTypes = {
|
TrackedCornerstoneViewport.propTypes = {
|
||||||
displaySet: PropTypes.object.isRequired,
|
displaySet: PropTypes.object.isRequired,
|
||||||
viewportIndex: PropTypes.number.isRequired,
|
viewportIndex: PropTypes.number.isRequired,
|
||||||
dataSource: PropTypes.object,
|
dataSource: PropTypes.object,
|
||||||
@ -168,7 +189,7 @@ OHIFCornerstoneViewport.propTypes = {
|
|||||||
customProps: PropTypes.object,
|
customProps: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
OHIFCornerstoneViewport.defaultProps = {
|
TrackedCornerstoneViewport.defaultProps = {
|
||||||
customProps: {},
|
customProps: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -207,6 +228,6 @@ async function _getViewportData(dataSource, displaySet) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return viewportData;
|
return viewportData;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default OHIFCornerstoneViewport;
|
export default TrackedCornerstoneViewport;
|
||||||
@ -17,7 +17,8 @@ const publicAPI = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const serviceImplementation = {
|
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 }
|
* @param {ViewportDialogProps} props { content, contentProps, viewportIndex }
|
||||||
*/
|
*/
|
||||||
function _show({ content = null, contentProps = null, viewportIndex }) {
|
function _show({ viewportIndex, type, message, actions, onSubmit }) {
|
||||||
const viewportIndexImplementation =
|
return serviceImplementation._show({
|
||||||
(viewportIndex !== undefined &&
|
|
||||||
serviceImplementation._viewports[viewportIndex]) ||
|
|
||||||
{};
|
|
||||||
|
|
||||||
if (!viewportIndexImplementation._show) {
|
|
||||||
console.warn('show() NOT IMPLEMENTED');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return viewportIndexImplementation._show({
|
|
||||||
content,
|
|
||||||
contentProps,
|
|
||||||
viewportIndex,
|
viewportIndex,
|
||||||
|
type,
|
||||||
|
message,
|
||||||
|
actions,
|
||||||
|
onSubmit,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides/dismisses the viewport dialog, if currently shown
|
* Hides/dismisses the viewport dialog, if currently shown
|
||||||
*
|
|
||||||
* @param {*} { viewportIndex }
|
|
||||||
*/
|
*/
|
||||||
function _hide({ viewportIndex }) {
|
function _hide() {
|
||||||
const viewportIndexImplementation =
|
return serviceImplementation._hide();
|
||||||
(viewportIndex && serviceImplementation._viewports[viewportIndex]) || {};
|
|
||||||
|
|
||||||
if (!viewportIndexImplementation._hide) {
|
|
||||||
console.warn('hide() NOT IMPLEMENTED');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return viewportIndexImplementation._hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,22 +55,12 @@ function _hide({ viewportIndex }) {
|
|||||||
function setServiceImplementation({
|
function setServiceImplementation({
|
||||||
hide: hideImplementation,
|
hide: hideImplementation,
|
||||||
show: showImplementation,
|
show: showImplementation,
|
||||||
viewportIndex,
|
|
||||||
}) {
|
}) {
|
||||||
if (viewportIndex !== undefined) {
|
if (hideImplementation) {
|
||||||
const newImplementations = {};
|
serviceImplementation._hide = hideImplementation;
|
||||||
if (hideImplementation) {
|
}
|
||||||
newImplementations._hide = hideImplementation;
|
if (showImplementation) {
|
||||||
}
|
serviceImplementation._show = showImplementation;
|
||||||
if (showImplementation) {
|
|
||||||
newImplementations._show = showImplementation;
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceImplementation._viewports[viewportIndex] = Object.assign(
|
|
||||||
{},
|
|
||||||
serviceImplementation._viewports[viewportIndex],
|
|
||||||
newImplementations
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
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 = {
|
const iconsByType = {
|
||||||
error: {
|
error: {
|
||||||
icon: 'info',
|
icon: 'info',
|
||||||
@ -35,12 +35,30 @@ const Notification = ({ type, text, actionButtons }) => {
|
|||||||
const { icon, color } = getIconData();
|
const { icon, color } = getIconData();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-2 mt-2 p-2 flex flex-col bg-common-bright rounded">
|
<div className="flex flex-col p-2 mx-2 mt-2 rounded bg-common-bright">
|
||||||
<div className="flex flex-grow">
|
<div className="flex flex-grow">
|
||||||
<Icon name={icon} className={classnames('w-5', color)} />
|
<Icon name={icon} className={classnames('w-5', color)} />
|
||||||
<span className="text-base text-black ml-2">{text}</span>
|
<span className="ml-2 text-base text-black">{message}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end mt-2">
|
||||||
|
{actions.map((action, index) => {
|
||||||
|
const isFirst = index === 0;
|
||||||
|
const isPrimary = action.type === 'primary';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={index}
|
||||||
|
className={classnames({ 'ml-2': !isFirst })}
|
||||||
|
color={isPrimary ? 'primary' : undefined}
|
||||||
|
onClick={() => {
|
||||||
|
onSubmit(action.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{action.text}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end mt-2">{actionButtons}</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -51,8 +69,15 @@ Notification.defaultProps = {
|
|||||||
|
|
||||||
Notification.propTypes = {
|
Notification.propTypes = {
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
text: PropTypes.string.isRequired,
|
message: PropTypes.string.isRequired,
|
||||||
actionButtons: PropTypes.node.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;
|
export default Notification;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ route: components/notification
|
|||||||
---
|
---
|
||||||
|
|
||||||
import { Playground, Props } from 'docz';
|
import { Playground, Props } from 'docz';
|
||||||
import { Notification, Button } from '@ohif/ui';
|
import { Notification } from '@ohif/ui';
|
||||||
|
|
||||||
# Notification
|
# Notification
|
||||||
|
|
||||||
@ -22,23 +22,31 @@ import { Notification } from '@ohif/ui';
|
|||||||
|
|
||||||
<Playground>
|
<Playground>
|
||||||
{() => {
|
{() => {
|
||||||
const actionButtons = () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Button>No</Button>
|
|
||||||
<Button className="ml-2">No, do not ask again</Button>
|
|
||||||
<Button className="ml-2" color="primary">
|
|
||||||
Yes
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 w-full lg:w-2/3">
|
<div className="p-4 w-full lg:w-2/3">
|
||||||
<Notification
|
<Notification
|
||||||
text="Track all measurement for this series?"
|
message="Track all measurement for this series?"
|
||||||
type="info"
|
type="info"
|
||||||
actionButtons={actionButtons()}
|
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);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { ViewportActionBar, Notification, Button } from '@ohif/ui';
|
|||||||
|
|
||||||
const Viewport = ({ viewportIndex, onSeriesChange, studyData, children }) => {
|
const Viewport = ({ viewportIndex, onSeriesChange, studyData, children }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full relative">
|
<div className="relative flex flex-col h-full">
|
||||||
<div className="absolute top-0 left-0 w-full">
|
<div className="absolute top-0 left-0 w-full">
|
||||||
<ViewportActionBar
|
<ViewportActionBar
|
||||||
onSeriesChange={onSeriesChange}
|
onSeriesChange={onSeriesChange}
|
||||||
@ -13,17 +13,28 @@ const Viewport = ({ viewportIndex, onSeriesChange, studyData, children }) => {
|
|||||||
|
|
||||||
{/* TODO: NOTIFICATION API DEFINITION - OHIF-112 */}
|
{/* TODO: NOTIFICATION API DEFINITION - OHIF-112 */}
|
||||||
<Notification
|
<Notification
|
||||||
text="Track all measurement for this series?"
|
message="Track all measurement for this series?"
|
||||||
type="info"
|
type="info"
|
||||||
actionButtons={
|
actions={[
|
||||||
<div>
|
{
|
||||||
<Button>No</Button>
|
type: 'cancel',
|
||||||
<Button className="ml-2">No, do not ask again</Button>
|
text: 'No',
|
||||||
<Button className="ml-2" color="primary">
|
value: 0,
|
||||||
Yes
|
},
|
||||||
</Button>
|
{
|
||||||
</div>
|
type: 'secondary',
|
||||||
}
|
text: 'No, do not ask again',
|
||||||
|
value: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'primary',
|
||||||
|
text: 'Yes',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onSubmit={value => {
|
||||||
|
window.alert(value);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -7,10 +7,20 @@ import React, {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const DEFAULT_OPTIONS = {
|
const DEFAULT_STATE = {
|
||||||
content: null,
|
viewportIndex: null,
|
||||||
contentProps: null,
|
message: undefined,
|
||||||
customClassName: null,
|
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);
|
const ViewportDialogContext = createContext(null);
|
||||||
@ -18,42 +28,26 @@ const { Provider } = ViewportDialogContext;
|
|||||||
|
|
||||||
export const useViewportDialog = () => useContext(ViewportDialogContext);
|
export const useViewportDialog = () => useContext(ViewportDialogContext);
|
||||||
|
|
||||||
const ViewportDialogProvider = ({
|
const ViewportDialogProvider = ({ children, service }) => {
|
||||||
children,
|
const [viewportDialogState, setViewportDialogState] = useState(DEFAULT_STATE);
|
||||||
dialog: Dialog,
|
const show = useCallback(
|
||||||
service,
|
params => setViewportDialogState({ ...viewportDialogState, ...params }),
|
||||||
viewportIndex,
|
[viewportDialogState]
|
||||||
}) => {
|
);
|
||||||
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
const hide = useCallback(() => setViewportDialogState(DEFAULT_STATE), []);
|
||||||
|
|
||||||
const show = useCallback((props) => setOptions({ ...options, ...props }), [
|
|
||||||
options,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const hide = useCallback(() => setOptions(DEFAULT_OPTIONS), []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (service) {
|
if (service) {
|
||||||
service.setServiceImplementation({ hide, show, viewportIndex });
|
service.setServiceImplementation({
|
||||||
|
hide,
|
||||||
|
show,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [hide, service, show, viewportIndex]);
|
}, [hide, service, show]);
|
||||||
|
|
||||||
const {
|
|
||||||
content: ViewportDialogContent,
|
|
||||||
contentProps,
|
|
||||||
customClassName,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider value={{ show, hide }}>
|
<Provider value={[viewportDialogState, { show, hide }]}>
|
||||||
<div className="relative w-full h-full">
|
{children}
|
||||||
{ViewportDialogContent && (
|
|
||||||
<Dialog className={customClassName}>
|
|
||||||
<ViewportDialogContent {...contentProps} show={show} hide={hide} />
|
|
||||||
</Dialog>
|
|
||||||
)}
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</Provider>
|
</Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -64,16 +58,9 @@ ViewportDialogProvider.propTypes = {
|
|||||||
PropTypes.arrayOf(PropTypes.node),
|
PropTypes.arrayOf(PropTypes.node),
|
||||||
PropTypes.node,
|
PropTypes.node,
|
||||||
]).isRequired,
|
]).isRequired,
|
||||||
/** dialog component */
|
|
||||||
dialog: PropTypes.oneOfType([
|
|
||||||
PropTypes.arrayOf(PropTypes.node),
|
|
||||||
PropTypes.node,
|
|
||||||
PropTypes.func,
|
|
||||||
]).isRequired,
|
|
||||||
service: PropTypes.shape({
|
service: PropTypes.shape({
|
||||||
setServiceImplementation: PropTypes.func,
|
setServiceImplementation: PropTypes.func,
|
||||||
}),
|
}),
|
||||||
viewportIndex: PropTypes.number,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ViewportDialogProvider;
|
export default ViewportDialogProvider;
|
||||||
|
|||||||
@ -28,19 +28,26 @@ component across all application.
|
|||||||
const ViewportNotification = ({ hide }) => {
|
const ViewportNotification = ({ hide }) => {
|
||||||
return (
|
return (
|
||||||
<Notification
|
<Notification
|
||||||
text="Track all measurement for this series?"
|
message="Track all measurement for this series?"
|
||||||
type="info"
|
type="info"
|
||||||
actionButtons={
|
actions={[
|
||||||
<div>
|
{
|
||||||
<Button onClick={hide}>No</Button>
|
type: 'cancel',
|
||||||
<Button onClick={hide} className="ml-2">
|
text: 'No',
|
||||||
No, do not ask again
|
value: 0,
|
||||||
</Button>
|
},
|
||||||
<Button onClick={hide} className="ml-2" color="primary">
|
{
|
||||||
Yes
|
type: 'secondary',
|
||||||
</Button>
|
text: 'No, do not ask again',
|
||||||
</div>
|
value: -1,
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
type: 'primary',
|
||||||
|
text: 'Yes',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onSubmit={value => { window.alert(value); }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -88,17 +95,26 @@ component across all application.
|
|||||||
const ViewportNotification = ({ hide }) => {
|
const ViewportNotification = ({ hide }) => {
|
||||||
return (
|
return (
|
||||||
<Notification
|
<Notification
|
||||||
text="Track all measurement for this series?"
|
message="Track all measurement for this series?"
|
||||||
type="info"
|
type="info"
|
||||||
actionButtons={
|
actions={[
|
||||||
<div>
|
{
|
||||||
<Button onClick={hide}>No</Button>
|
type: 'cancel',
|
||||||
<Button className="ml-2">No, do not ask again</Button>
|
text: 'No',
|
||||||
<Button className="ml-2" color="primary">
|
value: 0,
|
||||||
Yes
|
},
|
||||||
</Button>
|
{
|
||||||
</div>
|
type: 'secondary',
|
||||||
}
|
text: 'No, do not ask again',
|
||||||
|
value: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'primary',
|
||||||
|
text: 'Yes',
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onSubmit={value => { window.alert(value); }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,8 +6,10 @@ import {
|
|||||||
DialogProvider,
|
DialogProvider,
|
||||||
Modal,
|
Modal,
|
||||||
ModalProvider,
|
ModalProvider,
|
||||||
|
Notification,
|
||||||
SnackbarProvider,
|
SnackbarProvider,
|
||||||
ThemeWrapper,
|
ThemeWrapper,
|
||||||
|
ViewportDialogProvider,
|
||||||
ViewportGridProvider,
|
ViewportGridProvider,
|
||||||
} from '@ohif/ui';
|
} from '@ohif/ui';
|
||||||
// Viewer Project
|
// Viewer Project
|
||||||
@ -48,7 +50,12 @@ function App({ config, defaultExtensions }) {
|
|||||||
extensionManager,
|
extensionManager,
|
||||||
servicesManager
|
servicesManager
|
||||||
);
|
);
|
||||||
const { UIDialogService, UIModalService, UINotificationService } = servicesManager.services;
|
const {
|
||||||
|
UIDialogService,
|
||||||
|
UIModalService,
|
||||||
|
UINotificationService,
|
||||||
|
UIViewportDialogService,
|
||||||
|
} = servicesManager.services;
|
||||||
|
|
||||||
// A UI Service may need to use the ViewportGrid context
|
// A UI Service may need to use the ViewportGrid context
|
||||||
const viewportGridReducer = (state, action) => {
|
const viewportGridReducer = (state, action) => {
|
||||||
@ -82,13 +89,15 @@ function App({ config, defaultExtensions }) {
|
|||||||
}}
|
}}
|
||||||
reducer={viewportGridReducer}
|
reducer={viewportGridReducer}
|
||||||
>
|
>
|
||||||
<SnackbarProvider service={UINotificationService}>
|
<ViewportDialogProvider service={UIViewportDialogService}>
|
||||||
<DialogProvider service={UIDialogService}>
|
<SnackbarProvider service={UINotificationService}>
|
||||||
<ModalProvider modal={Modal} service={UIModalService}>
|
<DialogProvider service={UIDialogService}>
|
||||||
{appRoutes}
|
<ModalProvider modal={Modal} service={UIModalService}>
|
||||||
</ModalProvider>
|
{appRoutes}
|
||||||
</DialogProvider>
|
</ModalProvider>
|
||||||
</SnackbarProvider>
|
</DialogProvider>
|
||||||
|
</SnackbarProvider>
|
||||||
|
</ViewportDialogProvider>
|
||||||
</ViewportGridProvider>
|
</ViewportGridProvider>
|
||||||
</ThemeWrapper>
|
</ThemeWrapper>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {
|
|||||||
UINotificationService,
|
UINotificationService,
|
||||||
UIModalService,
|
UIModalService,
|
||||||
UIDialogService,
|
UIDialogService,
|
||||||
|
UIViewportDialogService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
ToolBarSerivce,
|
ToolBarSerivce,
|
||||||
@ -44,6 +45,7 @@ function appInit(appConfigOrFunc, defaultExtensions) {
|
|||||||
UINotificationService,
|
UINotificationService,
|
||||||
UIModalService,
|
UIModalService,
|
||||||
UIDialogService,
|
UIDialogService,
|
||||||
|
UIViewportDialogService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
DisplaySetService,
|
DisplaySetService,
|
||||||
ToolBarSerivce,
|
ToolBarSerivce,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user