@penaprieto/design-system
Version:
Multi-brand React design system with design tokens from Figma
122 lines (121 loc) • 5.63 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Menu = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importStar(require("react"));
const react_dom_1 = require("react-dom");
require("./Menu.css");
const Icon_1 = require("../Icon");
const Menu = ({ items, children, 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 menuRef = (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)
return;
const triggerRect = triggerRef.current.getBoundingClientRect();
const menuWidth = 200;
const gap = 4;
let top = triggerRect.bottom + gap;
let left = triggerRect.left;
// Adjust if menu goes off screen
if (left + menuWidth > window.innerWidth) {
left = triggerRect.right - menuWidth;
}
setPosition({ top, left });
};
(0, react_1.useEffect)(() => {
if (open) {
calculatePosition();
window.addEventListener('scroll', calculatePosition);
window.addEventListener('resize', calculatePosition);
}
return () => {
window.removeEventListener('scroll', calculatePosition);
window.removeEventListener('resize', calculatePosition);
};
}, [open]);
(0, react_1.useEffect)(() => {
if (!open)
return;
const handleClickOutside = (e) => {
if (menuRef.current &&
!menuRef.current.contains(e.target) &&
triggerRef.current &&
!triggerRef.current.contains(e.target)) {
setOpen(false);
}
};
const handleEscape = (e) => {
if (e.key === 'Escape') {
setOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('keydown', handleEscape);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleEscape);
};
}, [open]);
const handleTriggerClick = () => {
setOpen(!open);
};
const handleItemClick = (item) => {
var _a;
if (item.disabled)
return;
(_a = item.onClick) === null || _a === void 0 ? void 0 : _a.call(item);
setOpen(false);
};
const menuClassName = ['ds-menu', 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-menu-trigger", onClick: handleTriggerClick, children: children }), open &&
(0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { ref: menuRef, className: menuClassName, style: {
position: 'fixed',
top: `${position.top}px`,
left: `${position.left}px`,
}, role: "menu", children: items.map((item) => ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: `ds-menu__item ${item.disabled ? 'ds-menu__item--disabled' : ''}`, onClick: () => handleItemClick(item), role: "menuitem", "aria-disabled": item.disabled, children: [item.icon && ((0, jsx_runtime_1.jsx)("span", { className: "ds-menu__icon", children: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { name: item.icon, size: 20 }) })), (0, jsx_runtime_1.jsx)("span", { className: "ds-menu__label", children: item.label })] }), item.divider && (0, jsx_runtime_1.jsx)("div", { className: "ds-menu__divider" })] }, item.id))) }), document.body)] }));
};
exports.Menu = Menu;