UNPKG

aura-glass

Version:

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

439 lines (436 loc) 19.9 kB
'use client'; import { jsx, jsxs } from 'react/jsx-runtime'; import { cn } from '../../lib/utilsComprehensive.js'; import { Filter, Activity, MoreHorizontal, Clock, AlertCircle, CheckCircle, XCircle, Info, Settings, User } from 'lucide-react'; import { useState, useMemo } from 'react'; import '../../primitives/GlassCore.js'; import '../../primitives/glass/GlassAdvanced.js'; import '../../primitives/OptimizedGlassCore.js'; import '../../primitives/glass/OptimizedGlassAdvanced.js'; import '../../primitives/MotionNative.js'; import { MotionFramer } from '../../primitives/motion/MotionFramer.js'; import { GlassButton } from '../button/GlassButton.js'; import '../button/GlassFab.js'; import '../button/GlassMagneticButton.js'; import { CardHeader, CardTitle, CardContent } from '../card/index.js'; import '../data-display/GlassAccordion.js'; import '../data-display/GlassAlert.js'; import '../data-display/GlassAvatar.js'; import { GlassBadge } from '../data-display/GlassBadge.js'; import '../data-display/GlassBadgeLine.js'; import '../data-display/GlassDataGrid.js'; import '../data-display/GlassDataTable.js'; import '../data-display/GlassHeatmap.js'; import '../data-display/GlassLoadingSkeleton.js'; import '../data-display/GlassProgress.js'; import '../data-display/GlassTimeline.js'; import '../data-display/GlassSkeleton.js'; import '../data-display/GlassNotificationCenter.js'; import '../data-display/GlassAnimatedNumber.js'; import { GlassCard } from '../card/GlassCard.js'; /** * GlassActivityFeed component * A glassmorphism activity timeline with filtering and grouping */ const GlassActivityFeed = ({ // TODO: Integrate ContrastGuard for table cells, list items, badges, card titles, and other text content for WCAG AA compliance activities, title = "Activity Feed", subtitle, maxItems, showFilters = true, showCategories = true, showTimestamps = true, showAvatars = true, compact = false, loading = false, emptyMessage = "No recent activity", filterBy = [], groupByDate = true, showLoadMore = false, onLoadMore, onActivityClick, className, ...props }) => { const [selectedFilter, setSelectedFilter] = useState("all"); const [visibleCount, setVisibleCount] = useState(maxItems || 10); // Get activity type icon and color const getActivityTypeConfig = type => { switch (type) { case "user": return { icon: User, color: "text-blue-400", bgColor: "bg-blue-500/20", borderColor: "border-blue-500/30" }; case "system": return { icon: Settings, color: "text-purple-400", bgColor: "bg-purple-500/20", borderColor: "border-purple-500/30" }; case "notification": return { icon: Info, color: "text-cyan-400", bgColor: "bg-cyan-500/20", borderColor: "border-cyan-500/30" }; case "error": return { icon: XCircle, color: "text-red-400", bgColor: "bg-red-500/20", borderColor: "border-red-500/30" }; case "success": return { icon: CheckCircle, color: "text-green-400", bgColor: "bg-green-500/20", borderColor: "border-green-500/30" }; case "warning": return { icon: AlertCircle, color: "text-yellow-400", bgColor: "bg-yellow-500/20", borderColor: "border-yellow-500/30" }; default: return { icon: Activity, color: "glass-text-secondary", bgColor: "bg-gray-500/20", borderColor: "border-gray-500/30" }; } }; // Format timestamp const formatTimestamp = date => { const now = new Date(); const diff = now.getTime() - date.getTime(); const minutes = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); if (minutes < 1) return "Just now"; if (minutes < 60) return `${minutes}m ago`; if (hours < 24) return `${hours}h ago`; if (days === 1) return "Yesterday"; if (days < 7) return `${days}d ago`; return date.toLocaleDateString(); }; // Group activities by date const groupedActivities = useMemo(() => { if (!groupByDate) return { "All Activities": activities }; const groups = {}; activities.forEach(activity => { const date = activity.timestamp.toDateString(); if (!groups[date]) { groups[date] = []; } groups[date].push(activity); }); return groups; }, [activities, groupByDate]); // Filter activities const filteredActivities = useMemo(() => { let filtered = activities; if (selectedFilter !== "all") { filtered = filtered.filter(activity => activity.type === selectedFilter); } if (filterBy && filterBy.length > 0) { filtered = filtered.filter(activity => filterBy.includes(activity.type)); } return filtered.slice(0, visibleCount); }, [activities, selectedFilter, filterBy, visibleCount]); // Get available filter options const filterOptions = useMemo(() => { const types = new Set(activities.map(activity => activity.type)); return ["all", ...Array.from(types)]; }, [activities]); // Handle load more const handleLoadMore = () => { setVisibleCount(prev => prev + (maxItems || 10)); onLoadMore?.(); }; // Loading skeleton if (loading) { return jsx(GlassCard, { "data-glass-component": true, className: cn("glass-p-6", className), children: jsxs("div", { className: 'animate-pulse glass-auto-gap glass-auto-gap-lg', children: [jsx("div", { className: 'h-6 glass-surface-subtle/20 glass-radius-md w-48' }), jsx("div", { className: "glass-auto-gap glass-auto-gap-md", children: Array.from({ length: 5 }).map((_, i) => jsxs("div", { className: "glass-flex glass-gap-3", children: [jsx("div", { className: 'w-8 h-8 glass-surface-subtle/20 glass-radius-full glass-flex-shrink-0' }), jsxs("div", { className: "glass-flex-1 glass-auto-gap glass-auto-gap-sm", children: [jsx("div", { className: 'h-4 glass-surface-subtle/20 glass-radius-md w-3-4' }), jsx("div", { className: 'h-3 glass-surface-subtle/20 glass-radius-md w-1-2' })] })] }, i)) })] }) }); } return jsx(MotionFramer, { preset: "fadeIn", className: "glass-w-full", children: jsxs(GlassCard, { className: cn("overflow-hidden", className), ...props, children: [jsx(CardHeader, { className: 'pb-4', children: jsxs("div", { className: "glass-flex glass-items-center glass-justify-between", children: [jsxs("div", { children: [jsx(CardTitle, { className: 'text-primary glass-text-xl font-semibold', children: title }), subtitle && jsx("p", { className: 'glass-text-sm text-primary/60 glass-mt-1', children: subtitle })] }), showFilters && filterOptions.length > 1 && jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [jsx(Filter, { className: 'w-4 h-4 text-primary/60' }), jsxs("select", { value: selectedFilter, onChange: e => setSelectedFilter(e.target.value), className: 'bg-glass-fill ring-1 ring-white/10 glass-radius-md glass-px-3 glass-py-1 glass-text-sm text-primary focus:outline-none focus:ring-white/30', children: [jsx("option", { value: "all", children: "All Types" }), filterOptions.slice(1).map(type => jsx("option", { value: type, children: type.charAt(0).toUpperCase() + type.slice(1) }, type))] })] })] }) }), jsx(CardContent, { className: 'pt-0', children: filteredActivities.length === 0 ? jsxs("div", { className: "glass-flex glass-flex-col glass-items-center glass-justify-center glass-py-12", children: [jsx(Activity, { className: 'w-12 h-12 text-primary/40 mb-4' }), jsx("p", { className: 'text-primary/60 text-center', children: emptyMessage })] }) : jsxs("div", { className: "glass-auto-gap glass-auto-gap-lg", children: [groupByDate ? Object.entries(groupedActivities).map(([date, items]) => jsxs("div", { children: [groupByDate && items.length > 0 && jsxs("div", { className: 'glass-flex glass-items-center glass-gap-2 mb-3', children: [jsx("div", { className: 'h-px glass-surface-subtle/20 glass-flex-1' }), jsx("span", { className: 'glass-text-xs text-primary/60 font-medium glass-px-2 glass-py-1 glass-surface-subtle/10 glass-radius-md', children: new Date(date).toLocaleDateString(undefined, { weekday: "long", year: "numeric", month: "long", day: "numeric" }) }), jsx("div", { className: 'h-px glass-surface-subtle/20 glass-flex-1' })] }), jsx("div", { className: "glass-auto-gap glass-auto-gap-md", children: items.filter(activity => { if (selectedFilter === "all") return true; return activity.type === selectedFilter; }).slice(0, visibleCount).map((activity, index) => { const config = getActivityTypeConfig(activity.type); const IconComponent = config.icon; return jsxs("div", { className: cn("flex glass-gap-3 glass-p-3 glass-radius-lg border transition-all duration-200 animate-slide-in-up", "hover:bg-white/5 cursor-pointer glass-foundation-complete glass-backdrop-blur-md bg-transparent border-white/40 shadow-2xl", config.bgColor, config.borderColor, compact && "glass-p-2"), style: { animationDelay: `${Math.min(index, 15) * 50}ms`, animationFillMode: "both" }, onClick: e => onActivityClick?.(activity), children: [jsx("div", { className: cn("flex-shrink-0 w-8 h-8 glass-radius-full flex items-center justify-center", config.bgColor, compact && "w-6 h-6"), children: activity.icon ? jsx("span", { className: 'w-4 h-4', children: activity.icon }) : jsx(IconComponent, { className: cn("w-4 h-4", config.color, compact && "w-3 h-3") }) }), jsxs("div", { className: "glass-flex-1 glass-min-w-0", children: [jsxs("div", { className: "glass-flex glass-items-start glass-justify-between glass-gap-2 glass-min-w-0", children: [jsxs("div", { className: "glass-flex-1 glass-min-w-0", children: [jsx("p", { className: cn("glass-text-primary font-medium truncate", compact ? "glass-text-sm" : "glass-text-base"), title: activity.title, children: activity.title }), activity.description && jsx("p", { className: cn("glass-text-primary/70 glass-mt-1 line-clamp-2", compact ? "glass-text-xs" : "glass-text-sm"), title: activity.description, children: activity.description })] }), activity.actions && activity.actions.length > 0 && jsx("div", { className: "glass-flex glass-items-center glass-gap-1", children: activity.actions.map((action, actionIndex) => jsx(GlassButton, { variant: "ghost", size: "sm", onClick: e => { e.stopPropagation(); action.onClick(); }, className: "glass-p-1", children: action.icon || jsx(MoreHorizontal, { className: 'w-3 h-3' }) }, actionIndex)) })] }), jsxs("div", { className: "glass-flex glass-items-center glass-gap-3 glass-mt-2", children: [activity.user && showAvatars && jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [activity.user.avatar ? jsx("img", { src: activity.user.avatar, alt: activity.user.name, className: 'w-5 h-5 glass-radius-full' }) : jsx("div", { className: 'w-5 h-5 glass-radius-full glass-surface-subtle/20 glass-flex glass-items-center glass-justify-center', children: jsx("span", { className: 'glass-text-xs text-primary/80 font-medium', children: activity.user.name.charAt(0).toUpperCase() }) }), jsx("span", { className: 'glass-text-xs text-primary/60', children: activity.user.name })] }), showTimestamps && jsxs("span", { className: 'glass-text-xs text-primary/50 glass-flex glass-items-center glass-gap-1', children: [jsx(Clock, { className: 'w-3 h-3' }), formatTimestamp(activity.timestamp)] }), activity.category && showCategories && jsx(GlassBadge, { variant: "secondary", size: "sm", className: 'truncate max-w-[100px]', title: activity.category, children: activity.category.length > 12 ? `${activity.category.slice(0, 12)}...` : activity.category }), activity.tags && activity.tags.length > 0 && jsxs("div", { className: "glass-flex glass-flex-wrap glass-gap-1 glass-items-center", children: [activity.tags.slice(0, compact ? 1 : 2).map((tag, tagIndex) => jsx(GlassBadge, { variant: "outline", size: "sm", className: 'truncate max-w-[80px]', title: tag, children: tag.length > 8 ? `${tag.slice(0, 8)}...` : tag }, tagIndex)), activity.tags.length > (compact ? 1 : 2) && jsxs(GlassBadge, { variant: "secondary", size: "sm", className: 'opacity-60', children: ["+", activity.tags.length - (compact ? 1 : 2)] })] })] })] })] }, activity.id); }) })] }, date)) : // Ungrouped view jsx("div", { className: "glass-auto-gap glass-auto-gap-md", children: filteredActivities.map((activity, index) => { const config = getActivityTypeConfig(activity.type); const IconComponent = config.icon; return jsxs(MotionFramer, { preset: "slideUp", delay: index * 50, className: cn("flex glass-gap-3 glass-p-3 glass-radius-lg border transition-all duration-200", "hover:bg-white/5 cursor-pointer", config.bgColor, config.borderColor, compact && "glass-p-2"), onClick: e => onActivityClick?.(activity), children: [jsx("div", { className: cn("flex-shrink-0 w-8 h-8 glass-radius-full flex items-center justify-center", config.bgColor, compact && "w-6 h-6"), children: activity.icon ? jsx("span", { className: 'w-4 h-4', children: activity.icon }) : jsx(IconComponent, { className: cn("w-4 h-4", config.color, compact && "w-3 h-3") }) }), jsxs("div", { className: "glass-flex-1 glass-min-w-0", children: [jsx("div", { className: "glass-flex glass-items-start glass-justify-between glass-gap-2 glass-min-w-0", children: jsxs("div", { className: "glass-flex-1 glass-min-w-0", children: [jsx("p", { className: cn("glass-text-primary font-medium truncate", compact ? "glass-text-sm" : "glass-text-base"), title: activity.title, children: activity.title }), activity.description && jsx("p", { className: cn("glass-text-primary/70 glass-mt-1 line-clamp-2", compact ? "glass-text-xs" : "glass-text-sm"), title: activity.description, children: activity.description })] }) }), jsxs("div", { className: "glass-flex glass-items-center glass-gap-3 glass-mt-2", children: [activity.user && showAvatars && jsxs("div", { className: "glass-flex glass-items-center glass-gap-2", children: [activity.user.avatar ? jsx("img", { src: activity.user.avatar, alt: activity.user.name, className: 'w-5 h-5 glass-radius-full' }) : jsx("div", { className: 'w-5 h-5 glass-radius-full glass-surface-subtle/20 glass-flex glass-items-center glass-justify-center', children: jsx("span", { className: 'glass-text-xs text-primary/80 font-medium', children: activity.user.name.charAt(0).toUpperCase() }) }), jsx("span", { className: 'glass-text-xs text-primary/60', children: activity.user.name })] }), showTimestamps && jsxs("span", { className: 'glass-text-xs text-primary/50 glass-flex glass-items-center glass-gap-1', children: [jsx(Clock, { className: 'w-3 h-3' }), formatTimestamp(activity.timestamp)] })] })] })] }, activity.id); }) }), showLoadMore && visibleCount < activities.length && jsx("div", { className: 'glass-flex glass-justify-center mt-6', children: jsx(GlassButton, { variant: "outline", onClick: handleLoadMore, children: "Load More Activities" }) })] }) })] }) }); }; export { GlassActivityFeed, GlassActivityFeed as default }; //# sourceMappingURL=GlassActivityFeed.js.map