UNPKG

@trail-ui/react

Version:
215 lines (213 loc) 7.1 kB
// src/overlay/overlay.tsx import { cloneElement, 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) { const [style, setStyle] = useState({ top: 0, left: 0, transform: "", position: "fixed" }); const overlayRef = useRef(null); const getMaxHeight = () => { var _a, _b, _c, _d, _e; 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 - (((_a = props.offset) == null ? void 0 : _a.y) || 0) * 2; break; case Positions.top: case Positions.topLeft: case Positions.topRight: maxAvailableHeight = refRect.top - (((_b = props.offset) == null ? void 0 : _b.y) || 0) * 2; break; default: return "100%"; } if ((_c = props.style) == null ? void 0 : _c.maxHeight) { maxAvailableHeight = maxAvailableHeight < ((_d = props.style) == null ? void 0 : _d.maxHeight) ? maxAvailableHeight : (_e = props.style) == null ? void 0 : _e.maxHeight; } return maxAvailableHeight; }; const getMaxWidth = () => { var _a, _b, _c, _d, _e; const refEl = props.referenceElement.current; if (!refEl) { 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 - (((_a = props.offset) == null ? void 0 : _a.x) || 0) * 2; break; case Positions.left: case Positions.leftBottom: case Positions.leftTop: maxAvailableWidth = refRect.left - (((_b = props.offset) == null ? void 0 : _b.x) || 0) * 2; break; default: return null; } if ((_c = props.style) == null ? void 0 : _c.maxWidth) { maxAvailableWidth = maxAvailableWidth < ((_d = props.style) == null ? void 0 : _d.maxWidth) ? maxAvailableWidth : (_e = props.style) == null ? void 0 : _e.maxWidth; } return maxAvailableWidth; }; const getUpdatedPositions = () => { var _a, _b, _c, _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 - (((_a = props.offset) == null ? void 0 : _a.y) || 0); left = refRect.left + refRect.width / 2; transform = "translate(-50%, -100%)"; break; case Positions.topLeft: top = refRect.top - (((_b = props.offset) == null ? void 0 : _b.y) || 0); left = refRect.left; transform = "translateY(-100%)"; break; case Positions.topRight: top = refRect.top - (((_c = props.offset) == null ? void 0 : _c.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, width: refRect.width, ...maxHeight && { maxHeight }, ...maxWidth && { maxWidth } })); }; useLayoutEffect(() => { if (!props.referenceElement.current) { return; } getUpdatedPositions(); const updatePositions = () => getUpdatedPositions(); const observer = new ResizeObserver(updatePositions); if (props.referenceElement.current) { observer.observe(props.referenceElement.current); } if (overlayRef.current) { observer.observe(overlayRef.current); } window.addEventListener("resize", updatePositions); window.addEventListener("scroll", updatePositions, true); return () => { observer.disconnect(); 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 } ), cloneElement(props.children, { style }) ] }); } else { return props.children; } } export { Positions, Overlay };