OHIF-43: Modal Context Provider (#1538)

This commit is contained in:
Gustavo André Lelis 2020-03-20 16:17:37 -03:00 committed by James A. Petts
parent b7a72f5225
commit e54ccd1373
3 changed files with 147 additions and 3 deletions

View File

@ -1,4 +1,10 @@
import * as utils from './src/utils/';
import {
ModalProvider,
ModalConsumer,
useModal,
withModal,
} from './src/contextProviders';
export {
Button,
@ -12,7 +18,5 @@ export {
ThemeWrapper,
Typography,
} from './src/components';
export { utils };
export { StudyList } from './src/views';
export { ModalProvider, ModalConsumer, useModal, withModal, utils };

View File

@ -0,0 +1,134 @@
import React, {
useState,
createContext,
useContext,
useEffect,
useCallback,
} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
const ModalContext = createContext(null);
const { Provider } = ModalContext;
export const useModal = () => useContext(ModalContext);
/**
* UI Modal
*
* @typedef {Object} ModalProps
* @property {ReactElement|HTMLElement} [content=null] Modal content.
* @property {Object} [contentProps=null] Modal content props.
* @property {boolean} [shouldCloseOnEsc=false] Modal is dismissible via the esc key.
* @property {boolean} [isOpen=true] Make the Modal visible or hidden.
* @property {boolean} [closeButton=true] Should the modal body render the close button.
* @property {string} [title=null] Should the modal render the title independently of the body content.
* @property {string} [customClassName=null] The custom class to style the modal.
*/
const ModalProvider = ({ children, modal: Modal, service }) => {
const DEFAULT_OPTIONS = {
content: null,
contentProps: null,
shouldCloseOnEsc: false,
isOpen: true,
closeButton: true,
title: null,
customClassName: '',
};
const [options, setOptions] = useState(DEFAULT_OPTIONS);
/**
* Sets the implementation of a modal service that can be used by extensions.
*
* @returns void
*/
useEffect(() => {
if (service) {
service.setServiceImplementation({ hide, show });
}
}, [hide, service, show]);
/**
* Show the modal and override its configuration props.
*
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, closeButton, title, customClassName }
* @returns void
*/
const show = useCallback(props => setOptions({ ...options, ...props }), [
options,
]);
/**
* Hide the modal and set its properties to default.
*
* @returns void
*/
const hide = useCallback(() => setOptions(DEFAULT_OPTIONS), [
DEFAULT_OPTIONS,
]);
const {
content: ModalContent,
contentProps,
isOpen,
title,
customClassName,
shouldCloseOnEsc,
closeButton,
} = options;
return (
<Provider value={{ show, hide }}>
{ModalContent && (
<Modal
className={classNames(customClassName, ModalContent.className)}
shouldCloseOnEsc={shouldCloseOnEsc}
isOpen={isOpen}
title={title}
closeButton={closeButton}
onClose={hide}
>
<ModalContent {...contentProps} show={show} hide={hide} />
</Modal>
)}
{children}
</Provider>
);
};
/**
* Higher Order Component to use the modal methods through a Class Component.
*
* @returns
*/
export const withModal = Component => {
return function WrappedComponent(props) {
const { show, hide } = useModal();
return <Component {...props} modal={{ show, hide }} />;
};
};
ModalProvider.defaultProps = {
service: null,
};
ModalProvider.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]).isRequired,
modal: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
PropTypes.func,
]).isRequired,
service: PropTypes.shape({
setServiceImplementation: PropTypes.func,
}),
};
export default ModalProvider;
export const ModalConsumer = ModalContext.Consumer;

View File

@ -0,0 +1,6 @@
export {
default as ModalProvider,
useModal,
withModal,
ModalConsumer,
} from './ModalProvider.js';