UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

283 lines (280 loc) 10.1 kB
'use client'; import { jsxs, jsx } from 'react/jsx-runtime'; import { cn } from '../../lib/utilsComprehensive.js'; import React, { 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 { GlassContainer } from './GlassContainer.js'; import { VStack } from './GlassStack.js'; import { useA11yId } from '../../utils/a11y.js'; import { useMotionPreferenceContext } from '../../contexts/MotionPreferenceContext.js'; /** * GlassAppShell component * Modern application shell with glassmorphism design */ const GlassAppShell = /*#__PURE__*/forwardRef(({ // TODO: Integrate ContrastGuard for any section titles, labels, and helper text for WCAG AA compliance variant = "default", header, sidebar, footer, collapsible = true, defaultCollapsed = false, sidebarWidth = "md", mobileOverlay = true, mobileBreakpoint = 1024, padding = "lg", maxWidth = "full", centered = false, contentElevation = 0, loading = false, loadingComponent, pageTransition = true, respectMotionPreference = true, "aria-label": ariaLabel = "Application shell", role = "application", className, children, ...props }, ref) => { const [sidebarCollapsed, setSidebarCollapsed] = useState(defaultCollapsed); const [sidebarOverlay, setSidebarOverlay] = useState(false); const [isMobile, setIsMobile] = useState(false); const shellId = useA11yId(); const { prefersReducedMotion, isMotionSafe } = useMotionPreferenceContext(); const shouldRespectMotion = respectMotionPreference && !prefersReducedMotion; // Handle responsive behavior useEffect(() => { const checkScreenSize = () => { const mobile = window.innerWidth < mobileBreakpoint; setIsMobile(mobile); if (mobile && mobileOverlay) { setSidebarOverlay(false); } }; checkScreenSize(); window.addEventListener("resize", checkScreenSize); return () => window.removeEventListener("resize", checkScreenSize); }, [mobileBreakpoint, mobileOverlay]); const variantClasses = { default: "", floating: "glass-p-4", minimal: "bg-transparent" }; // Clone sidebar with props (align with GlassSidebar API) const sidebarExtraProps = { collapsed: isMobile ? false : sidebarCollapsed, onCollapsedChange: setSidebarCollapsed, width: sidebarWidth }; if (isMobile && mobileOverlay) { sidebarExtraProps.variant = "overlay"; sidebarExtraProps.open = sidebarOverlay; sidebarExtraProps.onOpenChange = setSidebarOverlay; } const sidebarElement = sidebar ? /*#__PURE__*/React.cloneElement(sidebar, sidebarExtraProps) : null; // Clone header with props (align with GlassHeader API) const headerElement = header ? /*#__PURE__*/React.cloneElement(header, { mobileMenuOpen: Boolean(isMobile && mobileOverlay && sidebarOverlay), onMobileMenuToggle: () => setSidebarOverlay(v => !v) }) : null; return jsxs("div", { ref: ref, id: shellId, role: role, "aria-label": ariaLabel, className: cn("flex h-screen overflow-hidden", "bg-gradient-to-br from-background via-background/95 to-surface/50", // Motion preferences shouldRespectMotion && "motion-safe:transition-all motion-reduce:transition-none", variantClasses?.[variant], className), ...props, children: [sidebarElement, jsxs("div", { className: 'glass-flex glass-flex-col glass-flex-1 overflow-hidden', children: [headerElement, jsx("main", { role: "main", "aria-label": "Main content", className: cn("flex-1 overflow-auto", "scrollbar-thin scrollbar-track-transparent scrollbar-thumb-border/30", "hover:scrollbar-thumb-border/50", // Motion preferences shouldRespectMotion && "motion-safe:transition-all motion-reduce:transition-none"), children: loading && loadingComponent ? jsx("div", { className: "glass-flex glass-items-center glass-justify-center glass-h-full", children: loadingComponent }) : jsx(GlassContainer, { size: maxWidth, centered: centered, padding: padding, glass: contentElevation > 0, elevation: contentElevation, radius: variant === "floating" ? "lg" : "none", className: cn("min-h-full", variant === "floating" && "glass-my-4"), children: pageTransition && shouldRespectMotion ? jsx(MotionFramer, { preset: "fadeIn", className: "glass-h-full", children: children }) : children }) }), footer && jsx("footer", { role: "contentinfo", className: cn("flex-shrink-0 border-t border-border/20", // Motion preferences shouldRespectMotion && "motion-safe:transition-all motion-reduce:transition-none"), children: footer })] })] }); }); GlassAppShell.displayName = "GlassAppShell"; /** * PageHeader component * Consistent page header with title, description, and actions */ const PageHeader = /*#__PURE__*/forwardRef(({ title, description, breadcrumb, actions, variant = "default", className, ...props }, ref) => { const variantClasses = { default: "text-left", centered: "text-center", minimal: "text-left border-none pb-4" }; return jsxs("div", { ref: ref, className: cn("glass-gap-4 pb-8 border-b border-border/20", variantClasses?.[variant], className), ...props, children: [breadcrumb && jsx("div", { className: "glass-text-sm glass-text-secondary", children: Array.isArray(breadcrumb) ? jsx("nav", { "aria-label": "Breadcrumb", children: jsx("ol", { className: "glass-flex glass-items-center glass-gap-2", children: breadcrumb.map((item, index) => { const isLast = index === breadcrumb.length - 1; return jsxs("li", { className: "glass-flex glass-items-center", children: [index > 0 && jsx("span", { className: "glass-mx-2", children: "/" }), item?.href && !isLast ? jsx("a", { href: item?.href, className: 'hover:text-primary transition-colors glass-focus glass-touch-target glass-contrast-guard', children: item?.label }) : jsx("span", { className: isLast ? "text-foreground font-medium" : "", children: item?.label })] }, index); }) }) }) : breadcrumb }), jsxs("div", { className: cn("flex glass-gap-4", variant === "centered" ? "flex-col items-center" : "flex-col sm:flex-row sm:items-center sm:justify-between"), children: [jsxs("div", { className: "glass-gap-2", children: [jsx("h1", { className: 'glass-text-3xl font-bold text-primary', children: title }), description && jsx("p", { className: cn("glass-text-lg glass-text-secondary", variant === "centered" ? "max-w-2xl" : "max-w-3xl"), children: description })] }), actions && jsx("div", { className: "glass-flex-shrink-0", children: actions })] })] }); }); PageHeader.displayName = "PageHeader"; /** * ContentSection component * Reusable content section with optional glassmorphism */ const ContentSection = /*#__PURE__*/forwardRef(({ title, description, actions, children, variant = "default", elevation = "level1", respectMotionPreference = true, id, className, ...props }, ref) => { const sectionId = useA11yId(); const { prefersReducedMotion, isMotionSafe } = useMotionPreferenceContext(); const shouldRespectMotion = respectMotionPreference && !prefersReducedMotion; const content = jsxs(VStack, { space: "lg", className: "glass-w-full", children: [(title || description || actions) && jsxs("div", { className: 'glass-flex glass-flex-col sm:flex-row sm:items-center sm:justify-between glass-gap-4', children: [jsxs("div", { className: "glass-gap-1", children: [title && jsx("h2", { id: `${id || sectionId}-title`, className: 'glass-text-xl font-semibold text-primary', children: title }), description && jsx("p", { className: "glass-text-secondary", children: description })] }), actions && jsx("div", { className: "glass-flex-shrink-0", children: actions })] }), jsx("div", { className: "glass-w-full", children: children })] }); if (variant === "card") { return jsx(OptimizedGlassCore, { variant: "frosted", elevation: typeof elevation === "number" ? `level${Math.min(5, Math.max(1, elevation + 1))}` : "level1", intensity: "medium", depth: 2, tint: "neutral", border: "subtle", animation: shouldRespectMotion ? "shimmer" : "none", performanceMode: "medium", ref: ref, id: id || sectionId, role: "region", "aria-labelledby": title ? `${id || sectionId}-title` : undefined, className: cn("glass-p-6 w-full", // Motion preferences shouldRespectMotion && "motion-safe:transition-all motion-reduce:transition-none", className), ...props, children: content }); } return jsx("section", { ref: ref, id: id || sectionId, role: "region", "aria-labelledby": title ? `${id || sectionId}-title` : undefined, className: cn("w-full", // Motion preferences shouldRespectMotion && "motion-safe:transition-all motion-reduce:transition-none", className), ...props, children: content }); }); ContentSection.displayName = "ContentSection"; export { ContentSection, GlassAppShell, PageHeader }; //# sourceMappingURL=GlassAppShell.js.map