UNPKG

@trail-ui/react

Version:
230 lines (228 loc) 8.18 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 getContainingBlockOffset = () => { const probe = document.createElement("div"); probe.style.cssText = "position:fixed;top:0;left:0;width:1px;height:1px;pointer-events:none;visibility:hidden;"; document.body.appendChild(probe); const rect = probe.getBoundingClientRect(); document.body.removeChild(probe); return { top: rect.top, left: rect.left }; }; const getMaxHeight = () => { var _a2, _b2, _c2; const refEl = props.referenceElement.current; if (!refEl) { return; } const refRect = refEl.getBoundingClientRect(); let maxAvailableHeight = 0; const windowHeight = window.innerHeight; const offsetY = ((_a2 = props.offset) == null ? void 0 : _a2.y) || 0; switch (props.position) { case Positions.bottom: case Positions.bottomLeft: case Positions.bottomRight: maxAvailableHeight = windowHeight - refRect.bottom - offsetY * 2; break; case Positions.top: case Positions.topLeft: case Positions.topRight: maxAvailableHeight = refRect.top - offsetY * 2; break; default: maxAvailableHeight = Math.min( windowHeight - offsetY * 2, ((_b2 = props.style) == null ? void 0 : _b2.maxHeight) || Number.POSITIVE_INFINITY ); } if ((_c2 = props.style) == null ? void 0 : _c2.maxHeight) { maxAvailableHeight = Math.min(maxAvailableHeight, props.style.maxHeight); } return maxAvailableHeight; }; const getMaxWidth = () => { var _a2, _b2, _c2; const refEl = props.referenceElement.current; if (!refEl) { return; } const refRect = refEl.getBoundingClientRect(); let maxAvailableWidth = 0; const windowWidth = window.innerWidth; const offsetX = ((_a2 = props.offset) == null ? void 0 : _a2.x) || 0; switch (props.position) { case Positions.right: case Positions.rightBottom: case Positions.rightTop: maxAvailableWidth = windowWidth - refRect.right - offsetX * 2; break; case Positions.left: case Positions.leftBottom: case Positions.leftTop: maxAvailableWidth = refRect.left - offsetX * 2; break; default: maxAvailableWidth = Math.min( windowWidth - offsetX * 2, ((_b2 = props.style) == null ? void 0 : _b2.maxWidth) || Number.POSITIVE_INFINITY ); } if ((_c2 = props.style) == null ? void 0 : _c2.maxWidth) { maxAvailableWidth = Math.min(maxAvailableWidth, props.style.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(); const cbOffset = getContainingBlockOffset(); 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) - cbOffset.top; left = refRect.left + refRect.width / 2 - cbOffset.left; transform = "translate(-50%, -100%)"; break; case Positions.topLeft: top = refRect.top - (((_b2 = props.offset) == null ? void 0 : _b2.y) || 0) - cbOffset.top; left = refRect.left - cbOffset.left; transform = "translateY(-100%)"; break; case Positions.topRight: top = refRect.top - (((_c2 = props.offset) == null ? void 0 : _c2.y) || 0) - cbOffset.top; left = refRect.right - cbOffset.left; transform = "translate(-100%, -100%)"; break; case Positions.bottom: top = refRect.bottom + (((_d = props.offset) == null ? void 0 : _d.y) || 0) - cbOffset.top; left = refRect.left + refRect.width / 2 - cbOffset.left; transform = "translateX(-50%)"; break; case Positions.bottomLeft: top = refRect.bottom + (((_e = props.offset) == null ? void 0 : _e.y) || 0) - cbOffset.top; left = refRect.left - cbOffset.left; break; case Positions.bottomRight: top = refRect.bottom + (((_f = props.offset) == null ? void 0 : _f.y) || 0) - cbOffset.top; left = refRect.right - cbOffset.left; transform = "translateX(-100%)"; break; case Positions.left: top = refRect.top + refRect.height / 2 - cbOffset.top; left = refRect.left - (((_g = props.offset) == null ? void 0 : _g.x) || 0) - cbOffset.left; transform = "translate(-100%, -50%)"; break; case Positions.leftTop: top = refRect.top - cbOffset.top; left = refRect.left - (((_h = props.offset) == null ? void 0 : _h.x) || 0) - cbOffset.left; transform = "translateX(-100%)"; break; case Positions.leftBottom: top = refRect.bottom - cbOffset.top; left = refRect.left - (((_i = props.offset) == null ? void 0 : _i.x) || 0) - cbOffset.left; transform = "translate(-100%, -100%)"; break; case Positions.right: top = refRect.top + refRect.height / 2 - cbOffset.top; left = refRect.right + (((_j = props.offset) == null ? void 0 : _j.x) || 0) - cbOffset.left; transform = "translateY(-50%)"; break; case Positions.rightTop: top = refRect.top - cbOffset.top; left = refRect.right + (((_k = props.offset) == null ? void 0 : _k.x) || 0) - cbOffset.left; break; case Positions.rightBottom: top = refRect.bottom - cbOffset.top; left = refRect.right + (((_l = props.offset) == null ? void 0 : _l.x) || 0) - cbOffset.left; 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, props.isOpen]); 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 }, children: React.cloneElement(props.children, { ref: elementRef }) } ) ] }); } return props.children; } export { Positions, Overlay };