@penaprieto/design-system
Version:
Multi-brand React design system with design tokens from Figma
119 lines (118 loc) • 5.14 kB
JavaScript
;
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;