UNPKG

@aegov/design-system-react

Version:

A design system for the Government of the United Arab Emirates. Extending the original design system released as @aegov/design-system, this is the package specefically created for the ReactJs enviornment.

380 lines (368 loc) 16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _zod = require("zod"); var _react = _interopRequireDefault(require("react")); var _tailwindMerge = require("tailwind-merge"); var _reactNavigationMenu = require("@radix-ui/react-navigation-menu"); var _Tooltip = _interopRequireDefault(require("../Tooltip/Tooltip")); var _react2 = require("@phosphor-icons/react"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } const dropdownSchema = _zod.z.array(_zod.z.object({ title: _zod.z.string(), items: _zod.z.array(_zod.z.object({ label: _zod.z.string(), href: _zod.z.string() })) })); const navItemSchema = _zod.z.object({ children: _zod.z.any(), icon: _zod.z.any().optional(), href: _zod.z.string().optional(), isActive: _zod.z.boolean().optional(), dropdown: _zod.z.union([dropdownSchema, _zod.z.any()]).optional(), asChild: _zod.z.boolean().optional(), type: _zod.z.enum(['primary', 'secondary']).optional(), tooltipText: _zod.z.string().optional() }); const mainMenuSchema = _zod.z.object({ children: _zod.z.any(), className: _zod.z.string().optional() }); const secondaryMenuSchema = _zod.z.object({ children: _zod.z.any(), className: _zod.z.string().optional() }); const NavItem = /*#__PURE__*/_react.default.forwardRef(({ children, icon: Icon, href, isActive, dropdown, asChild = false, type = 'primary', tooltipText, ...props }, ref) => { try { navItemSchema.parse({ children, icon: Icon, href, isActive, dropdown, asChild, type, tooltipText }); } catch (error) { console.error('NavItem validation error:', error); return null; } const hasDropdown = dropdown && (Array.isArray(dropdown) || /*#__PURE__*/_react.default.isValidElement(dropdown)); if (hasDropdown) { // If dropdown is a React node, we can assume it's a custom dropdown if (! /*#__PURE__*/_react.default.isValidElement(dropdown)) { // Validate the dropdown schema const validatedDropdown = dropdownSchema.parse(dropdown); if (!validatedDropdown) { console.error('Invalid dropdown schema', dropdown); } } } // If asChild is true, we render the child directly if (asChild) { return /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.Item, _extends({ className: (0, _tailwindMerge.twMerge)("relative", isActive && "active-page"), ref: ref }, props), children); } // If it's a secondary menu item (icon menu to the right) if (type === 'secondary') { return /*#__PURE__*/_react.default.createElement(_Tooltip.default, { content: tooltipText || children, side: "bottom", delayDuration: 0 }, /*#__PURE__*/_react.default.createElement("li", _extends({ ref: ref }, props), /*#__PURE__*/_react.default.createElement("a", { href: href || "#", className: "flex items-center justify-center flex-shrink-0 h-14 px-3 focus-visible:ring-primary-support-400 focus-visible:ring-2 focus-visible:ring-inset outline-none", "aria-label": children, title: tooltipText || children }, Icon && /*#__PURE__*/_react.default.createElement(Icon, { weight: "regular", className: "w-6 h-6 text-primary-600 hover:text-primary-500" }), /*#__PURE__*/_react.default.createElement("span", { className: "sr-only" }, children)))); } // If it has a dropdown if (hasDropdown) { return /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.Item, { className: "group relative z-[1]" }, /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.Trigger, { className: (0, _tailwindMerge.twMerge)("group inline-flex rtl:flex-row-reverse items-center gap-2 border-b-2 border-transparent px-3 py-4 font-bold transition-colors", "hover:border-primary-800 hover:text-primary-800", "[&[data-state=open]]:border-primary-800", "focus-visible:ring-primary-support-400 focus-visible:ring-2 focus-visible:ring-inset outline-none", isActive && "border-primary-900 text-primary-900") }, Icon && /*#__PURE__*/_react.default.createElement(Icon, { weight: "regular", className: "h-5 w-5" }), children, /*#__PURE__*/_react.default.createElement(_react2.CaretDown, { weight: "bold", className: "h-4 w-4 transition-transform [&[data-state=open]]:rotate-180", "aria-hidden": true })), /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.Content, { className: ` mt-2 absolute z-50 w-[300px] rtl:right-0 data-[motion=from-start]:animate-enterFromLeft data-[motion=from-end]:animate-enterFromRight data-[motion=to-start]:animate-exitToLeft data-[motion=to-end]:animate-exitToRight ` }, /*#__PURE__*/_react.default.createElement("div", { className: "rounded-lg border border-aeblack-100 bg-whitely-50 p-4 shadow-lg" }, /*#__PURE__*/_react.default.isValidElement(dropdown) ? dropdown : /*#__PURE__*/_react.default.createElement("div", { className: "flex flex-col rtl:text-right" }, dropdown.map((group, index) => /*#__PURE__*/_react.default.createElement("div", { key: index, className: "mb-6 last:mb-0" }, /*#__PURE__*/_react.default.createElement("h2", { className: "mb-2 text-primary-500 font-bold" }, group.title), /*#__PURE__*/_react.default.createElement("ul", { className: "space-y-1" }, group.items.map((item, itemIndex) => /*#__PURE__*/_react.default.createElement("li", { key: itemIndex }, /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.Link, { href: item.href || "#", className: "block px-2 py-1.5 text-aeblack-900 rounded hover:bg-aeblack-50 hover:text-primary-700 transition-colors" }, item.label)))))))))); } // Regular nav item return /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.Item, _extends({ className: (0, _tailwindMerge.twMerge)("relative", isActive && "active-page"), ref: ref }, props), /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.Link, { href: href || "#", className: (0, _tailwindMerge.twMerge)("inline-flex rtl:flex-row-reverse items-center gap-2 border-b-2 border-transparent px-3 py-4 font-bold transition-colors hover:border-primary-800 hover:text-primary-800", "focus-visible:ring-primary-support-400 focus-visible:ring-2 focus-visible:ring-inset outline-none", isActive && "border-primary-900 text-primary-900") }, Icon && /*#__PURE__*/_react.default.createElement(Icon, { weight: "regular", className: "h-5 w-5" }), children)); }); NavItem.displayName = 'NavItem'; // MainMenu Component const MainMenu = /*#__PURE__*/_react.default.forwardRef(({ children, className, ...props }, ref) => { try { mainMenuSchema.parse({ children, className }); } catch (error) { console.error('MainMenu validation error:', error); return null; } return /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.Root, _extends({ className: "relative z-[1]", ref: ref }, props), /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.List, { className: (0, _tailwindMerge.twMerge)("flex items-center gap-1 rtl:flex-row-reverse", className) }, children, /*#__PURE__*/_react.default.createElement(_reactNavigationMenu.Indicator, { className: "top-full z-[1] flex h-[10px] items-end justify-center overflow-hidden transition-[width,transform_250ms_ease]" }, /*#__PURE__*/_react.default.createElement("div", { className: "relative top-[70%] h-[10px] w-[10px] rotate-[45deg] rounded-tl-[2px] bg-white" }))), /*#__PURE__*/_react.default.createElement("div", { className: "absolute top-full left-0 w-full" })); }); MainMenu.displayName = 'MainMenu'; // SecondaryMenu Component const SecondaryMenu = /*#__PURE__*/_react.default.forwardRef(({ children, className, ...props }, ref) => { try { secondaryMenuSchema.parse({ children, className }); } catch (error) { console.error('SecondaryMenu validation error:', error); return null; } return /*#__PURE__*/_react.default.createElement("div", _extends({ className: (0, _tailwindMerge.twMerge)("header-navs-right", className), ref: ref }, props), /*#__PURE__*/_react.default.createElement("ul", { className: "flex items-center" }, children)); }); SecondaryMenu.displayName = 'SecondaryMenu'; // Mobile Navigation Component const MobileNavigation = ({ children, logo }) => { const [isOpen, setIsOpen] = _react.default.useState(false); // Extract main and secondary menu items let mainMenuItems = []; let secondaryMenuItems = []; _react.default.Children.forEach(children, child => { if (child.type === MainMenu) { mainMenuItems = _react.default.Children.toArray(child.props.children); } else if (child.type === SecondaryMenu) { secondaryMenuItems = _react.default.Children.toArray(child.props.children); } }); // Pre-initialize state for all menu items to avoid hooks order issues const [submenuOpenStates, setSubmenuOpenStates] = _react.default.useState(Array(mainMenuItems.length).fill(false)); const toggleSubmenu = index => { const newStates = [...submenuOpenStates]; newStates[index] = !newStates[index]; setSubmenuOpenStates(newStates); }; return /*#__PURE__*/_react.default.createElement("div", { className: "" }, /*#__PURE__*/_react.default.createElement("div", { className: "py-2.5" }, /*#__PURE__*/_react.default.createElement("div", { className: "" }, /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center justify-between" }, /*#__PURE__*/_react.default.createElement("div", { className: "logos" }, /*#__PURE__*/_react.default.createElement("div", { className: "logo-item" }, /*#__PURE__*/_react.default.createElement("a", { href: "#", className: "logo block" }, /*#__PURE__*/_react.default.createElement("span", { className: "sr-only" }, "Logo"), logo && logo))), /*#__PURE__*/_react.default.createElement("div", { className: "header-top-right" }, /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center justify-between gap-3" }, /*#__PURE__*/_react.default.createElement("button", { onClick: () => setIsOpen(true), className: "hamburger-icon text-aeblack-700" }, /*#__PURE__*/_react.default.createElement(_react2.List, { weight: "light", className: "w-8 h-8" }), /*#__PURE__*/_react.default.createElement("span", { className: "sr-only" }, "Toggle main menu"))))))), isOpen && /*#__PURE__*/_react.default.createElement("div", { className: "fixed inset-0 z-50 bg-white overflow-auto" }, /*#__PURE__*/_react.default.createElement("div", { className: "flex flex-col h-full" }, /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center justify-between p-4 border-b border-gray-200" }, /*#__PURE__*/_react.default.createElement("a", { href: "#", className: "logo" }, logo && logo), /*#__PURE__*/_react.default.createElement("button", { onClick: () => setIsOpen(false), className: "text-black" }, /*#__PURE__*/_react.default.createElement(_react2.X, { weight: "light", className: "w-8 h-8" }), /*#__PURE__*/_react.default.createElement("span", { className: "sr-only" }, "Close main menu"))), /*#__PURE__*/_react.default.createElement("div", { className: "flex-1 overflow-y-auto" }, /*#__PURE__*/_react.default.createElement("div", { className: "p-4" }, /*#__PURE__*/_react.default.createElement("div", { className: "mb-6" }, /*#__PURE__*/_react.default.createElement("nav", { "aria-label": "Main navigation" }, /*#__PURE__*/_react.default.createElement("ul", { className: "space-y-4" }, mainMenuItems.map((item, index) => { const props = item.props; const hasDropdown = props.dropdown; const Icon = props.icon; const isSubmenuOpen = submenuOpenStates[index]; return /*#__PURE__*/_react.default.createElement("li", { key: index, className: "relative" }, /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center" }, /*#__PURE__*/_react.default.createElement("a", { href: hasDropdown ? undefined : props.href || "#", onClick: hasDropdown ? () => toggleSubmenu(index) : undefined, className: `py-2 w-full text-black font-medium ${hasDropdown ? 'rtl:pr-6' : ''}` }, Icon && /*#__PURE__*/_react.default.createElement(Icon, { className: "inline-block mr-2 rtl:mr-0 rtl:ml-2 h-5 w-5" }), props.children), hasDropdown && /*#__PURE__*/_react.default.createElement("button", { onClick: () => toggleSubmenu(index), className: "absolute right-0 top-3 w-6" }, /*#__PURE__*/_react.default.createElement(_react2.CaretDown, { weight: "bold", className: `transition-transform ${isSubmenuOpen ? 'rotate-180' : ''}` }), /*#__PURE__*/_react.default.createElement("span", { className: "sr-only" }, isSubmenuOpen ? `Hide` : `Show`, " submenu for \"", props.children, "\""))), hasDropdown && isSubmenuOpen && Array.isArray(props.dropdown) && /*#__PURE__*/_react.default.createElement("div", { className: "mt-2 pl-2" }, props.dropdown.map((group, groupIndex) => /*#__PURE__*/_react.default.createElement("div", { key: groupIndex, className: "mb-4" }, /*#__PURE__*/_react.default.createElement("h3", { className: "text-primary-500 font-bold mb-2" }, group.title), /*#__PURE__*/_react.default.createElement("ul", { className: "space-y-2" }, group.items.map((subItem, subItemIndex) => /*#__PURE__*/_react.default.createElement("li", { key: subItemIndex }, /*#__PURE__*/_react.default.createElement("a", { href: subItem.href || "#", className: "block py-1 text-black" }, subItem.label)))))))); })))))), /*#__PURE__*/_react.default.createElement("div", { className: "border-t border-gray-200 p-4" }, /*#__PURE__*/_react.default.createElement("ul", { className: "space-y-4" }, secondaryMenuItems.map((item, index) => { const props = item.props; const Icon = props.icon; return /*#__PURE__*/_react.default.createElement("li", { key: index }, /*#__PURE__*/_react.default.createElement("a", { href: props.href || "#", className: "flex items-center text-black" }, Icon && /*#__PURE__*/_react.default.createElement(Icon, { className: "w-5 h-5 mr-2 rtl:mr-0 rtl:ml-2" }), /*#__PURE__*/_react.default.createElement("span", null, props.children))); })))))); }; // Main Navigation Component const Navigation = /*#__PURE__*/_react.default.forwardRef(({ children, className, isMobile = false, logo, ...props }, ref) => { if (isMobile) { return /*#__PURE__*/_react.default.createElement(MobileNavigation, { logo: logo }, children); } return /*#__PURE__*/_react.default.createElement("div", _extends({ className: (0, _tailwindMerge.twMerge)("hidden lg:block", className), ref: ref }, props), /*#__PURE__*/_react.default.createElement("div", { className: "bg-aeblack-50" }, /*#__PURE__*/_react.default.createElement("div", { className: "container" }, /*#__PURE__*/_react.default.createElement("div", { className: "flex items-center justify-between" }, children)))); }); Navigation.displayName = 'Navigation'; // Export all components Navigation.MainMenu = MainMenu; Navigation.SecondaryMenu = SecondaryMenu; Navigation.NavItem = NavItem; var _default = exports.default = Navigation;