@create-figma-plugin/ui
Version:
Production-grade Preact components that replicate the Figma UI design
131 lines • 5.97 kB
JavaScript
import { h, render } from 'preact';
import { useEffect, useRef } from 'preact/hooks';
import { IconClose24 } from '../../icons/icon-24/icon-close-24.js';
import { createClassName } from '../../utilities/create-class-name.js';
import { createComponent } from '../../utilities/create-component.js';
import { getCurrentFromRef } from '../../utilities/get-current-from-ref.js';
import { createFocusTrapKeyDownHandler } from '../../utilities/private/create-focus-trap-key-down-handler.js';
import { getFocusableElements } from '../../utilities/private/get-focusable-elements.js';
import { IconButton } from '../icon-button/icon-button.js';
import { Text } from '../text/text.js';
import styles from './modal.module.css';
export const Modal = createComponent(function ({ children, closeButtonIcon = h(IconClose24, null), closeButtonPosition = 'right', open, transition = true, onCloseButtonClick, onEscapeKeyDown, onOverlayClick, position = 'center', title, ...rest }, ref) {
const portalElementRef = useRef(null);
const modalElementsRef = useRef([]);
const previousFocusedElementRef = useRef(null);
useEffect(function () {
const portalElement = document.createElement('div');
document.body.appendChild(portalElement);
portalElementRef.current = portalElement;
return function () {
document.body.removeChild(portalElement);
};
}, []);
useEffect(function () {
const portalElement = getCurrentFromRef(portalElementRef);
const focusTrapKeyDownHandler = createFocusTrapKeyDownHandler(portalElement);
function handleTabKeyDown(event) {
if (open === true) {
focusTrapKeyDownHandler(event);
}
}
window.addEventListener('keydown', handleTabKeyDown);
return function () {
window.removeEventListener('keydown', handleTabKeyDown);
};
}, [open]);
useEffect(function () {
function handleEscapeKeyDown(event) {
const modalElements = getCurrentFromRef(modalElementsRef);
const portalElement = getCurrentFromRef(portalElementRef);
if (open === false ||
event.key !== 'Escape' ||
typeof onEscapeKeyDown === 'undefined' ||
modalElements[modalElements.length - 1] !== portalElement) {
return;
}
onEscapeKeyDown(event);
}
window.addEventListener('keydown', handleEscapeKeyDown);
return function () {
window.removeEventListener('keydown', handleEscapeKeyDown);
};
}, [open, onEscapeKeyDown]);
useEffect(function () {
const modalElements = getCurrentFromRef(modalElementsRef);
const portalElement = getCurrentFromRef(portalElementRef);
const bodyElement = document.body;
if (open === true) {
if (modalElements.length === 0) {
const scrollY = window.scrollY;
bodyElement.style.cssText += `position:fixed;top:-${scrollY}px;width:100%;overflow-y:hidden;`;
}
modalElements.push(portalElement);
portalElement.style.cssText =
'position:absolute;top:0;right:0;bottom:0;left:0;z-index:1';
previousFocusedElementRef.current =
document.activeElement;
const focusableElements = getFocusableElements(portalElement);
if (focusableElements.length > 0) {
focusableElements[0].focus();
}
else {
previousFocusedElementRef.current.blur();
}
}
else {
if (modalElements.length === 1) {
const top = parseInt(document.body.style.top);
bodyElement.style.removeProperty('position');
bodyElement.style.removeProperty('top');
bodyElement.style.removeProperty('overflow-y');
bodyElement.style.removeProperty('width');
window.scrollTo({ top: top * -1 });
}
modalElements.pop();
portalElement.style.cssText = 'position:static';
}
return function () {
if (previousFocusedElementRef.current !== null) {
previousFocusedElementRef.current.focus();
}
};
}, [open]);
useEffect(function () {
const portalElement = getCurrentFromRef(portalElementRef);
render(h("div", { ref: ref },
h("div", { ...rest, class: createClassName([
styles.modal,
open === true ? styles.open : null,
transition === false ? styles.noTransition : null,
styles[position]
]) },
children,
typeof onCloseButtonClick === 'undefined' &&
typeof title === 'undefined' ? null : (h("div", { class: styles.topBar },
h("div", { class: styles.title }, typeof title === 'undefined' ? null : (h(Text, null,
h("strong", null, title)))),
typeof onCloseButtonClick === 'undefined' ? null : (h("div", { class: createClassName([
styles.closeButton,
closeButtonPosition === 'left'
? styles.closeButtonLeft
: null
]) },
h(IconButton, { onClick: onCloseButtonClick }, closeButtonIcon)))))),
h("div", { class: styles.overlay, onClick: typeof onOverlayClick === 'undefined' ? undefined : onOverlayClick })), portalElement);
}, [
children,
closeButtonIcon,
closeButtonPosition,
onCloseButtonClick,
onOverlayClick,
open,
position,
ref,
rest,
title,
transition
]);
return null;
});
//# sourceMappingURL=modal.js.map