UNPKG

@dr.pogodin/react-utils

Version:

Collection of generic ReactJS components and utils

19 lines 3.44 kB
import{useEffect,useMemo,useRef}from"react";import ReactDom from"react-dom";import themed from"@dr.pogodin/react-themes";const baseTheme={"context":"sCYXfW","ad":"e0BH-f","hoc":"vqUuSP","overlay":"uAH4as","container":"zqpc6q"};const S={"scrollingDisabledByModal":"yfvOIZ"};import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";/** * The `<Modal>` component implements a simple themeable modal window, wrapped * into the default theme. `<BaseModal>` exposes the base non-themed component. * **Children:** Component children are rendered as the modal content. * @param {object} props Component properties. Beside props documented below, * [Other theming properties](https://www.npmjs.com/package/@dr.pogodin/react-themes#themed-component-properties) are supported as well. * @param {function} [props.onCancel] The callback to trigger when user * clicks outside the modal, or presses Escape. It is expected to hide the * modal. * @param {ModalTheme} [props.theme] _Ad hoc_ theme. */const BaseModal=({cancelOnScrolling,children,containerStyle,dontDisableScrolling,onCancel,overlayStyle,style,testId,testIdForOverlay,theme})=>{const containerRef=useRef(null);const overlayRef=useRef(null);// Sets up modal cancellation of scrolling, if opted-in. useEffect(()=>{if(cancelOnScrolling&&onCancel){window.addEventListener("scroll",onCancel);window.addEventListener("wheel",onCancel)}return()=>{if(cancelOnScrolling&&onCancel){window.removeEventListener("scroll",onCancel);window.removeEventListener("wheel",onCancel)}}},[cancelOnScrolling,onCancel]);// Disables window scrolling, if it is not opted-out. useEffect(()=>{if(!dontDisableScrolling){document.body.classList.add(S.scrollingDisabledByModal)}return()=>{if(!dontDisableScrolling){document.body.classList.remove(S.scrollingDisabledByModal)}}},[dontDisableScrolling]);const focusLast=useMemo(()=>/*#__PURE__*/_jsx("div",{onFocus:()=>{const elems=containerRef.current.querySelectorAll("*");for(let i=elems.length-1;i>=0;--i){elems[i].focus();if(document.activeElement===elems[i])return}overlayRef.current?.focus()}// TODO: Have a look at this later. // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex ,tabIndex:0}),[]);return/*#__PURE__*/ReactDom.createPortal(/*#__PURE__*/_jsxs("div",{children:[focusLast,/*#__PURE__*/_jsx("div",{"aria-label":"Cancel",className:theme.overlay,"data-testid":process.env.NODE_ENV==="production"?undefined:testIdForOverlay,onClick:e=>{if(onCancel){onCancel();e.stopPropagation()}},onKeyDown:e=>{if(e.key==="Escape"&&onCancel){onCancel();e.stopPropagation()}},ref:node=>{if(node&&node!==overlayRef.current){overlayRef.current=node;node.focus()}},role:"button",style:overlayStyle,tabIndex:0}),/*#__PURE__*/_jsx("div",{// eslint-disable-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions "aria-modal":"true",className:theme.container,"data-testid":process.env.NODE_ENV==="production"?undefined:testId,onClick:e=>{e.stopPropagation()},onWheel:event=>{event.stopPropagation()},ref:containerRef,role:"dialog",style:style??containerStyle,children:children}),/*#__PURE__*/_jsx("div",{onFocus:()=>{overlayRef.current?.focus()}// TODO: Have a look at this later. // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex ,tabIndex:0}),focusLast]}),document.body)};export default themed(BaseModal,"Modal",baseTheme);/* Non-themed version of the Modal. */export{BaseModal}; //# sourceMappingURL=index.js.map