import React from 'react'; import Modal, { ModalProps } from '@/components/elements/Modal'; import ModalContext from '@/context/ModalContext'; import isEqual from 'react-fast-compare'; export interface AsModalProps { visible: boolean; onModalDismissed?: () => void; } type ExtendedModalProps = Omit<ModalProps, 'appear' | 'visible' | 'onDismissed'>; interface State { render: boolean; visible: boolean; modalProps: ExtendedModalProps | undefined; } type ExtendedComponentType<T> = (C: React.ComponentType<T>) => React.ComponentType<T & AsModalProps>; // eslint-disable-next-line @typescript-eslint/ban-types function asModal<P extends object> (modalProps?: ExtendedModalProps | ((props: P) => ExtendedModalProps)): ExtendedComponentType<P> { return function (Component) { return class extends React.PureComponent <P & AsModalProps, State> { static displayName = `asModal(${Component.displayName})`; constructor (props: P & AsModalProps) { super(props); this.state = { render: props.visible, visible: props.visible, modalProps: typeof modalProps === 'function' ? modalProps(this.props) : modalProps, }; } componentDidUpdate (prevProps: Readonly<P & AsModalProps>) { const mapped = typeof modalProps === 'function' ? modalProps(this.props) : modalProps; if (!isEqual(this.state.modalProps, mapped)) { // noinspection JSPotentiallyInvalidUsageOfThis this.setState({ modalProps: mapped }); } if (prevProps.visible && !this.props.visible) { // noinspection JSPotentiallyInvalidUsageOfThis this.setState({ visible: false }); } else if (!prevProps.visible && this.props.visible) { // noinspection JSPotentiallyInvalidUsageOfThis this.setState({ render: true, visible: true }); } } dismiss = () => this.setState({ visible: false }); toggleSpinner = (value?: boolean) => this.setState(s => ({ modalProps: { ...s.modalProps, showSpinnerOverlay: value || false, }, })); render () { return ( <ModalContext.Provider value={{ dismiss: this.dismiss.bind(this), toggleSpinner: this.toggleSpinner.bind(this), }} > { this.state.render ? <Modal appear visible={this.state.visible} onDismissed={() => this.setState({ render: false }, () => { if (typeof this.props.onModalDismissed === 'function') { this.props.onModalDismissed(); } })} {...this.state.modalProps} > <Component {...this.props}/> </Modal> : null } </ModalContext.Provider> ); } }; }; } export default asModal;