UNPKG

@trail-ui/react

Version:
224 lines (222 loc) 8.04 kB
// src/overlay/overlay.tsx import React from "react"; import { isValidElement, useLayoutEffect, useRef, useState } from "react"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var Positions = { top: "top", topLeft: "topLeft", topRight: "topRight", bottom: "bottom", bottomLeft: "bottomLeft", bottomRight: "bottomRight", left: "left", leftTop: "leftTop", leftBottom: "leftBottom", right: "right", rightTop: "rightTop", rightBottom: "rightBottom" }; function Overlay(props) { var _a, _b, _c; const [style, setStyle] = useState({ top: 0, left: 0, transform: "", position: "fixed" }); const elementRef = useRef(null); const getMaxHeight = () => { var _a2, _b2, _c2, _d, _e, _f, _g, _h; const refEl = props.referenceElement.current; if (!refEl) { return; } const refRect = refEl.getBoundingClientRect(); let maxAvailableHeight = 0; const windowHeight = window.innerHeight; switch (props.position) { case Positions.bottom: case Positions.bottomLeft: case Positions.bottomRight: maxAvailableHeight = windowHeight - refRect.bottom - (((_a2 = props.offset) == null ? void 0 : _a2.y) || 0) * 2; break; case Positions.top: case Positions.topLeft: case Positions.topRight: maxAvailableHeight = refRect.top - (((_b2 = props.offset) == null ? void 0 : _b2.y) || 0) * 2; break; default: maxAvailableHeight = Math.min( windowHeight - (((_c2 = props.offset) == null ? void 0 : _c2.y) || 0) * 2, ((_d = props.style) == null ? void 0 : _d.maxHeight) || Number.POSITIVE_INFINITY, ((_e = props.style) == null ? void 0 : _e.height) || Number.POSITIVE_INFINITY ); } if ((_f = props.style) == null ? void 0 : _f.maxHeight) { maxAvailableHeight = maxAvailableHeight < ((_g = props.style) == null ? void 0 : _g.maxHeight) ? maxAvailableHeight : (_h = props.style) == null ? void 0 : _h.maxHeight; } return maxAvailableHeight; }; const getMaxWidth = () => { var _a2, _b2, _c2, _d, _e, _f, _g, _h; const refEl = props.referenceElement.current; const currentEl = elementRef.current; if (!refEl || !currentEl) { return; } const refRect = refEl.getBoundingClientRect(); let maxAvailableWidth = 0; const windowWidth = window.innerWidth; switch (props.position) { case Positions.right: case Positions.rightBottom: case Positions.rightTop: maxAvailableWidth = windowWidth - refRect.right - (((_a2 = props.offset) == null ? void 0 : _a2.x) || 0) * 2; break; case Positions.left: case Positions.leftBottom: case Positions.leftTop: maxAvailableWidth = refRect.left - (((_b2 = props.offset) == null ? void 0 : _b2.x) || 0) * 2; break; default: maxAvailableWidth = Math.min( windowWidth - (((_c2 = props.offset) == null ? void 0 : _c2.x) || 0) * 2, ((_d = props.style) == null ? void 0 : _d.width) || Number.POSITIVE_INFINITY, ((_e = props.style) == null ? void 0 : _e.maxWidth) || Number.POSITIVE_INFINITY ); } if ((_f = props.style) == null ? void 0 : _f.maxWidth) { maxAvailableWidth = maxAvailableWidth < ((_g = props.style) == null ? void 0 : _g.maxWidth) ? maxAvailableWidth : (_h = props.style) == null ? void 0 : _h.maxWidth; } return maxAvailableWidth; }; const getUpdatedPositions = () => { var _a2, _b2, _c2, _d, _e, _f, _g, _h, _i, _j, _k, _l; const refEl = props.referenceElement.current; if (!refEl) { return; } const refRect = refEl.getBoundingClientRect(); let top = 0; let left = 0; let transform = ""; switch (props.position) { case Positions.top: top = refRect.top - (((_a2 = props.offset) == null ? void 0 : _a2.y) || 0); left = refRect.left + refRect.width / 2; transform = "translate(-50%, -100%)"; break; case Positions.topLeft: top = refRect.top - (((_b2 = props.offset) == null ? void 0 : _b2.y) || 0); left = refRect.left; transform = "translateY(-100%)"; break; case Positions.topRight: top = refRect.top - (((_c2 = props.offset) == null ? void 0 : _c2.y) || 0); left = refRect.right; transform = "translate(-100%, -100%)"; break; case Positions.bottom: top = refRect.bottom + (((_d = props.offset) == null ? void 0 : _d.y) || 0); left = refRect.left + refRect.width / 2; transform = "translateX(-50%)"; break; case Positions.bottomLeft: top = refRect.bottom + (((_e = props.offset) == null ? void 0 : _e.y) || 0); left = refRect.left; break; case Positions.bottomRight: top = refRect.bottom + (((_f = props.offset) == null ? void 0 : _f.y) || 0); left = refRect.right; transform = "translateX(-100%)"; break; case Positions.left: top = refRect.top + refRect.height / 2; left = refRect.left - (((_g = props.offset) == null ? void 0 : _g.x) || 0); transform = "translate(-100%,-50%)"; break; case Positions.leftTop: top = refRect.top; left = refRect.left - (((_h = props.offset) == null ? void 0 : _h.x) || 0); transform = "translateX(-100%)"; break; case Positions.leftBottom: top = refRect.bottom; left = refRect.left - (((_i = props.offset) == null ? void 0 : _i.x) || 0); transform = "translate(-100%, -100%)"; break; case Positions.right: top = refRect.top + refRect.height / 2; left = refRect.right + (((_j = props.offset) == null ? void 0 : _j.x) || 0); transform = "translateY(-50%)"; break; case Positions.rightTop: top = refRect.top; left = refRect.right + (((_k = props.offset) == null ? void 0 : _k.x) || 0); break; case Positions.rightBottom: top = refRect.bottom; left = refRect.right + (((_l = props.offset) == null ? void 0 : _l.x) || 0); transform = "translateY(-100%)"; break; } const maxHeight = getMaxHeight(); const maxWidth = getMaxWidth(); setStyle((prevState) => ({ ...prevState, top, left, transform, ...maxHeight && { maxHeight }, ...maxWidth && { maxWidth } })); }; useLayoutEffect(() => { if (!props.referenceElement.current) { return; } getUpdatedPositions(); const updatePositions = () => getUpdatedPositions(); window.addEventListener("resize", updatePositions); window.addEventListener("scroll", updatePositions, true); return () => { window.removeEventListener("resize", updatePositions); window.removeEventListener("scroll", updatePositions, true); }; }, [props.referenceElement.current]); if (isValidElement(props.children)) { return /* @__PURE__ */ jsxs(Fragment, { children: [ props.onOverlayClick && /* @__PURE__ */ jsx( "div", { role: "button", className: "z-2 fixed left-[0] top-[0] h-[100vh] w-[100vw]", onClick: props.onOverlayClick, onKeyDown: props.onOverlayClick, "aria-label": "Close dropdown", tabIndex: -1 } ), /* @__PURE__ */ jsx( "div", { style: { ...style, overflow: "auto", width: ((_a = props.style) == null ? void 0 : _a.width) || ((_b = props.referenceElement.current) == null ? void 0 : _b.getBoundingClientRect().width), height: ((_c = props.style) == null ? void 0 : _c.height) || "fit-content", zIndex: 30 }, className: "rounded-md border-[1px] bg-neutral-50 shadow-md", children: React.cloneElement(props.children, { ref: elementRef }) } ) ] }); } else { return props.children; } } export { Positions, Overlay };