UNPKG

@onehat/ui

Version:
227 lines (215 loc) 5.92 kB
import { forwardRef, useState } from 'react'; import { Icon, Modal, ModalBackdrop, ModalHeader, ModalContent, ModalCloseButton, ModalBody, ModalFooter, Pressable, Text, } from '@project-components/Gluestack'; import { CURRENT_MODE, UI_MODE_WEB, UI_MODE_NATIVE, } from '../../Constants/UiModes.js'; import clsx from 'clsx'; import Button from '../Buttons/Button.js'; import Panel from '../Panel/Panel.js'; import Footer from '../Layout/Footer.js'; import useAdjustedWindowSize from '../../Hooks/useAdjustedWindowSize.js'; import testProps from '../../Functions/testProps.js'; import _ from 'lodash'; // This HOC enables usage of more complex dialogs in the wrapped component. // Use withAlert for simple alerts, confirmations, and info dialogs. export default function withModal(WrappedComponent) { return forwardRef((props, ref) => { if (props.disableWithModal || props.alreadyHasWithModal) { return <WrappedComponent {...props} ref={ref} />; } const [title, setTitle] = useState(''), [canClose, setCanClose] = useState(true), [includeCancel, setIncludeCancel] = useState(false), [isModalShown, setIsModalShown] = useState(false), [h, setHeight] = useState(), [w, setWidth] = useState(), [onOk, setOnOk] = useState(), [okBtnLabel, setOkBtnLabel] = useState(), [onYes, setOnYes] = useState(), [onNo, setOnNo] = useState(), [onCancel, setOnCancel] = useState(), [customButtons, setCustomButtons] = useState(), [body, setBody] = useState(), [whichModal, setWhichModal] = useState(), [testID, setTestID] = useState('Modal'), [windowWidth, windowHeight] = useAdjustedWindowSize(w, h), hideModal = () => { setIsModalShown(false); }, showModal = (args) => { let { title = null, body = null, canClose = false, includeCancel = false, onCancel = null, onOk = null, okBtnLabel = null, onYes = null, onNo = null, customButtons = null, h = null, w = null, whichModal = null, testID = null, formProps = null, // deprecated } = args; if (formProps) { // deprecated formProps bc we were getting circular dependencies throw new Error('withModal: formProps is deprecated. Instead, insert the <Form> in "body" directly from the component that called showModal.'); } if (!body) { throw new Error('withModal: body is required for showModal'); } if (_.isFunction(body)) { body = body(); } setTitle(title); setBody(body); setCanClose(canClose); setIncludeCancel(includeCancel); setOnCancel(() => onCancel); setOnOk(onOk ? () => onOk : null); setOkBtnLabel(okBtnLabel || 'OK'); setOnYes(onYes ? () => onYes : null); setOnNo(onNo ? () => onNo : null); setCustomButtons(customButtons); setHeight(h); // || 250 setWidth(w); // || 400 setWhichModal(whichModal); setIsModalShown(true); if (testID) { setTestID(testID); } }, updateModalBody = (newBody) => { setBody(newBody); }; let modalBody, buttons = []; if (isModalShown) { // assemble buttons if (includeCancel) { buttons.push(<Button {...testProps('cancelBtn')} key="cancelBtn" onPress={onCancel || hideModal} colorScheme="coolGray" className="mr-2" text="Cancel" variant="outline" // or unstyled />); } if (onNo) { buttons.push(<Button {...testProps('noBtn')} key="noBtn" onPress={onNo} className="text-grey-800 mr-2" text="No" variant="outline" />); } if (onOk) { buttons.push(<Button {...testProps('okBtn')} key="okBtn" onPress={onOk} text={okBtnLabel} className="text-white" />); } if (onYes) { buttons.push(<Button {...testProps('yesBtn')} key="yesBtn" onPress={onYes} text="Yes" className="text-white" />); } if (customButtons) { _.each(customButtons, (button) => { buttons.push(button); }); } // assemble body modalBody = body; if (h || w || title) { let footer = null; if (buttons && buttons.length > 0) { footer = <Footer className={clsx( 'justify-end', 'py-2', 'pr-4', 'bg-grey-100', )} >{buttons}</Footer>; } modalBody = <Panel title={title} isCollapsible={false} className="withModal-Panel bg-white" h={h > windowHeight ? windowHeight : h} w={w > windowWidth ? windowWidth : w} isWindow={true} disableAutoFlex={true} onClose={canClose ? hideModal : null} footer={footer} isScrollable={true} >{modalBody}</Panel> } } let modalBackdrop = <ModalBackdrop className="withModal-ModalBackdrop" /> if (CURRENT_MODE === UI_MODE_NATIVE) { // Gluestack's ModalBackdrop was not working on Native, // so workaround is to do it manually for now modalBackdrop = <Pressable onPress={() => hideModal()} className={clsx( 'withModal-ModalBackdrop-replacment', 'h-full', 'w-full', 'absolute', 'top-0', 'left-0', 'bg-[#000]', 'opacity-50', )} />; } return <> <WrappedComponent {...props} disableWithModal={false} alreadyHasWithModal={true} ref={ref} showModal={showModal} hideModal={onCancel || hideModal} updateModalBody={updateModalBody} isModalShown={isModalShown} whichModal={whichModal} /> {isModalShown && <Modal isOpen={true} onClose={onCancel || (canClose ? hideModal : null)} className="withModal-Modal" {...testProps(testID)} > {modalBackdrop} {modalBody} </Modal>} </>; }); }