@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
JavaScript
"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;