feat: Notification provider and service (#1703)
* Create basic structure for Viewport dialog provier and Dialog component * Implementation of a UIViewportDialogService * Update viewportDialogProvider docz * Create example of use of UIViewportDialogService
This commit is contained in:
parent
cee09fcf8d
commit
e633eb80eb
@ -22,10 +22,11 @@ import errorHandler from './errorHandler.js';
|
||||
import utils, { hotkeys } from './utils/';
|
||||
|
||||
import {
|
||||
UINotificationService,
|
||||
UIModalService,
|
||||
UIDialogService,
|
||||
MeasurementService,
|
||||
UIDialogService,
|
||||
UIModalService,
|
||||
UINotificationService,
|
||||
UIViewportDialogService,
|
||||
} from './services';
|
||||
|
||||
const OHIF = {
|
||||
@ -56,9 +57,10 @@ const OHIF = {
|
||||
measurements,
|
||||
hangingProtocols,
|
||||
//
|
||||
UINotificationService,
|
||||
UIModalService,
|
||||
UIDialogService,
|
||||
UIModalService,
|
||||
UINotificationService,
|
||||
UIViewportDialogService,
|
||||
MeasurementService,
|
||||
};
|
||||
|
||||
@ -89,9 +91,10 @@ export {
|
||||
measurements,
|
||||
hangingProtocols,
|
||||
//
|
||||
UINotificationService,
|
||||
UIModalService,
|
||||
UIDialogService,
|
||||
UIModalService,
|
||||
UINotificationService,
|
||||
UIViewportDialogService,
|
||||
MeasurementService,
|
||||
};
|
||||
|
||||
|
||||
99
platform/core/src/services/UIViewportDialogService/index.js
Normal file
99
platform/core/src/services/UIViewportDialogService/index.js
Normal file
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Viewport Dialog
|
||||
*
|
||||
* @typedef {Object} ViewportDialogProps
|
||||
* @property {ReactElement|HTMLElement} [content=null] Modal content.
|
||||
* @property {Object} [contentProps=null] Modal content props.
|
||||
* @property {boolean} [viewportIndex=false] Modal is dismissible via the esc key.
|
||||
*/
|
||||
|
||||
const name = 'UIViewportDialogService';
|
||||
|
||||
const publicAPI = {
|
||||
name,
|
||||
hide: _hide,
|
||||
show: _show,
|
||||
setServiceImplementation,
|
||||
};
|
||||
|
||||
const serviceImplementation = {
|
||||
_viewports: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a new UI viewport dialog on the specified viewportIndex;
|
||||
*
|
||||
* @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,
|
||||
viewportIndex,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {*} {
|
||||
* hide: hideImplementation,
|
||||
* show: showImplementation,
|
||||
* 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name,
|
||||
create: ({ configuration = {} }) => {
|
||||
return publicAPI;
|
||||
},
|
||||
};
|
||||
@ -1,13 +1,15 @@
|
||||
import ServicesManager from './ServicesManager.js';
|
||||
import UINotificationService from './UINotificationService';
|
||||
import UIModalService from './UIModalService';
|
||||
import UIDialogService from './UIDialogService';
|
||||
import MeasurementService from './MeasurementService';
|
||||
import ServicesManager from './ServicesManager.js';
|
||||
import UIDialogService from './UIDialogService';
|
||||
import UIModalService from './UIModalService';
|
||||
import UINotificationService from './UINotificationService';
|
||||
import UIViewportDialogService from './UIViewportDialogService';
|
||||
|
||||
export {
|
||||
UINotificationService,
|
||||
UIModalService,
|
||||
UIDialogService,
|
||||
ServicesManager,
|
||||
MeasurementService,
|
||||
ServicesManager,
|
||||
UIDialogService,
|
||||
UIModalService,
|
||||
UINotificationService,
|
||||
UIViewportDialogService,
|
||||
};
|
||||
|
||||
@ -8,6 +8,8 @@ export {
|
||||
ModalConsumer,
|
||||
useModal,
|
||||
withModal,
|
||||
ViewportDialogProvider,
|
||||
useViewportDialog,
|
||||
} from './src/contextProviders';
|
||||
|
||||
/** COMPONENTS */
|
||||
@ -15,6 +17,7 @@ export {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
DateRange,
|
||||
Dialog,
|
||||
EmptyStudies,
|
||||
Icon,
|
||||
IconButton,
|
||||
|
||||
12
platform/ui/src/components/Dialog/Dialog.jsx
Normal file
12
platform/ui/src/components/Dialog/Dialog.jsx
Normal file
@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const Dialog = ({ children }) => {
|
||||
return <div className="absolute top-0 left-0">{children}</div>;
|
||||
};
|
||||
|
||||
Dialog.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default Dialog;
|
||||
2
platform/ui/src/components/Dialog/index.js
Normal file
2
platform/ui/src/components/Dialog/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
import Dialog from './Dialog';
|
||||
export default Dialog;
|
||||
@ -1,6 +1,7 @@
|
||||
import Button from './Button';
|
||||
import ButtonGroup from './ButtonGroup';
|
||||
import DateRange from './DateRange';
|
||||
import Dialog from './Dialog';
|
||||
import EmptyStudies from './EmptyStudies';
|
||||
import Icon from './Icon';
|
||||
import IconButton from './IconButton';
|
||||
@ -47,6 +48,7 @@ export {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
DateRange,
|
||||
Dialog,
|
||||
EmptyStudies,
|
||||
Icon,
|
||||
IconButton,
|
||||
|
||||
79
platform/ui/src/contextProviders/ViewportDialogProvider.jsx
Normal file
79
platform/ui/src/contextProviders/ViewportDialogProvider.jsx
Normal file
@ -0,0 +1,79 @@
|
||||
import React, {
|
||||
useState,
|
||||
createContext,
|
||||
useContext,
|
||||
useCallback,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
content: null,
|
||||
contentProps: null,
|
||||
customClassName: null,
|
||||
};
|
||||
|
||||
const ViewportDialogContext = createContext(null);
|
||||
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), []);
|
||||
|
||||
useEffect(() => {
|
||||
if (service) {
|
||||
service.setServiceImplementation({ hide, show, viewportIndex });
|
||||
}
|
||||
}, [hide, service, show, viewportIndex]);
|
||||
|
||||
const {
|
||||
content: ViewportDialogContent,
|
||||
contentProps,
|
||||
customClassName,
|
||||
} = options;
|
||||
|
||||
return (
|
||||
<Provider value={{ show, hide }}>
|
||||
<div className="relative w-full h-full">
|
||||
{ViewportDialogContent && (
|
||||
<Dialog className={customClassName}>
|
||||
<ViewportDialogContent {...contentProps} show={show} hide={hide} />
|
||||
</Dialog>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
ViewportDialogProvider.propTypes = {
|
||||
/** Children that will be wrapped with Modal Context */
|
||||
children: PropTypes.oneOfType([
|
||||
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;
|
||||
165
platform/ui/src/contextProviders/ViewportDialogProvider.mdx
Normal file
165
platform/ui/src/contextProviders/ViewportDialogProvider.mdx
Normal file
@ -0,0 +1,165 @@
|
||||
---
|
||||
name: ViewportDialogProvider
|
||||
route: customHooks/viewportDialogProvider
|
||||
---
|
||||
|
||||
import { Playground, Props } from 'docz';
|
||||
|
||||
<!-- This is a workaround to import things from ohif/core as docz does not allow us to access window element and @ohif/core does use it once we import to instanciate cornerstone -->
|
||||
import { UIViewportDialogService, ServicesManager } from './../../../core/src/services';
|
||||
|
||||
import {
|
||||
ViewportDialogProvider,
|
||||
Dialog,
|
||||
Button,
|
||||
useViewportDialog,
|
||||
Notification,
|
||||
} from '@ohif/ui';
|
||||
|
||||
# Viewport Dialog Provider
|
||||
|
||||
This is a context provider that allow the application to share the modal
|
||||
component across all application.
|
||||
|
||||
## Sample
|
||||
|
||||
<Playground>
|
||||
{() => {
|
||||
const ViewportNotification = ({ hide }) => {
|
||||
return (
|
||||
<Notification
|
||||
text="Track all measurement for this series?"
|
||||
type="info"
|
||||
actionButtons={
|
||||
<div>
|
||||
<Button onClick={hide}>No</Button>
|
||||
<Button onClick={hide} className="ml-2">
|
||||
No, do not ask again
|
||||
</Button>
|
||||
<Button onClick={hide} className="ml-2" color="primary">
|
||||
Yes
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const ViewportActionButtons = () => {
|
||||
const dialog = useViewportDialog();
|
||||
return (
|
||||
<Button
|
||||
onClick={() =>
|
||||
dialog.show({
|
||||
content: ViewportNotification,
|
||||
})
|
||||
}
|
||||
>
|
||||
Open Dialog
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div className="w-full flex flex-row p-4" style={{height: '400px'}}>
|
||||
<div className="flex flex-1 items-center justify-center h-full w-full bg-black text-white border border-primary-main">
|
||||
<ViewportDialogProvider dialog={Dialog}>
|
||||
<div className="flex flex-1 flex-col items-center justify-center h-full">
|
||||
<ViewportActionButtons />
|
||||
<span>CONTENT 1</span>
|
||||
</div>
|
||||
</ViewportDialogProvider>
|
||||
</div>
|
||||
<div className="flex flex-1 items-center justify-center h-full w-full bg-black text-white border border-primary-main">
|
||||
<ViewportDialogProvider dialog={Dialog}>
|
||||
<div className="flex flex-1 flex-col items-center justify-center h-full">
|
||||
<ViewportActionButtons />
|
||||
<span>CONTENT 1</span>
|
||||
</div>
|
||||
</ViewportDialogProvider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Playground>
|
||||
|
||||
## Example using UIViewportDialogService
|
||||
|
||||
<Playground>
|
||||
{() => {
|
||||
const ViewportNotification = ({ hide }) => {
|
||||
return (
|
||||
<Notification
|
||||
text="Track all measurement for this series?"
|
||||
type="info"
|
||||
actionButtons={
|
||||
<div>
|
||||
<Button onClick={hide}>No</Button>
|
||||
<Button className="ml-2">No, do not ask again</Button>
|
||||
<Button className="ml-2" color="primary">
|
||||
Yes
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
// Creating servicesManager and register services should be in the root of your app
|
||||
const servicesManager = new ServicesManager();
|
||||
servicesManager.registerServices([UIViewportDialogService]);
|
||||
// Get service instance
|
||||
const _UIViewportDialogService =
|
||||
servicesManager.services.UIViewportDialogService;
|
||||
return (
|
||||
<div className="w-full flex flex-col p-4" style={{height: '400px'}}>
|
||||
<div className="flex flex-2">
|
||||
<Button
|
||||
className="mr-4 mb-4"
|
||||
onClick={() =>
|
||||
_UIViewportDialogService.show({
|
||||
content: ViewportNotification,
|
||||
viewportIndex: 0,
|
||||
})
|
||||
}
|
||||
>
|
||||
Open dialog on 0
|
||||
</Button>
|
||||
<Button
|
||||
className="mr-4 mb-4"
|
||||
onClick={() =>
|
||||
_UIViewportDialogService.show({
|
||||
content: ViewportNotification,
|
||||
viewportIndex: 1,
|
||||
})
|
||||
}
|
||||
>
|
||||
Open dialog on 1
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-1 w-full flex-row">
|
||||
<ViewportDialogProvider
|
||||
dialog={Dialog}
|
||||
service={_UIViewportDialogService}
|
||||
viewportIndex={0}
|
||||
>
|
||||
<div className="flex flex-1 items-center justify-center h-full bg-black text-white border border-primary-main">
|
||||
CONTENT 0
|
||||
</div>
|
||||
</ViewportDialogProvider>
|
||||
<ViewportDialogProvider
|
||||
dialog={Dialog}
|
||||
service={_UIViewportDialogService}
|
||||
viewportIndex={1}
|
||||
>
|
||||
<div className="flex flex-1 items-center justify-center h-full bg-black text-white border border-primary-main">
|
||||
CONTENT 1
|
||||
</div>
|
||||
</ViewportDialogProvider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Playground>
|
||||
|
||||
|
||||
## Properties:
|
||||
|
||||
<Props of={ViewportDialogProvider} />
|
||||
@ -3,4 +3,9 @@ export {
|
||||
useModal,
|
||||
withModal,
|
||||
ModalConsumer,
|
||||
} from './ModalProvider.js';
|
||||
} from './ModalProvider';
|
||||
|
||||
export {
|
||||
default as ViewportDialogProvider,
|
||||
useViewportDialog,
|
||||
} from './ViewportDialogProvider';
|
||||
|
||||
@ -15,7 +15,7 @@ export const Sidebar = React.forwardRef((props, ref) => {
|
||||
const menus = useMenus({ query });
|
||||
const currentDoc = useCurrentDoc();
|
||||
const currentDocRef = useRef();
|
||||
const handleChange = ev => {
|
||||
const handleChange = (ev) => {
|
||||
setQuery(ev.target.value);
|
||||
};
|
||||
useEffect(() => {
|
||||
@ -40,26 +40,26 @@ export const Sidebar = React.forwardRef((props, ref) => {
|
||||
'Data Display',
|
||||
'Other',
|
||||
],
|
||||
'Custom Hooks': ['ModalProvider'],
|
||||
'Custom Hooks': ['ModalProvider', 'ViewportDialogProvider'],
|
||||
Examples: ['Views'],
|
||||
System: ['Colors'],
|
||||
};
|
||||
|
||||
const renderMenuCategories = () => {
|
||||
return Object.keys(customMenus).map(menuName => {
|
||||
return Object.keys(customMenus).map((menuName) => {
|
||||
return (
|
||||
<div key={menuName}>
|
||||
<h2 className="pl-2 border-l-8 -ml-4 border-secondary-active">
|
||||
{menuName}
|
||||
</h2>
|
||||
{customMenus[menuName].map(item => item)}
|
||||
{customMenus[menuName].map((item) => item)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const getMenuCategory = menuName => {
|
||||
return Object.keys(MENU_CATEGORIES).find(category => {
|
||||
const getMenuCategory = (menuName) => {
|
||||
return Object.keys(MENU_CATEGORIES).find((category) => {
|
||||
if (MENU_CATEGORIES[category].includes(menuName)) {
|
||||
return category;
|
||||
}
|
||||
@ -99,7 +99,7 @@ export const Sidebar = React.forwardRef((props, ref) => {
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{menus &&
|
||||
menus.map(menu => {
|
||||
menus.map((menu) => {
|
||||
const isGroup = !!menu.menu;
|
||||
const Component = isGroup ? NavGroup : NavLink;
|
||||
const menuCategory = getMenuCategory(menu.name) || null;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user