UNPKG

@penaprieto/design-system

Version:

Multi-brand React design system with design tokens from Figma

119 lines (118 loc) 5.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Popover = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const react_dom_1 = require("react-dom"); require("./Popover.css"); const Popover = ({ content, children, placement = 'bottom', trigger = 'click', open: controlledOpen, onOpenChange, className = '', }) => { const isControlled = controlledOpen !== undefined; const [internalOpen, setInternalOpen] = (0, react_1.useState)(false); const open = isControlled ? controlledOpen : internalOpen; const triggerRef = (0, react_1.useRef)(null); const popoverRef = (0, react_1.useRef)(null); const [position, setPosition] = (0, react_1.useState)({ top: 0, left: 0 }); const setOpen = (value) => { if (!isControlled) { setInternalOpen(value); } onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(value); }; const calculatePosition = () => { if (!triggerRef.current || !popoverRef.current) return; const triggerRect = triggerRef.current.getBoundingClientRect(); const popoverRect = popoverRef.current.getBoundingClientRect(); const gap = 8; let top = 0; let left = 0; switch (placement) { case 'top': top = triggerRect.top - popoverRect.height - gap; left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2; break; case 'bottom': top = triggerRect.bottom + gap; left = triggerRect.left + triggerRect.width / 2 - popoverRect.width / 2; break; case 'left': top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2; left = triggerRect.left - popoverRect.width - gap; break; case 'right': top = triggerRect.top + triggerRect.height / 2 - popoverRect.height / 2; left = triggerRect.right + gap; break; } // Keep popover within viewport const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; if (left < 0) left = gap; if (left + popoverRect.width > viewportWidth) { left = viewportWidth - popoverRect.width - gap; } if (top < 0) top = gap; if (top + popoverRect.height > viewportHeight) { top = viewportHeight - popoverRect.height - gap; } setPosition({ top, left }); }; (0, react_1.useEffect)(() => { if (open) { // Small delay to ensure popover is rendered and has dimensions setTimeout(calculatePosition, 0); window.addEventListener('scroll', calculatePosition); window.addEventListener('resize', calculatePosition); } return () => { window.removeEventListener('scroll', calculatePosition); window.removeEventListener('resize', calculatePosition); }; }, [open, placement, content]); (0, react_1.useEffect)(() => { if (!open || trigger !== 'click') return; const handleClickOutside = (e) => { if (popoverRef.current && !popoverRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) { setOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, [open, trigger]); const handleTriggerClick = () => { if (trigger === 'click') { setOpen(!open); } }; const handleMouseEnter = () => { if (trigger === 'hover') { setOpen(true); } }; const handleMouseLeave = () => { if (trigger === 'hover') { setOpen(false); } }; const popoverClassName = [ 'ds-popover', `ds-popover--${placement}`, className, ] .filter(Boolean) .join(' '); return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { ref: triggerRef, className: "ds-popover-trigger", onClick: handleTriggerClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: children }), open && (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { ref: popoverRef, className: popoverClassName, style: { position: 'fixed', top: `${position.top}px`, left: `${position.left}px`, visibility: position.top === 0 && position.left === 0 ? 'hidden' : 'visible', }, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: (0, jsx_runtime_1.jsx)("div", { className: "ds-popover__content", children: content }) }), document.body)] })); }; exports.Popover = Popover;