UNPKG

saagie-ui

Version:

Saagie UI from Saagie Design System

197 lines (178 loc) 4.74 kB
import React, { useEffect } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { useId } from '@reach/auto-id'; import { ClickAwayListener } from '../../helpers/clickAwayListener/ClickAwayListener'; import { modifierCSS } from '../../../helpers'; const propTypes = { /** * Children to set inside the Modal. * * @see ModalHeader * @see ModalBody * @see ModalFooter */ children: PropTypes.node, defaultClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), /** * Class name for the Modal. */ className: PropTypes.string, /** * The attributes to give to the content of the modal. */ contentAttributes: PropTypes.object, /** * The tag of the content of the modal. */ contentTag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), /** * The default class name of the content of the modal. */ contentDefaultClassName: PropTypes.oneOfType([ PropTypes.string, PropTypes.arrayOf(PropTypes.string), ]), /** * Disable the outside click. Note that by default the outside click is enable and the overlay * is considered as outside. */ isClickOutsideDisabled: PropTypes.bool, /** * Disable the dismiss of the modal using Escape key. */ isEscapeKeyDisabled: PropTypes.bool, /** * Boolean to set the modal as fullscreen variant. */ isFullscreen: PropTypes.bool, /** * Boolean to open or close the */ isOpen: PropTypes.bool, isPortalDisabled: PropTypes.bool, /** * Callback function trigerred on close event. */ onClose: PropTypes.func, portalContainer: PropTypes.instanceOf(Element), /** * The size of the Modal */ size: PropTypes.oneOf(['', 'xxs', 'xs', 'sm', 'lg', 'xl', 'xxl']), /** * The custom tag for the root of the Modal */ tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), }; const defaultProps = { children: '', className: '', contentAttributes: {}, contentTag: 'div', contentDefaultClassName: 'sui-o-modal__content', defaultClassName: 'sui-o-modal', isClickOutsideDisabled: false, isEscapeKeyDisabled: false, isFullscreen: false, isOpen: false, isPortalDisabled: false, portalContainer: null, onClose: () => {}, size: '', tag: 'div', }; export const ModalContext = React.createContext({}); export const useModalContext = () => React.useContext(ModalContext); export const Modal = ({ children, className, contentAttributes, contentTag: ContentTag, contentDefaultClassName, defaultClassName, isClickOutsideDisabled, isEscapeKeyDisabled, isFullscreen, isOpen, onClose, size, isPortalDisabled, portalContainer, tag: Tag, ...attributes }) => { const classes = classnames( defaultClassName, className, isOpen ? 'as--open' : '', isFullscreen ? 'as--fullscreen' : '', modifierCSS(size, 'size'), ); const contentClasses = classnames( contentDefaultClassName, ); const handleClose = (event) => { event.stopPropagation(); if (onClose) { onClose(); } }; // Escape Key Listener useEffect(() => { /** * Function to handle the Escape key down. * @param {KeyboardEvent} event */ const handleKeyDown = (event) => { if (event.key !== 'Escape' && event.keyCode !== 27) { return; } // Swallow the event, in case someone is listening for the escape key on the body. event.stopPropagation(); if (!isEscapeKeyDisabled) { handleClose(); } }; if (!isEscapeKeyDisabled && isOpen) { window.addEventListener('keydown', handleKeyDown); } return () => { window.removeEventListener('keydown', handleKeyDown); }; }, [isOpen]); const id = useId(); const mountModal = () => { const portalTarget = portalContainer || document.body; const modal = ( <Tag className={classes} {...attributes}> <ClickAwayListener onClickOutside={handleClose} isClickOutsideDisabled={isClickOutsideDisabled} > <ContentTag role="dialog" aria-labelledby={`dialog:${id}:label`} aria-modal="true" id={`dialog:${id}`} className={contentClasses} {...contentAttributes}> {children} </ContentTag> </ClickAwayListener> </Tag> ); if (isPortalDisabled) { return modal; } return ReactDOM.createPortal(modal, portalTarget); }; return ( <ModalContext.Provider value={{ handleClose, id, isOpen }} > {mountModal()} </ModalContext.Provider> ); }; Modal.propTypes = propTypes; Modal.defaultProps = defaultProps;