feat: 🎸 New modal provider (#1110)
* feat: 🎸 New modal provider A new modal provider/context component to unify modal related code and simplify modal use Closes: #1086 * refactor(ModalContext): Keep current modal lib * Update modal provider version according to PR #1116 * Update modal provider props to use inner props * Add custom class prop * CR Update: Refactor provider to extract specific modal * Fix modal import * CR Update: Move from spread to object assign * CR Update: Add proptypes, use classnames dependency over interpolation and rename modal
This commit is contained in:
parent
8baba22a2e
commit
5ee832b195
@ -16,6 +16,7 @@ import { QuickSwitch } from './quickSwitch';
|
|||||||
import { RoundedButtonGroup } from './roundedButtonGroup';
|
import { RoundedButtonGroup } from './roundedButtonGroup';
|
||||||
import { SelectTree } from './selectTree';
|
import { SelectTree } from './selectTree';
|
||||||
import { SimpleDialog } from './simpleDialog';
|
import { SimpleDialog } from './simpleDialog';
|
||||||
|
import { OHIFModal } from './ohifModal';
|
||||||
import {
|
import {
|
||||||
PageToolbar,
|
PageToolbar,
|
||||||
StudyList,
|
StudyList,
|
||||||
@ -52,4 +53,5 @@ export {
|
|||||||
AboutModal,
|
AboutModal,
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
UserPreferencesModal,
|
UserPreferencesModal,
|
||||||
|
OHIFModal,
|
||||||
};
|
};
|
||||||
|
|||||||
60
platform/ui/src/components/ohifModal/OHIFModal.js
Normal file
60
platform/ui/src/components/ohifModal/OHIFModal.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ReactBootstrapModal from 'react-bootstrap-modal';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
const OHIFModal = ({
|
||||||
|
className,
|
||||||
|
closeButton,
|
||||||
|
backdrop,
|
||||||
|
keyboard,
|
||||||
|
show,
|
||||||
|
title,
|
||||||
|
onHide,
|
||||||
|
footer: Footer,
|
||||||
|
header: Header,
|
||||||
|
children: Component,
|
||||||
|
}) => (
|
||||||
|
<ReactBootstrapModal
|
||||||
|
className={classNames('modal fade themed in', className)}
|
||||||
|
backdrop={backdrop}
|
||||||
|
keyboard={keyboard}
|
||||||
|
show={show}
|
||||||
|
large={true}
|
||||||
|
title={title}
|
||||||
|
onHide={onHide}
|
||||||
|
>
|
||||||
|
{(Header || title) && (
|
||||||
|
<ReactBootstrapModal.Header closeButton={closeButton}>
|
||||||
|
{title && (
|
||||||
|
<ReactBootstrapModal.Title>{title}</ReactBootstrapModal.Title>
|
||||||
|
)}
|
||||||
|
{Header && <Header hide={onHide} />}
|
||||||
|
</ReactBootstrapModal.Header>
|
||||||
|
)}
|
||||||
|
<ReactBootstrapModal.Body>
|
||||||
|
{Component && <Component hide={onHide} />}
|
||||||
|
</ReactBootstrapModal.Body>
|
||||||
|
{Footer && (
|
||||||
|
<ReactBootstrapModal.Footer>
|
||||||
|
{' '}
|
||||||
|
<Footer hide={onHide} />
|
||||||
|
</ReactBootstrapModal.Footer>
|
||||||
|
)}
|
||||||
|
</ReactBootstrapModal>
|
||||||
|
);
|
||||||
|
|
||||||
|
OHIFModal.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
closeButton: PropTypes.bool,
|
||||||
|
backdrop: PropTypes.bool,
|
||||||
|
keyboard: PropTypes.bool,
|
||||||
|
show: PropTypes.bool,
|
||||||
|
title: PropTypes.string,
|
||||||
|
onHide: PropTypes.func,
|
||||||
|
footer: PropTypes.node,
|
||||||
|
header: PropTypes.node,
|
||||||
|
children: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default OHIFModal;
|
||||||
39
platform/ui/src/components/ohifModal/__docs__/ohifModal.mdx
Normal file
39
platform/ui/src/components/ohifModal/__docs__/ohifModal.mdx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
name: OHIFModal
|
||||||
|
menu: Components
|
||||||
|
route: /components/ohif-modal
|
||||||
|
---
|
||||||
|
|
||||||
|
import { Playground, Props } from 'docz'
|
||||||
|
import { State } from 'react-powerplug'
|
||||||
|
import { OHIFModal } from './../index'
|
||||||
|
import NameSpace from '../../../__docs__/NameSpace'
|
||||||
|
|
||||||
|
# OHIF Modal
|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
<Playground>
|
||||||
|
<State
|
||||||
|
initial={{}}
|
||||||
|
>
|
||||||
|
{({ state, setState }) => (
|
||||||
|
<React.Fragment>
|
||||||
|
<div>
|
||||||
|
<pre>{JSON.stringify(state, null, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
<div style={{ maxWidth: '400px', margin: '0 auto' }}>
|
||||||
|
<OHIFModal />
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</State>
|
||||||
|
</Playground>
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
<Props of={OHIFModal} />
|
||||||
|
|
||||||
|
## Translation Namespace
|
||||||
|
|
||||||
|
<NameSpace name="OHIFModal" />
|
||||||
2
platform/ui/src/components/ohifModal/index.js
Normal file
2
platform/ui/src/components/ohifModal/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import OHIFModal from './OHIFModal';
|
||||||
|
export { OHIFModal };
|
||||||
@ -25,6 +25,7 @@ import {
|
|||||||
AboutModal,
|
AboutModal,
|
||||||
UserPreferences,
|
UserPreferences,
|
||||||
UserPreferencesModal,
|
UserPreferencesModal,
|
||||||
|
OHIFModal,
|
||||||
} from './components';
|
} from './components';
|
||||||
import { useDebounce, useMedia } from './hooks';
|
import { useDebounce, useMedia } from './hooks';
|
||||||
|
|
||||||
@ -52,6 +53,11 @@ import SnackbarProvider, {
|
|||||||
useSnackbarContext,
|
useSnackbarContext,
|
||||||
withSnackbar,
|
withSnackbar,
|
||||||
} from './utils/SnackbarProvider';
|
} from './utils/SnackbarProvider';
|
||||||
|
import ModalProvider, {
|
||||||
|
useModal,
|
||||||
|
withModal,
|
||||||
|
ModalConsumer,
|
||||||
|
} from './utils/ModalProvider';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
// Elements
|
// Elements
|
||||||
@ -100,6 +106,11 @@ export {
|
|||||||
SnackbarProvider,
|
SnackbarProvider,
|
||||||
useSnackbarContext,
|
useSnackbarContext,
|
||||||
withSnackbar,
|
withSnackbar,
|
||||||
|
ModalProvider,
|
||||||
|
useModal,
|
||||||
|
ModalConsumer,
|
||||||
|
withModal,
|
||||||
|
OHIFModal,
|
||||||
// Hooks
|
// Hooks
|
||||||
useDebounce,
|
useDebounce,
|
||||||
useMedia,
|
useMedia,
|
||||||
|
|||||||
83
platform/ui/src/utils/ModalProvider.js
Normal file
83
platform/ui/src/utils/ModalProvider.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import React, { useState, createContext, useContext } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
const ModalContext = createContext(null);
|
||||||
|
const { Provider, Consumer } = ModalContext;
|
||||||
|
|
||||||
|
export const useModal = () => useContext(ModalContext);
|
||||||
|
|
||||||
|
const ModalProvider = ({ children, modal: Modal }) => {
|
||||||
|
const DEFAULT_OPTIONS = {
|
||||||
|
component: null /* The component instance inside the modal. */,
|
||||||
|
backdrop: false /* Should the modal render a backdrop overlay. */,
|
||||||
|
keyboard: false /* Modal is dismissible via the esc key. */,
|
||||||
|
show: true /* Make the Modal visible or hidden. */,
|
||||||
|
closeButton: true /* Should the modal body render the close button. */,
|
||||||
|
title: null /* Should the modal render the title independently of the body content. */,
|
||||||
|
customClassName: null /* The custom class to style the modal. */,
|
||||||
|
};
|
||||||
|
|
||||||
|
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the modal and override its configuration props.
|
||||||
|
*
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
const show = (component, props = {}) =>
|
||||||
|
setOptions(Object.assign({}, options, props, { component }));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the modal and set its properties to default.
|
||||||
|
*
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
const hide = () => setOptions(DEFAULT_OPTIONS);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Provider value={{ ...options, show, hide }}>
|
||||||
|
<Consumer>
|
||||||
|
{props => {
|
||||||
|
const { component, footer, header, customClassName } = props;
|
||||||
|
return component ? (
|
||||||
|
<Modal
|
||||||
|
className={classNames(customClassName, component.className)}
|
||||||
|
backdrop={options.backdrop}
|
||||||
|
keyboard={options.keyboard}
|
||||||
|
show={options.show}
|
||||||
|
title={options.title}
|
||||||
|
closeButton={options.closeButton}
|
||||||
|
onHide={hide}
|
||||||
|
footer={footer}
|
||||||
|
header={header}
|
||||||
|
>
|
||||||
|
{component}
|
||||||
|
</Modal>
|
||||||
|
) : null;
|
||||||
|
}}
|
||||||
|
</Consumer>
|
||||||
|
{children}
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ModalProvider.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
modal: PropTypes.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Higher Order Component to use the modal methods through a Class Component.
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const withModal = Component => {
|
||||||
|
return function WrappedComponent(props) {
|
||||||
|
return <Component {...props} modalContext={{ ...useModal() }} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalProvider;
|
||||||
|
|
||||||
|
export const ModalConsumer = ModalContext.Consumer;
|
||||||
@ -28,7 +28,7 @@ import { BrowserRouter as Router } from 'react-router-dom';
|
|||||||
import { getActiveContexts } from './store/layout/selectors.js';
|
import { getActiveContexts } from './store/layout/selectors.js';
|
||||||
import i18n from '@ohif/i18n';
|
import i18n from '@ohif/i18n';
|
||||||
import store from './store';
|
import store from './store';
|
||||||
import { SnackbarProvider } from '@ohif/ui';
|
import { SnackbarProvider, ModalProvider, OHIFModal } from '@ohif/ui';
|
||||||
|
|
||||||
// Contexts
|
// Contexts
|
||||||
import WhiteLabellingContext from './context/WhiteLabellingContext';
|
import WhiteLabellingContext from './context/WhiteLabellingContext';
|
||||||
@ -85,6 +85,7 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { whiteLabelling, routerBasename } = this.props;
|
||||||
const userManager = this._userManager;
|
const userManager = this._userManager;
|
||||||
const config = {
|
const config = {
|
||||||
appConfig: this._appConfig,
|
appConfig: this._appConfig,
|
||||||
@ -97,12 +98,12 @@ class App extends Component {
|
|||||||
<I18nextProvider i18n={i18n}>
|
<I18nextProvider i18n={i18n}>
|
||||||
<OidcProvider store={store} userManager={userManager}>
|
<OidcProvider store={store} userManager={userManager}>
|
||||||
<UserManagerContext.Provider value={userManager}>
|
<UserManagerContext.Provider value={userManager}>
|
||||||
<Router basename={this.props.routerBasename}>
|
<Router basename={routerBasename}>
|
||||||
<WhiteLabellingContext.Provider
|
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||||
value={this.props.whiteLabelling}
|
|
||||||
>
|
|
||||||
<SnackbarProvider>
|
<SnackbarProvider>
|
||||||
|
<ModalProvider modal={OHIFModal}>
|
||||||
<OHIFStandaloneViewer userManager={userManager} />
|
<OHIFStandaloneViewer userManager={userManager} />
|
||||||
|
</ModalProvider>
|
||||||
</SnackbarProvider>
|
</SnackbarProvider>
|
||||||
</WhiteLabellingContext.Provider>
|
</WhiteLabellingContext.Provider>
|
||||||
</Router>
|
</Router>
|
||||||
@ -118,10 +119,12 @@ class App extends Component {
|
|||||||
<AppContext.Provider value={config}>
|
<AppContext.Provider value={config}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<I18nextProvider i18n={i18n}>
|
<I18nextProvider i18n={i18n}>
|
||||||
<Router basename={this.props.routerBasename}>
|
<Router basename={routerBasename}>
|
||||||
<WhiteLabellingContext.Provider value={this.props.whiteLabelling}>
|
<WhiteLabellingContext.Provider value={whiteLabelling}>
|
||||||
<SnackbarProvider>
|
<SnackbarProvider>
|
||||||
|
<ModalProvider modal={OHIFModal}>
|
||||||
<OHIFStandaloneViewer />
|
<OHIFStandaloneViewer />
|
||||||
|
</ModalProvider>
|
||||||
</SnackbarProvider>
|
</SnackbarProvider>
|
||||||
</WhiteLabellingContext.Provider>
|
</WhiteLabellingContext.Provider>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user