aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
231 lines (228 loc) • 7.84 kB
JavaScript
'use client';
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
import { cn } from '../../lib/utilsComprehensive.js';
import { forwardRef, useState, useEffect } 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';
import { IconButton, GlassButton } from '../button/GlassButton.js';
import { GlassBadge } from '../data-display/GlassBadge.js';
import { HStack, VStack } from '../layout/GlassStack.js';
/**
* GlassMobileNav component
* Mobile-first navigation with glassmorphism design
*/
const GlassMobileNav = /*#__PURE__*/forwardRef(({
open = false,
onOpenChange,
variant = "overlay",
position = "left",
elevation = "modal",
logo,
title,
navigation = [],
activePath = "",
isActive,
footer,
closeOnClick = true,
showBackdrop = true,
renderItem,
className,
children,
...props
}, ref) => {
const [expandedItems, setExpandedItems] = useState(new Set());
// Default active path matcher
const defaultIsActive = (href, currentPath) => {
if (href === "/") return currentPath === "/";
return currentPath.startsWith(href);
};
const checkIsActive = isActive || defaultIsActive;
// Position classes
const positionClasses = {
left: {
overlay: "fixed inset-y-0 left-0 w-80 max-w-[85vw]",
push: "fixed inset-y-0 left-0 w-80 max-w-[85vw]",
slide: "fixed inset-y-0 left-0 w-80 max-w-[85vw]"
},
right: {
overlay: "fixed inset-y-0 right-0 w-80 max-w-[85vw]",
push: "fixed inset-y-0 right-0 w-80 max-w-[85vw]",
slide: "fixed inset-y-0 right-0 w-80 max-w-[85vw]"
},
top: {
overlay: "fixed inset-x-0 top-0 h-96 max-h-[85vh]",
push: "fixed inset-x-0 top-0 h-96 max-h-[85vh]",
slide: "fixed inset-x-0 top-0 h-96 max-h-[85vh]"
},
bottom: {
overlay: "fixed inset-x-0 bottom-0 h-96 max-h-[85vh]",
push: "fixed inset-x-0 bottom-0 h-96 max-h-[85vh]",
slide: "fixed inset-x-0 bottom-0 h-96 max-h-[85vh]"
}
};
// Transform classes for animations
const transformClasses = {
left: open ? "translate-x-0" : "-translate-x-full",
right: open ? "translate-x-0" : "translate-x-full",
top: open ? "translate-y-0" : "-translate-y-full",
bottom: open ? "translate-y-0" : "translate-y-full"
};
// Toggle item expansion
const toggleItem = itemId => {
const newExpanded = new Set(expandedItems);
if (newExpanded.has(itemId)) {
newExpanded.delete(itemId);
} else {
newExpanded.add(itemId);
}
setExpandedItems(newExpanded);
};
// Handle item click
const handleItemClick = item => {
if (item?.children && item?.children.length > 0) {
toggleItem(item?.id);
} else {
if (item?.href && closeOnClick) {
onOpenChange?.(false);
}
item?.onClick?.();
}
};
// Render navigation item
const renderNavigationItem = (item, level = 0) => {
if (renderItem) {
return renderItem(item, level);
}
const active = item?.href ? checkIsActive(item?.href, activePath) : false;
const hasChildren = item?.children && item?.children.length > 0;
const isExpanded = expandedItems.has(item?.id);
const itemContent = jsxs(Fragment, {
children: [jsxs("div", {
className: 'glass-flex glass-items-center glass-gap-3 glass-flex-1 min-glass-w-0',
children: [jsx("div", {
className: "glass-flex-shrink-0 glass-text-lg",
children: item?.icon
}), jsx("span", {
className: 'font-medium truncate',
children: item?.label
}), item?.badge && jsx(GlassBadge, {
variant: item?.badgeVariant || "outline",
size: "xs",
className: 'ml-auto',
children: item?.badge
})]
}), hasChildren && jsx("div", {
className: "glass-flex-shrink-0 glass-ml-2",
children: jsx("div", {
className: cn("transition-transform duration-200", isExpanded && "rotate-90"),
children: "\u25B6"
})
})]
});
return jsxs("div", {
"data-glass-component": true,
children: [jsx(GlassButton, {
variant: active ? "primary" : "ghost",
size: "md",
disabled: item?.disabled,
className: cn("w-full justify-start h-12 glass-px-4", level > 0 && "ml-6 glass-mr-2"),
onClick: e => handleItemClick(item),
children: itemContent
}), hasChildren && isExpanded && jsx(MotionFramer, {
preset: "slideDown",
className: "glass-mt-1",
children: jsx(VStack, {
space: "xs",
children: item?.children.map(child => renderNavigationItem(child, level + 1))
})
})]
}, item?.id);
};
// Render section
const renderSection = section => {
return jsxs("div", {
children: [section.label && jsx("div", {
className: "glass-px-4 glass-py-2",
children: jsx("h3", {
className: 'glass-text-xs font-medium glass-text-secondary uppercase tracking-wide',
children: section.label
})
}), jsx(VStack, {
space: "xs",
children: section.items.map(item => renderNavigationItem(item))
})]
}, section.id);
};
// Handle backdrop click
const handleBackdropClick = () => {
onOpenChange?.(false);
};
// Prevent body scroll when open
useEffect(() => {
if (open) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
return () => {
document.body.style.overflow = "";
};
}, [open]);
return jsxs(Fragment, {
children: [showBackdrop && open && jsx(MotionFramer, {
preset: "fadeIn",
className: 'fixed inset-0 glass-surface-dark/50 glass-glass-glass-backdrop-blur-md z-[100] glass-contrast-guard',
onClick: handleBackdropClick
}), jsxs(OptimizedGlassCore, {
intent: "neutral",
elevation: "level2",
intensity: "medium",
depth: 2,
tint: "neutral",
border: "subtle",
animation: "none",
performanceMode: "medium",
ref: ref,
className: cn("flex flex-col z-[101] transition-transform duration-300 ease-out", positionClasses?.[position][variant], transformClasses?.[position], className),
...props,
children: [jsxs("div", {
className: "glass-flex glass-items-center glass-justify-between glass-p-4 glass-border-b glass-border-glass-border/20",
children: [jsxs(HStack, {
space: "sm",
align: "center",
className: 'glass-flex-1 min-glass-w-0',
children: [logo && jsx("div", {
className: "glass-flex-shrink-0",
children: logo
}), title && jsx("h1", {
className: 'font-bold glass-text-lg text-primary truncate',
children: title
})]
}), jsx(IconButton, {
icon: "\u00D7",
variant: "ghost",
size: "sm",
onClick: e => onOpenChange?.(false),
"aria-label": "Close navigation",
className: "glass-focus glass-touch-target"
})]
}), jsx("nav", {
className: 'glass-flex-1 glass-p-4 overflow-y-auto',
children: jsxs(VStack, {
space: "lg",
children: [navigation.map(section => renderSection(section)), children]
})
}), footer && jsx("div", {
className: "glass-p-4 glass-border-t glass-border-glass-border/20",
children: footer
})]
})]
});
});
GlassMobileNav.displayName = "GlassMobileNav";
export { GlassMobileNav };
//# sourceMappingURL=GlassMobileNav.js.map