1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-08-02 19:37:29 +02:00

turn modal into fn component

This commit is contained in:
Kushagra Gour
2021-04-01 14:27:59 +05:30
parent 9643ae6ff0
commit a274ece4c5

View File

@@ -1,39 +1,49 @@
import { h, Component } from 'preact'; import { h } from 'preact';
import Portal from './Portal'; import { createPortal, useEffect, useRef } from 'preact/compat';
export default class Modal extends Component { const Portal = ({ children, into }) => {
componentDidMount() { const container = document.querySelector(into);
this.container = document.createElement('div'); return createPortal(children, container);
this.container.id = `container-${~~(Math.random() * 1000)}`; };
document.body.append(this.container);
window.addEventListener('keydown', this.onKeyDownHandler.bind(this)); const Modal = ({
} show,
componentWillUnmount() { extraClasses,
this.container.remove(); small,
window.removeEventListener('keydown', this.onKeyDownHandler.bind(this)); hideCloseButton,
if (this.focusGrabber) { closeHandler,
this.focusGrabber.remove(); noOverlay,
this.focusGrabber = null; children
} }) => {
} const focusGrabberRef = useRef();
onKeyDownHandler(e) { const overlayRef = useRef();
const onKeyDownHandler = e => {
if (e.keyCode === 27) { if (e.keyCode === 27) {
this.props.closeHandler(); closeHandler();
} }
};
const onOverlayClick = e => {
if (e.target === overlayRef.current) {
closeHandler();
} }
onOverlayClick(e) { };
if (e.target === this.overlayEl) { useEffect(() => {
this.props.closeHandler(); window.addEventListener('keydown', onKeyDownHandler);
return () => {
window.removeEventListener('keydown', this.onKeyDownHandler.bind(this));
if (focusGrabberRef.current) {
focusGrabberRef.current.remove();
focusGrabberRef.current = null;
} }
};
}, []);
useEffect(() => {
if (!noOverlay) {
document.body.classList[show ? 'add' : 'remove']('overlay-visible');
} }
componentDidUpdate(prevProps) { if (show) {
if (this.props.show !== prevProps.show) {
if (!this.props.noOverlay) {
document.body.classList[this.props.show ? 'add' : 'remove'](
'overlay-visible'
);
}
if (this.props.show) {
// HACK: refs will evaluate on next tick due to portals // HACK: refs will evaluate on next tick due to portals
setTimeout(() => { setTimeout(() => {
const closeButton = this.overlayEl.querySelector( const closeButton = this.overlayEl.querySelector(
@@ -45,40 +55,42 @@ export default class Modal extends Component {
}, 0); }, 0);
/* We insert a dummy hidden input which will take focus as soon as focus /* We insert a dummy hidden input which will take focus as soon as focus
escapes the modal, instead of focus going outside modal because modal * escapes the modal, instead of focus going outside modal because modal
is last focusable element. */ * is last focusable element.
this.focusGrabber = document.createElement('input'); */
this.focusGrabber.setAttribute( focusGrabberRef.current = document.createElement('input');
focusGrabberRef.current.setAttribute(
'style', 'style',
'height:0;opacity:0;overflow:hidden;width:0;' 'height:0;opacity:0;overflow:hidden;width:0;'
); );
setTimeout(() => { setTimeout(() => {
document.body.appendChild(this.focusGrabber); document.body.appendChild(focusGrabberRef.current);
}, 10); }, 10);
} else { } else {
this.focusGrabber.remove(); if (focusGrabberRef.current) {
this.focusGrabber = null; focusGrabberRef.current.remove();
focusGrabberRef.current = null;
} }
} }
} }, [show]);
render() {
if (!this.props.show) return null; if (!show) return null;
return ( return (
<Portal into={`#${this.container.id}`}> <Portal into={`body`}>
<div <div
role="dialog" role="dialog"
class={`${this.props.extraClasses || ''} modal is-modal-visible ${ class={`${extraClasses || ''} modal is-modal-visible ${
this.props.small ? 'modal--small' : '' small ? 'modal--small' : ''
}`} }`}
ref={el => (this.overlayEl = el)} ref={overlayRef}
onClick={this.onOverlayClick.bind(this)} onClick={onOverlayClick}
> >
<div class="modal__content"> <div class="modal__content">
{this.props.hideCloseButton ? null : ( {hideCloseButton ? null : (
<button <button
type="button" type="button"
onClick={this.props.closeHandler} onClick={closeHandler}
aria-label="Close modal" aria-label="Close modal"
title="Close" title="Close"
class="js-modal__close-btn modal__close-btn" class="js-modal__close-btn modal__close-btn"
@@ -86,10 +98,11 @@ export default class Modal extends Component {
Close Close
</button> </button>
)} )}
{this.props.children} {children}
</div> </div>
</div> </div>
</Portal> </Portal>
); );
} };
}
export default Modal;