import { cva, type VariantProps } from "class-variance-authority"; import * as React from "react"; import { cn } from "@sparkle/lib/utils"; const markerVariants = cva( "s-flex s-h-3.5 s-w-3.5 s-items-center s-justify-center s-rounded-full s-border-2", { variants: { variant: { complete: cn( "s-border-highlight-500 dark:s-border-highlight-500-night", "s-bg-highlight-500 dark:s-bg-highlight-500-night", "s-shadow-sm" ), current: cn( "s-border-highlight-500 dark:s-border-highlight-500-night", "s-bg-background dark:s-bg-background-night", "s-shadow-sm" ), upcoming: cn( "s-border-border dark:s-border-border-night", "s-bg-background dark:s-bg-background-night" ), }, }, defaultVariants: { variant: "complete", }, } ); const lineVariants = cva("s-w-[2px] s-flex-1", { variants: { variant: { complete: "s-bg-highlight-500 dark:s-bg-highlight-500-night", current: "s-bg-highlight-500 dark:s-bg-highlight-500-night", upcoming: "s-bg-border dark:s-bg-border-night", }, }, defaultVariants: { variant: "complete", }, }); export interface TimelineProps extends React.HTMLAttributes { /** * When true, last item does not render a line below it. */ bounded?: boolean; } function Timeline({ className, children, bounded = true, ...props }: TimelineProps) { const items = React.Children.toArray(children); return (
{items.map((child, index) => { if (!React.isValidElement(child)) { return child; } const isLast = index === items.length - 1; return React.cloneElement(child, { isLast: bounded ? isLast : false, }); })}
); } export interface TimelineItemProps extends React.HTMLAttributes, VariantProps { title?: string; description?: React.ReactNode; meta?: React.ReactNode; isLast?: boolean; } const TimelineItem = React.forwardRef( ( { className, variant = "complete", title, description, meta, children, isLast, ...props }, ref ) => { const showBottomLine = !isLast; return (