71 lines
1.9 KiB
TypeScript
71 lines
1.9 KiB
TypeScript
import { useState, useEffect, useRef } from 'react';
|
|
import { ModalSetter } from '../../../../lib/types/modal.interface';
|
|
import ModalService from '../services/ModalService';
|
|
import styles from './ModalRoot.module.scss';
|
|
|
|
export default function ModalRoot() {
|
|
const [modal, setModal] = useState<ModalSetter<Object> | null>(null);
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
ModalService.on('open', ({ component, props, target, resolve }) => {
|
|
setModal({
|
|
component,
|
|
props,
|
|
resolve,
|
|
close: (...args) => {
|
|
setModal(null);
|
|
resolve?.call(null, ...args);
|
|
target?.focus();
|
|
},
|
|
});
|
|
});
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!modal) {
|
|
return;
|
|
}
|
|
|
|
function handleEscapeKey(e: KeyboardEvent) {
|
|
if (e.key === 'Escape') {
|
|
modal?.close?.call(null, false);
|
|
}
|
|
}
|
|
|
|
// Off click listener
|
|
function handleClickOutside(event: MouseEvent | TouchEvent) {
|
|
if (
|
|
ref.current &&
|
|
!(ref.current as unknown as HTMLElement).contains(
|
|
event.target as HTMLElement
|
|
)
|
|
) {
|
|
modal?.close?.call(null, false);
|
|
}
|
|
}
|
|
|
|
window.addEventListener('keyup', handleEscapeKey);
|
|
document.body.style.overflow = 'hidden';
|
|
document.addEventListener('mousedown', handleClickOutside);
|
|
document.addEventListener('touchstart', handleClickOutside);
|
|
|
|
return () => {
|
|
window.removeEventListener('keyup', handleEscapeKey);
|
|
document.body.style.overflow = '';
|
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
document.removeEventListener('touchstart', handleClickOutside);
|
|
};
|
|
}, [modal]);
|
|
|
|
const ModalComponent = modal?.component ? modal.component : null;
|
|
|
|
return (
|
|
<section className={modal?.component ? styles.modalRoot : ''}>
|
|
{ModalComponent && (
|
|
<ModalComponent {...modal?.props} modalRef={ref} close={modal!.close} />
|
|
)}
|
|
</section>
|
|
);
|
|
}
|