@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
JavaScript
"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;