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:
@@ -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;
|
||||||
|
Reference in New Issue
Block a user