File "modal.js"

Full Path: /home/warrior1/public_html/wp-content/plugins/elementor/app/assets/js/ui/modal/modal.js
File size: 3.59 KB
MIME-type: text/x-java
Charset: utf-8

import { useState, useRef, useEffect } from 'react';
import { arrayToClassName } from 'elementor-app/utils/utils.js';

import Button from 'elementor-app/ui/molecules/button';
import Grid from 'elementor-app/ui/grid/grid';
import Icon from 'elementor-app/ui/atoms/icon';
import Text from 'elementor-app/ui/atoms/text';

import ModalSection from './modal-section';
import ModalTip from './modal-tip';

import './modal.scss';

export default function ModalProvider( props ) {
	const [ show, setShow ] = useState( props.show ),
		hideModal = () => {
			setShow( false );

			// The purpose of the props.setShow is to sync an external state with the component inner state.
			if ( props.setShow ) {
				props.setShow( false );
			}
		},
		showModal = () => {
			setShow( true );

			// The purpose of the props.setShow is to sync an external state with the component inner state.
			if ( props.setShow ) {
				props.setShow( true );
			}
		},
		modalAttrs = {
			...props,
			show,
			hideModal,
			showModal,
		};

	useEffect( () => {
		// Sync with external state.
		setShow( props.show );
	}, [ props.show ] );

	return (
		<>
			{
				props.toggleButtonProps &&
				<Button { ...props.toggleButtonProps } onClick={ showModal } />
			}

			<Modal { ...modalAttrs }>
				{ props.children }
			</Modal>
		</>
	);
}

ModalProvider.propTypes = {
	children: PropTypes.node.isRequired,
	toggleButtonProps: PropTypes.object,
	title: PropTypes.string,
	icon: PropTypes.string,
	show: PropTypes.bool,
	setShow: PropTypes.func,
	onOpen: PropTypes.func,
	onClose: PropTypes.func,
};

ModalProvider.defaultProps = {
	show: false,
};

ModalProvider.Section = ModalSection;
ModalProvider.Tip = ModalTip;

export const Modal = ( props ) => {
	const modalRef = useRef( null ),
		closeRef = useRef( null ),
		closeModal = ( e ) => {
			const node = modalRef.current,
				closeNode = closeRef.current,
				isInCloseNode = closeNode && closeNode.contains( e.target );

			// Ignore if click is inside the modal
			if ( node && node.contains( e.target ) && ! isInCloseNode ) {
				return;
			}

			props.hideModal();

			if ( props.onClose ) {
				props.onClose( e );
			}
		};

	useEffect( () => {
		if ( props.show ) {
			document.addEventListener( 'mousedown', closeModal, false );
			props.onOpen?.();
		}

		return () => document.removeEventListener( 'mousedown', closeModal, false );
	}, [ props.show ] );

	if ( ! props.show ) {
		return null;
	}

	return (
		// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
		<div className="eps-modal__overlay" onClick={ closeModal }>
			<div className={ arrayToClassName( [ 'eps-modal', props.className ] ) } ref={ modalRef } >
				<Grid container className="eps-modal__header" justify="space-between" alignItems="center">
					<Grid item>
						<Icon className={ `eps-modal__icon ${ props.icon }` } />
						<Text className="title" tag="span">{ props.title }</Text>
					</Grid>
					<Grid item>
						<div className="eps-modal__close-wrapper" ref={ closeRef }>
							<Button text={ __( 'Close', 'elementor' ) } hideText icon="eicon-close" onClick={ props.closeModal } />
						</div>
					</Grid>
				</Grid>
				<div className="eps-modal__body">
					{ props.children }
				</div>
			</div>
		</div>
	);
};

Modal.propTypes = {
	className: PropTypes.string,
	children: PropTypes.any.isRequired,
	title: PropTypes.string.isRequired,
	icon: PropTypes.string,
	show: PropTypes.bool,
	setShow: PropTypes.func,
	hideModal: PropTypes.func,
	showModal: PropTypes.func,
	closeModal: PropTypes.func,
	onOpen: PropTypes.func,
	onClose: PropTypes.func,
};

Modal.defaultProps = {
	className: '',
};