import * as React from "react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { cn } from "@/lib/utils" export type ActivityFeedItem = { id: string title: React.ReactNode description?: React.ReactNode time?: React.ReactNode icon?: React.ReactNode tone?: "default" | "success" | "warning" | "danger" | "info" | "muted" actions?: React.ReactNode meta?: React.ReactNode badge?: React.ReactNode unread?: boolean hidden?: boolean interactive?: boolean href?: string target?: React.ComponentProps<"a">["target"] rel?: React.ComponentProps<"a">["rel"] onSelect?: () => void className?: string } export type ActivityFeedProps = React.ComponentProps & { title?: React.ReactNode description?: React.ReactNode actions?: React.ReactNode items: ActivityFeedItem[] empty?: React.ReactNode compact?: boolean density?: "comfortable" | "compact" showConnector?: boolean connectorClassName?: string contentClassName?: string itemClassName?: string } const toneDotClassName: Record, string> = { default: "bg-primary", success: "bg-emerald-500", warning: "bg-amber-500", danger: "bg-destructive", info: "bg-blue-500", muted: "bg-muted-foreground", } function ActivityFeed({ className, title, description, actions, items, empty = "No activity yet.", compact = false, density, showConnector = true, connectorClassName, contentClassName, itemClassName, ...props }: ActivityFeedProps) { const visibleItems = items.filter((item) => !item.hidden) const hasHeader = Boolean(title || description || actions) const isCompact = density ? density === "compact" : compact return ( {hasHeader && (
{title && {title}} {description && {description}}
{actions &&
{actions}
}
)} {visibleItems.length === 0 ? (
{empty}
) : ( visibleItems.map((item, index) => { const isInteractive = item.interactive ?? Boolean(item.href || item.onSelect) const content = ( <>
{item.icon ?? }
{item.title}
{item.meta &&
{item.meta}
}
{item.badge} {item.time &&
{item.time}
}
{item.description &&
{item.description}
} {item.actions &&
{item.actions}
}
) return (
{showConnector && index < visibleItems.length - 1 && (
)} {item.href ? ( {content} ) : isInteractive ? ( ) : (
{content}
)}
)}) )} ) } export { ActivityFeed }