UNPKG

@vectara/vectara-ui

Version:

Vectara's design system, codified as a React and Sass component library

88 lines (87 loc) 5.76 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useEffect, useRef, useState } from "react"; import classNames from "classnames"; import { FocusOn } from "react-focus-on"; import { BiX } from "react-icons/bi"; import { VuiFlexContainer } from "../flex/FlexContainer"; import { VuiFlexItem } from "../flex/FlexItem"; import { VuiIconButton } from "../button/IconButton"; import { VuiIcon } from "../icon/Icon"; import { VuiPortal } from "../portal/Portal"; import { VuiScreenBlock } from "../screenBlock/ScreenBlock"; import { useVuiContext } from "../context/Context"; import { getOverlayProps } from "../../utils/getOverlayProps"; const COLOR = ["primary", "danger"]; export const VuiModal = (_a) => { var { className, color = "primary", title, icon, children, isOpen, onClose, key, canClickOutsideToClose = true, size = "s" } = _a, rest = __rest(_a, ["className", "color", "title", "icon", "children", "isOpen", "onClose", "key", "canClickOutsideToClose", "size"]); const { DrawerTitle } = useVuiContext(); const returnFocusElRef = useRef(null); // Uncouple open state from visibile state so that when the popover is closed, // we can set the button to be unselected immediately while the popover content // is still transitioning out. const [showTransition, setShowTransition] = useState(false); const [isContentVisible, setIsContentVisible] = useState(false); // Return focus on unmount. useEffect(() => { var _a; if (isOpen) { returnFocusElRef.current = document.activeElement; setIsContentVisible(true); requestAnimationFrame(() => { setShowTransition(true); }); } else { (_a = returnFocusElRef.current) === null || _a === void 0 ? void 0 : _a.focus(); returnFocusElRef.current = null; setShowTransition(false); // Wait for the transition to complete before unmounting. // This duration should match the CSS transition speed. window.setTimeout(() => { setIsContentVisible(false); }, 200); } }, [isOpen]); // Allow contents to respond to blur events before unmounting. const onCloseDelayed = () => { window.setTimeout(() => { onClose === null || onClose === void 0 ? void 0 : onClose(); }, 0); }; // Handle outside clicks, but ignore clicks on notifications. const handleClickOutside = (event) => { const target = event.target; // Check if the click is within a notification. if (target.closest("[data-awareness='notification']")) { return; } onCloseDelayed(); }; const containerClasses = classNames("vuiModalContainer", { "vuiModalContainer-isLoaded": showTransition }); const classes = classNames("vuiModal", `vuiModal--${color}`, `vuiModal--${size}`, className); return (_jsx(VuiPortal, { children: (isOpen || isContentVisible || showTransition) && (_jsx(VuiScreenBlock, Object.assign({ type: "modal", isHidden: !isOpen }, { children: _jsx(FocusOn // Disable the focus guard as soon as the modal begins closing // so it doesn't intercept clicks while transitioning out. , Object.assign({ // Disable the focus guard as soon as the modal begins closing // so it doesn't intercept clicks while transitioning out. enabled: isOpen, onEscapeKey: onCloseDelayed, onClickOutside: canClickOutsideToClose ? handleClickOutside : undefined, // Enable manual focus return to work. returnFocus: false, // Enable focus on contents when it's open, // but enable manual focus return to work when it's closed. autoFocus: isOpen }, { children: _jsx("div", Object.assign({ className: containerClasses }, getOverlayProps("modalTitle"), { children: _jsxs("div", Object.assign({ className: classes }, rest, { children: [_jsx("div", Object.assign({ className: "vuiModalHeader" }, { children: _jsxs(VuiFlexContainer, Object.assign({ justifyContent: "spaceBetween", alignItems: "center" }, { children: [_jsx(VuiFlexItem, Object.assign({ grow: false }, { children: _jsxs(VuiFlexContainer, Object.assign({ alignItems: "center", spacing: "xs" }, { children: [icon && (_jsx(VuiFlexItem, Object.assign({ grow: false, shrink: false }, { children: _jsx(VuiIcon, Object.assign({ size: "l" }, { children: icon })) }))), _jsx(VuiFlexItem, Object.assign({ grow: false }, { children: _jsx("div", Object.assign({ className: "vuiModalHeader__title" }, { children: _jsx(DrawerTitle, Object.assign({ id: "modalTitle" }, { children: title })) })) }))] })) })), onClose && (_jsx(VuiFlexItem, { children: _jsx(VuiIconButton, { "aria-label": "Close", "data-testid": "modalCloseButton", onClick: onCloseDelayed, color: "neutral", icon: _jsx(VuiIcon, Object.assign({ size: "m", color: "neutral" }, { children: _jsx(BiX, {}) })) }) }))] })) })), _jsx("div", Object.assign({ className: "vuiModalContent" }, { children: _jsx("div", Object.assign({ className: "vuiModalContent__inner" }, { children: children })) }))] })) })) })) }), key)) })); };