diff --git a/platform/core/src/index.js b/platform/core/src/index.js
index 3f7727d2d..f12d7e2ec 100644
--- a/platform/core/src/index.js
+++ b/platform/core/src/index.js
@@ -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,
};
diff --git a/platform/core/src/services/UIViewportDialogService/index.js b/platform/core/src/services/UIViewportDialogService/index.js
new file mode 100644
index 000000000..8b0aeddeb
--- /dev/null
+++ b/platform/core/src/services/UIViewportDialogService/index.js
@@ -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;
+ },
+};
diff --git a/platform/core/src/services/index.js b/platform/core/src/services/index.js
index a7fa17072..096283956 100644
--- a/platform/core/src/services/index.js
+++ b/platform/core/src/services/index.js
@@ -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,
};
diff --git a/platform/ui/index.js b/platform/ui/index.js
index 0097336b1..0ab595a6d 100644
--- a/platform/ui/index.js
+++ b/platform/ui/index.js
@@ -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,
diff --git a/platform/ui/src/components/Dialog/Dialog.jsx b/platform/ui/src/components/Dialog/Dialog.jsx
new file mode 100644
index 000000000..aa0295709
--- /dev/null
+++ b/platform/ui/src/components/Dialog/Dialog.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+const Dialog = ({ children }) => {
+ return
{children}
;
+};
+
+Dialog.propTypes = {
+ children: PropTypes.node,
+};
+
+export default Dialog;
diff --git a/platform/ui/src/components/Dialog/index.js b/platform/ui/src/components/Dialog/index.js
new file mode 100644
index 000000000..42ce789bf
--- /dev/null
+++ b/platform/ui/src/components/Dialog/index.js
@@ -0,0 +1,2 @@
+import Dialog from './Dialog';
+export default Dialog;
diff --git a/platform/ui/src/components/index.js b/platform/ui/src/components/index.js
index 72e217170..b5825a9b6 100644
--- a/platform/ui/src/components/index.js
+++ b/platform/ui/src/components/index.js
@@ -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,
diff --git a/platform/ui/src/contextProviders/ViewportDialogProvider.jsx b/platform/ui/src/contextProviders/ViewportDialogProvider.jsx
new file mode 100644
index 000000000..e412188ee
--- /dev/null
+++ b/platform/ui/src/contextProviders/ViewportDialogProvider.jsx
@@ -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 (
+
+
+ {ViewportDialogContent && (
+
+ )}
+ {children}
+
+
+ );
+};
+
+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;
diff --git a/platform/ui/src/contextProviders/ViewportDialogProvider.mdx b/platform/ui/src/contextProviders/ViewportDialogProvider.mdx
new file mode 100644
index 000000000..ffc0ee55e
--- /dev/null
+++ b/platform/ui/src/contextProviders/ViewportDialogProvider.mdx
@@ -0,0 +1,165 @@
+---
+name: ViewportDialogProvider
+route: customHooks/viewportDialogProvider
+---
+
+import { Playground, Props } from 'docz';
+
+
+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
+
+
+ {() => {
+ const ViewportNotification = ({ hide }) => {
+ return (
+
+
+
+
+
+ }
+ />
+ );
+ };
+ const ViewportActionButtons = () => {
+ const dialog = useViewportDialog();
+ return (
+
+ );
+ };
+ return (
+
+ );
+ }}
+
+
+## Example using UIViewportDialogService
+
+
+ {() => {
+ const ViewportNotification = ({ hide }) => {
+ return (
+
+
+
+
+
+ }
+ />
+ );
+ };
+ // 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 (
+
+
+
+
+
+
+
+
+ CONTENT 0
+
+
+
+
+ CONTENT 1
+
+
+
+
+ );
+ }}
+
+
+
+## Properties:
+
+
diff --git a/platform/ui/src/contextProviders/index.js b/platform/ui/src/contextProviders/index.js
index f2e6811fb..177d456af 100644
--- a/platform/ui/src/contextProviders/index.js
+++ b/platform/ui/src/contextProviders/index.js
@@ -3,4 +3,9 @@ export {
useModal,
withModal,
ModalConsumer,
-} from './ModalProvider.js';
+} from './ModalProvider';
+
+export {
+ default as ViewportDialogProvider,
+ useViewportDialog,
+} from './ViewportDialogProvider';
diff --git a/platform/ui/src/gatsby-theme-docz/components/Sidebar/index.js b/platform/ui/src/gatsby-theme-docz/components/Sidebar/index.js
index 587ca6365..c34861d72 100644
--- a/platform/ui/src/gatsby-theme-docz/components/Sidebar/index.js
+++ b/platform/ui/src/gatsby-theme-docz/components/Sidebar/index.js
@@ -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 (
{menuName}
- {customMenus[menuName].map(item => item)}
+ {customMenus[menuName].map((item) => item)}
);
});
};
- 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;