UNPKG

@kiwicom/orbit-components

Version:

Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com's products.

151 lines (150 loc) 6.33 kB
"use client"; import * as React from "react"; import cx from "clsx"; import { useFloating, offset, flip, shift, autoUpdate, autoPlacement, FloatingFocusManager } from "@floating-ui/react"; import Button from "../../Button"; import useMediaQuery from "../../hooks/useMediaQuery"; import useClickOutside from "../../hooks/useClickOutside"; import useLockScrolling from "../../hooks/useLockScrolling"; import { ModalContext } from "../../Modal/ModalContext"; import boundingClientRect from "../../utils/boundingClientRect"; import { getAutoAlignment, isFixedPlacement, PLACEMENTS } from "../../common/placements"; const PopoverContentWrapper = ({ children, onClose, zIndex = 710, labelClose, width, maxHeight, noFlip, offset: offsetProp = { top: 4, left: 0 }, referenceElement, dataTest, id, placement = PLACEMENTS.BOTTOM_START, noPadding, overlapped, shown, fixed, allowOverflow, lockScrolling = true, actions, ariaLabelledby, ariaLabel, role, overlayId }) => { const [actionsHeight, setActionsHeight] = React.useState(null); const { isInsideModal } = React.useContext(ModalContext); const { isLargeMobile } = useMediaQuery(); const actionsRef = React.useRef(null); const content = React.useRef(null); const scrollingElementRef = React.useRef(null); useLockScrolling(scrollingElementRef, lockScrolling && !isLargeMobile); const windowHeight = typeof window !== "undefined" ? window.innerHeight : 0; const isAutoPlacement = !isFixedPlacement(placement); const { refs, floatingStyles, context, elements } = useFloating({ placement: isAutoPlacement ? undefined : placement, strategy: fixed ? "fixed" : "absolute", whileElementsMounted: autoUpdate, elements: { reference: referenceElement }, middleware: [offset({ mainAxis: overlapped ? -Number(referenceElement?.offsetHeight) : offsetProp.top, crossAxis: offsetProp.left }), isAutoPlacement && autoPlacement({ alignment: getAutoAlignment(placement), autoAlignment: !noFlip }), !noFlip && !isAutoPlacement && flip(), !allowOverflow && shift()] }); React.useEffect(() => { const timer = setTimeout(() => { if (elements.floating) { elements.floating.focus(); } }, 100); if (actionsRef.current) { const { height } = boundingClientRect({ current: actionsRef.current }); setActionsHeight(height); } const handleKeyDown = ev => { if (ev.code === "Escape" && onClose) { onClose(); } }; document.addEventListener("keydown", handleKeyDown); return () => { clearTimeout(timer); document.removeEventListener("keydown", handleKeyDown); }; }, [actions, setActionsHeight, onClose, elements.floating]); useClickOutside(content, ev => { if (isLargeMobile) onClose(ev); }, "click"); const cssVars = { "--popover-top": floatingStyles.top ?? 0, "--popover-left": floatingStyles.left ?? 0, "--popover-right": floatingStyles.right ?? "auto", "--popover-bottom": floatingStyles.bottom ?? "auto", "--popover-transform": floatingStyles.transform, "--popover-position": floatingStyles.position, "--popover-zIndex": zIndex, "--popover-width": width }; return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { role: "presentation", id: overlayId, className: cx("block", "fixed", "inset-x-0 top-0", "size-full", "bg-ink-dark/60", "duration-normal transition-[opacity,transform] ease-in-out", "z-[999]", "lm:hidden", shown ? "opacity-100" : "opacity-0"), onClick: onClose }), /*#__PURE__*/React.createElement(FloatingFocusManager, { context: context }, /*#__PURE__*/React.createElement("div", { role: role, ref: refs.setFloating, "data-test": dataTest, id: id, className: cx("fixed", "inset-x-0 bottom-0", "h-auto w-full", "z-[1000]", "box-border", "shadow-level3-reverse", "bg-white-normal", "max-h-[calc(100%_-_theme(spacing.800))]", "focus:outline-none", "lm:top-[var(--popover-top)]", "lm:left-[var(--popover-left)]", "lm:right-[var(--popover-right)]", "lm:bottom-[var(--popover-bottom)]", "lm:[position:var(--popover-position)]", "lm:[transform:var(--popover-transform)]", "lm:transition-opacity lm:duration-fast lm:ease-in-out", "lm:rounded-400", "lm:shadow-level3", "lm:max-h-none", isInsideModal ? "lm:z-[1000]" : "lm:z-[var(--popover-zIndex)]", width ? "lm:w-[var(--popover-width)]" : "lm:w-auto", shown ? "lm:opacity-100" : "lm:opacity-0"), style: cssVars, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby }, /*#__PURE__*/React.createElement("div", { ref: content, className: cx(shown ? "translate-y-0" : "translate-y-full", "will-change-transform", "duration-fast transition-[opacity,transform] ease-in-out", "lm:transform-none", "lm:transition-none") }, /*#__PURE__*/React.createElement("div", { ref: scrollingElementRef, className: cx("overflow-auto", "rounded-t-modal", "absolute left-0", "w-full", "bg-white-normal", "bottom-[var(--actions-height)]", windowHeight && actionsHeight && "max-h-[calc(var(--window-height)-var(--actions-height)-32px)]", noPadding ? "p-0" : "p-400", "lm:max-h-[var(--max-height)]", "lm:rounded-400", "lm:bottom-auto", "lm:left-auto", "lm:relative"), style: { "--actions-height": actionsHeight != null && `${actionsHeight}px`, "--window-height": windowHeight != null && `${windowHeight}px`, "--max-height": maxHeight != null ? `${maxHeight}px` : "100%" } }, children), actions ? /*#__PURE__*/React.createElement("div", { ref: actionsRef, className: cx("fixed", "bottom-0 left-0", "w-full", "box-border", "p-400 pt-300", "bg-white-normal", "[&_.orbit-button-primitive]:w-full [&_.orbit-button-primitive]:flex-auto", "lm:relative", "lm:bottom-auto lm:left-auto", "lm:rounded-b-400", "lm:[&_.orbit-button-primitive]:w-auto lm:[&_.orbit-button-primitive]:grow-0") }, actions) : /*#__PURE__*/React.createElement("div", { ref: actionsRef, className: "p-400 lm:hidden lm:pb-0" }, /*#__PURE__*/React.createElement(Button, { type: "secondary", fullWidth: true, onClick: onClose }, labelClose)))))); }; export default PopoverContentWrapper;