aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
256 lines (253 loc) • 9.52 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { cn } from '../../lib/utilsComprehensive.js';
import { ChevronRight } from 'lucide-react';
import React, { useState } from 'react';
import '../../primitives/GlassCore.js';
import '../../primitives/glass/GlassAdvanced.js';
import { OptimizedGlassCore } from '../../primitives/OptimizedGlassCore.js';
import '../../primitives/glass/OptimizedGlassAdvanced.js';
import '../../primitives/MotionNative.js';
import { MotionFramer } from '../../primitives/motion/MotionFramer.js';
/**
* GlassNavigationMenu component
* Advanced glassmorphism navigation menu with nested items
*/
const GlassNavigationMenu = ({
items = [],
orientation = "vertical",
variant = "default",
size = "md",
activeItem,
className,
collapsed = false,
onItemClick,
"aria-label": ariaLabel,
...rest
}) => {
const [openSubmenus, setOpenSubmenus] = useState(new Set());
const handleItemClick = item => {
if (item?.disabled) return;
if (item?.children && item?.children.length > 0) {
// Toggle submenu
setOpenSubmenus(prev => {
const newSet = new Set(prev);
if (newSet.has(item?.id)) {
newSet.delete(item?.id);
} else {
newSet.add(item?.id);
}
return newSet;
});
} else {
// Execute action
if (item?.href) {
if (item?.external) {
window.open(item?.href, "_blank");
} else {
window.location.href = item?.href;
}
} else {
item?.action?.();
}
onItemClick?.(item);
}
};
const toggleSubmenu = itemId => {
setOpenSubmenus(prev => {
const newSet = new Set(prev);
if (newSet.has(itemId)) {
newSet.delete(itemId);
} else {
newSet.add(itemId);
}
return newSet;
});
};
const variantClasses = {
default: "glass-glass-backdrop-blur-md ring-1 ring-white/10 bg-white/5",
sidebar: "glass-glass-backdrop-blur-md ring-0 border-r border-white/10 bg-white/5",
header: "glass-glass-backdrop-blur-md ring-0 border-b border-white/10 bg-white/5"
};
return jsx(OptimizedGlassCore, {
as: "nav",
"data-glass-component": true,
intent: "neutral",
elevation: "level1",
intensity: "medium",
depth: 2,
tint: "neutral",
border: "subtle",
animation: "none",
performanceMode: "medium",
className: cn(variantClasses?.[variant], orientation === "horizontal" ? "flex flex-row" : "flex flex-col", className),
role: "navigation",
"aria-label": ariaLabel ?? "Navigation menu",
...rest,
children: items.map((item, index) => jsxs(React.Fragment, {
children: [item?.separator && jsx("div", {
className: cn("bg-white/20", orientation === "horizontal" ? "w-px h-8 glass-mx-4" : "h-px w-full glass-my-2 glass-mx-4")
}), jsx(GlassNavigationMenuItem, {
item: item,
isActive: activeItem === item?.id,
hasSubmenuOpen: openSubmenus.has(item?.id),
collapsed: collapsed,
size: size,
onClick: handleItemClick,
onToggleSubmenu: toggleSubmenu
}), item?.children && openSubmenus.has(item?.id) && !collapsed && jsx(MotionFramer, {
preset: "slideDown",
duration: 200,
children: jsx("div", {
className: cn("glass-ml-4 border-l border-white/20 pl-4", orientation === "horizontal" && "absolute top-full left-0 glass-mt-2 z-50"),
children: jsx(GlassNavigationMenu, {
items: item?.children ?? [],
orientation: "vertical",
variant: "default",
size: size,
activeItem: activeItem,
onItemClick: onItemClick
})
})
})]
}, item?.id || index))
});
};
/**
* GlassNavigationMenuItem component
* Individual navigation menu item
*/
const GlassNavigationMenuItem = ({
item,
isActive = false,
hasSubmenuOpen = false,
collapsed = false,
size = "md",
onClick,
onToggleSubmenu,
className
}) => {
const hoverTimer = React.useRef(null);
const clearHoverTimer = () => {
if (hoverTimer.current) {
window.clearTimeout(hoverTimer.current);
hoverTimer.current = null;
}
};
const onHoverEnter = () => {
if (!item?.children || item?.children.length === 0 || collapsed) return;
clearHoverTimer();
hoverTimer.current = window.setTimeout(() => {
if (!hasSubmenuOpen) onToggleSubmenu(item?.id);
}, 120);
};
const onHoverLeave = () => {
if (!item?.children || item?.children.length === 0 || collapsed) return;
clearHoverTimer();
hoverTimer.current = window.setTimeout(() => {
if (hasSubmenuOpen) onToggleSubmenu(item?.id);
}, 180);
};
// Cleanup timer on unmount
React.useEffect(() => {
return () => {
clearHoverTimer();
};
}, []);
const sizeClasses = {
sm: "h-8 glass-px-3 glass-text-sm",
md: "h-10 glass-px-4 glass-text-base",
lg: "h-12 glass-px-6 glass-text-lg"
};
const handleClick = () => {
onClick(item);
};
const handleSubmenuToggle = e => {
e.stopPropagation();
onToggleSubmenu(item?.id);
};
if (item?.separator) {
return jsx("div", {
className: 'h-px glass-surface-subtle/20 glass-mx-4 glass-my-2'
});
}
if (collapsed) {
return jsx(MotionFramer, {
preset: "none",
children: jsxs("button", {
className: cn("relative flex items-center justify-center w-full", "glass-text-primary/70 hover:glass-text-primary transition-colors duration-200", "hover:bg-white/10 glass-radius-lg", "glass-focus glass-touch-target glass-contrast-guard", "focus:outline-none focus:ring-2 focus:ring-white/30 focus:ring-offset-2 focus:ring-offset-transparent", "disabled:opacity-50 disabled:cursor-not-allowed", sizeClasses?.[size], {
"bg-white/20 glass-text-primary": isActive,
"glass-text-primary": isActive
}, className),
onClick: handleClick,
disabled: item?.disabled,
title: item?.label,
type: "button",
children: [item?.icon && jsx("div", {
className: "glass-flex glass-items-center glass-justify-center",
children: item?.icon
}), item?.badge && jsx("div", {
className: 'absolute glass-top-1 -right-1 w-5 h-5 glass-surface-red glass-radius-full glass-flex glass-items-center glass-justify-center glass-text-xs font-bold text-primary',
children: typeof item?.badge === "number" && item?.badge > 99 ? "99+" : item?.badge
})]
})
});
}
return jsx(MotionFramer, {
preset: "none",
children: jsxs("button", {
className: cn("relative flex items-center justify-between w-full", "glass-text-primary/70 hover:glass-text-primary transition-all duration-200", "hover:bg-white/10 glass-radius-lg", "glass-focus glass-touch-target glass-contrast-guard", "focus:outline-none focus:ring-2 focus:ring-white/30 focus:ring-offset-2 focus:ring-offset-transparent", "disabled:opacity-50 disabled:cursor-not-allowed", sizeClasses?.[size], {
"bg-white/20 glass-text-primary shadow-md": isActive,
"glass-text-primary": isActive
}, className),
onClick: handleClick,
onMouseEnter: onHoverEnter,
onMouseLeave: onHoverLeave,
disabled: item?.disabled,
type: "button",
children: [jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-3 glass-flex-1 min-glass-w-0',
children: [item?.icon && jsx("div", {
className: "glass-flex glass-items-center glass-justify-center glass-flex-shrink-0",
children: item?.icon
}), jsxs("div", {
className: 'glass-flex-1 min-glass-w-0 text-left',
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2",
children: [jsx("span", {
className: 'truncate font-medium',
children: item?.label
}), item?.badge && jsx("span", {
className: 'glass-flex-shrink-0 glass-px-2 glass-py-0.5 glass-surface-red/20 glass-text-secondary glass-radius-full glass-text-xs font-medium',
children: typeof item?.badge === "number" && item?.badge > 99 ? "99+" : item?.badge
})]
}), item?.description && jsx("p", {
className: 'text-primary/50 glass-text-sm truncate glass-mt-0-5',
children: item?.description
})]
})]
}), jsxs("div", {
className: "glass-flex glass-items-center glass-gap-2 glass-flex-shrink-0",
children: [item?.external && jsx("div", {
className: 'w-3 h-3 text-primary/50',
children: "\u2197"
}), item?.featured && jsx("div", {
className: 'w-2 h-2 glass-surface-yellow glass-radius-full animate-pulse'
}), item?.children && item?.children.length > 0 && jsx("button", {
onClick: handleSubmenuToggle,
className: 'glass-p-1 hover:glass-surface-subtle/10 glass-radius-md transition-colors duration-200 glass-focus glass-touch-target glass-focus glass-touch-target glass-contrast-guard',
"aria-label": "Toggle submenu",
children: jsx(MotionFramer, {
preset: "rotateIn",
duration: 200,
children: jsx(ChevronRight, {
className: 'w-4 h-4 text-primary/50'
})
})
})]
})]
})
});
};
export { GlassNavigationMenu, GlassNavigationMenuItem };
//# sourceMappingURL=GlassNavigationMenu.js.map