{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"timeline","type":"registry:ui","title":"Timeline","description":"A vertical list of events in chronological order.","dependencies":[],"registryDependencies":["https://dashral.com/r/utils.json"],"files":[{"path":"registry/dashral/timeline.tsx","type":"registry:file","target":"components/dashral/timeline.tsx","content":"import * as React from \"react\"\n\nimport { cn } from \"./lib/utils\"\n\ntype TimelineColor = \"neutral\" | \"primary\" | \"info\" | \"success\" | \"warning\" | \"destructive\" | \"highlight\"\n\n// The marker: a tinted circle around the icon, or a dot without one. Every class is written out in full.\nconst markers: Record<TimelineColor, { circle: string; dot: string }> = {\n  neutral: { circle: \"bg-card text-muted-foreground\", dot: \"bg-muted-foreground\" },\n  primary: { circle: \"border-primary/30 bg-primary/10 text-primary-text\", dot: \"bg-primary\" },\n  info: { circle: \"border-info/30 bg-info/10 text-info-text\", dot: \"bg-info\" },\n  success: { circle: \"border-success/30 bg-success/10 text-success-text\", dot: \"bg-success\" },\n  warning: { circle: \"border-warning/40 bg-warning/12 text-warning-text\", dot: \"bg-warning\" },\n  destructive: { circle: \"border-destructive/30 bg-destructive/10 text-destructive-text\", dot: \"bg-destructive\" },\n  highlight: { circle: \"border-highlight/30 bg-highlight/10 text-highlight-text\", dot: \"bg-highlight\" },\n}\n\ntype TimelineItem = {\n  title: React.ReactNode\n  description?: React.ReactNode\n  /** When it happened, as shown: \"2 hours ago\", \"Oct 12\". */\n  time?: React.ReactNode\n  /** The same moment, machine-readable, for the <time> element: an ISO date. */\n  dateTime?: string\n  /** The marker's icon; without one it's a dot. */\n  icon?: React.ReactNode\n  color?: TimelineColor\n  /** A stable key; the index is used without one. */\n  id?: string | number\n}\n\ntype TimelineProps = Omit<React.ComponentProps<\"ol\">, \"children\"> & {\n  items: TimelineItem[]\n  classNames?: { item?: string; marker?: string; title?: string; time?: string; description?: string }\n}\n\n/** Events in order, in one tag: each with a marker on a line, a title, a time and a description. An ordered list. */\nfunction Timeline({ items, className, classNames, ...props }: TimelineProps) {\n  return (\n    <ol data-slot=\"timeline\" className={cn(\"relative\", className)} {...props}>\n      {items.map((item, i) => {\n        const marker = markers[item.color ?? (item.icon ? \"neutral\" : \"primary\")]\n        return (\n          <li key={item.id ?? i} className={cn(\"relative flex gap-4 pb-6 last:pb-0\", classNames?.item)}>\n            <span aria-hidden className=\"absolute top-8 bottom-0 start-4 w-px -translate-x-1/2 rtl:translate-x-1/2 bg-border [li:last-child>&]:hidden\" />\n            <span\n              className={cn(\n                \"relative z-10 flex size-8 shrink-0 items-center justify-center rounded-full border [&_svg]:size-4\",\n                item.icon ? marker.circle : \"bg-card\",\n                classNames?.marker\n              )}\n            >\n              {item.icon ?? <span className={cn(\"size-2 rounded-full\", marker.dot)} />}\n            </span>\n            <div className=\"min-w-0 flex-1 pt-1\">\n              <div className=\"flex flex-wrap items-baseline justify-between gap-x-3\">\n                <p className={cn(\"text-sm font-medium\", classNames?.title)}>{item.title}</p>\n                {item.time && (\n                  <time dateTime={item.dateTime} className={cn(\"text-xs text-muted-foreground tabular-nums\", classNames?.time)}>\n                    {item.time}\n                  </time>\n                )}\n              </div>\n              {item.description && <div className={cn(\"mt-1 text-sm text-muted-foreground\", classNames?.description)}>{item.description}</div>}\n            </div>\n          </li>\n        )\n      })}\n    </ol>\n  )\n}\n\nexport { Timeline, type TimelineProps, type TimelineItem, type TimelineColor }\n"}],"cssVars":{"theme":{"color-primary-text":"var(--primary-text, var(--primary))","color-destructive-foreground":"var(--destructive-foreground)","color-destructive-text":"var(--destructive-text)","color-info":"var(--info)","color-info-foreground":"var(--info-foreground)","color-info-text":"var(--info-text)","color-success":"var(--success)","color-success-foreground":"var(--success-foreground)","color-success-text":"var(--success-text)","color-warning":"var(--warning)","color-warning-foreground":"var(--warning-foreground)","color-warning-text":"var(--warning-text)","color-highlight":"var(--highlight)","color-highlight-foreground":"var(--highlight-foreground)","color-highlight-text":"var(--highlight-text)"},"light":{"destructive-foreground":"oklch(0.985 0 0)","destructive-text":"#bd2800","info":"oklch(0.5 0.134 242.749)","info-foreground":"oklch(0.985 0 0)","info-text":"oklch(0.443 0.11 240.79)","success":"oklch(0.508 0.118 165.612)","success-foreground":"oklch(0.985 0 0)","success-text":"oklch(0.432 0.095 166.913)","warning":"#f59200","warning-foreground":"oklch(0.3 0.07 60)","warning-text":"#964e00","highlight":"#b8008c","highlight-foreground":"oklch(0.985 0 0)","highlight-text":"#a3007b"},"dark":{"destructive-foreground":"oklch(0.2 0 0)","destructive-text":"#ff8a5c","info":"oklch(0.685 0.169 237.323)","info-foreground":"oklch(0.2 0 0)","info-text":"oklch(0.746 0.16 232.661)","success":"oklch(0.696 0.17 162.48)","success-foreground":"oklch(0.2 0.04 160)","success-text":"oklch(0.765 0.177 163.223)","warning":"#f59200","warning-foreground":"oklch(0.25 0.06 60)","warning-text":"#ffba42","highlight":"#ff2ecf","highlight-foreground":"oklch(0.2 0 0)","highlight-text":"#ff75df"}}}