{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "timeline",
  "title": "时间线",
  "description": "可组合的时间线原语，支持经历、活动流、项目进度、水平路线图与交替更新日志。",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "https://yiui.dev/r/flow-tone.json"
  ],
  "files": [
    {
      "path": "src/registry/ui/timeline/timeline.tsx",
      "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\nimport {\n  flowToneStyle,\n  type FlowToneName,\n} from \"@/lib/flow-tone\"\nimport \"./timeline.css\"\n\ntype TimelineStatus = \"done\" | \"current\" | \"upcoming\"\ntype TimelineOrientation = \"vertical\" | \"horizontal\"\ntype TimelineAlign = \"start\" | \"alternate\"\n\nfunction Timeline({\n  className,\n  orientation = \"vertical\",\n  align = \"start\",\n  ...props\n}: React.ComponentProps<\"ol\"> & {\n  orientation?: TimelineOrientation\n  /** 仅竖向时间线在宽屏交替排列。 */\n  align?: TimelineAlign\n}) {\n  return (\n    <ol\n      data-slot=\"timeline\"\n      data-orientation={orientation}\n      data-align={align}\n      className={cn(\"isolate m-0 w-full list-none p-0\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction TimelineItem({\n  className,\n  status = \"done\",\n  time,\n  children,\n  ...props\n}: React.ComponentProps<\"li\"> & {\n  status?: TimelineStatus\n  /** 左侧编号栏；建议传入 TimelineTime。 */\n  time?: React.ReactNode\n}) {\n  return (\n    <li\n      data-slot=\"timeline-item\"\n      data-status={status}\n      data-time={time ? \"column\" : undefined}\n      aria-current={status === \"current\" ? \"step\" : undefined}\n      className={cn(\"group/timeline-item relative min-w-0\", className)}\n      {...props}\n    >\n      {time ? <div data-slot=\"timeline-item-time\">{time}</div> : null}\n      {children}\n    </li>\n  )\n}\n\nconst timelineMarkerVariants = cva(\n  \"relative z-10 flex size-8 shrink-0 items-center justify-center self-start rounded-full border border-edge bg-background font-mono text-xs text-(--flow-tone) [&_svg]:size-3.5\",\n  {\n    variants: {\n      status: {\n        done: \"\",\n        current: \"ring-4 ring-(--flow-tone)/10 motion-safe:animate-pulse\",\n        upcoming: \"border-dashed text-muted-foreground\",\n      },\n    },\n    defaultVariants: { status: \"done\" },\n  },\n)\n\nfunction TimelineMarker({\n  className,\n  tone = \"primary\",\n  status,\n  style,\n  children,\n  label,\n  ...props\n}: React.ComponentProps<\"span\"> &\n  VariantProps<typeof timelineMarkerVariants> & {\n    tone?: FlowToneName\n    /** 有语义的标记请提供文字说明。 */\n    label?: string\n  }) {\n  return (\n    <span\n      data-slot=\"timeline-marker\"\n      data-status={status ?? undefined}\n      role={label ? \"img\" : undefined}\n      aria-label={label}\n      aria-hidden={label ? undefined : true}\n      className={cn(timelineMarkerVariants({ status }), className)}\n      style={{ ...flowToneStyle(tone), ...style }}\n      {...props}\n    >\n      {children ?? (\n        <span\n          data-slot=\"timeline-dot\"\n          className=\"size-2 rounded-full bg-current\"\n        />\n      )}\n    </span>\n  )\n}\n\nfunction TimelineConnector({\n  className,\n  ...props\n}: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"timeline-connector\"\n      aria-hidden=\"true\"\n      className={cn(\"pointer-events-none border-edge\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction TimelineContent({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"timeline-content\"\n      className={cn(\"min-w-0 pt-1 pb-8\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction TimelineTitle({ className, ...props }: React.ComponentProps<\"h3\">) {\n  return (\n    <h3\n      data-slot=\"timeline-title\"\n      className={cn(\n        \"m-0 text-sm leading-6 font-medium tracking-tight\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TimelineTime({ className, ...props }: React.ComponentProps<\"time\">) {\n  return (\n    <time\n      data-slot=\"timeline-time\"\n      className={cn(\n        \"shrink-0 font-mono text-xs leading-6 text-muted-foreground tabular-nums\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TimelineDescription({\n  className,\n  ...props\n}: React.ComponentProps<\"p\">) {\n  return (\n    <p\n      data-slot=\"timeline-description\"\n      className={cn(\n        \"mt-1 mb-0 text-[13px] leading-7 text-muted-foreground\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction TimelineAttachment({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"timeline-attachment\"\n      className={cn(\n        \"mt-3 flex flex-wrap items-center gap-2 text-xs\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Timeline,\n  TimelineItem,\n  TimelineMarker,\n  TimelineConnector,\n  TimelineContent,\n  TimelineTitle,\n  TimelineTime,\n  TimelineDescription,\n  TimelineAttachment,\n  timelineMarkerVariants,\n}\nexport type { TimelineStatus, TimelineOrientation, TimelineAlign }\n",
      "type": "registry:ui",
      "target": "@ui/timeline/timeline.tsx"
    },
    {
      "path": "src/registry/ui/timeline/timeline.css",
      "content": "[data-slot=\"timeline-item\"] {\n  display: grid;\n  grid-template-columns: 2rem minmax(0, 1fr);\n  column-gap: 1rem;\n  animation: timeline-enter 250ms ease-out both;\n}\n[data-slot=\"timeline-marker\"] {\n  grid-column: 1;\n  grid-row: 1;\n}\n[data-slot=\"timeline-content\"] {\n  grid-column: 2;\n  grid-row: 1;\n}\n[data-slot=\"timeline-item-time\"] {\n  padding-top: 0.25rem;\n  grid-column: 1;\n  grid-row: 1;\n}\n[data-slot=\"timeline-item\"][data-time=\"column\"] {\n  grid-template-columns: 3.5rem 2rem minmax(0, 1fr);\n}\n[data-time=\"column\"] > [data-slot=\"timeline-marker\"] {\n  grid-column: 2;\n}\n[data-time=\"column\"] > [data-slot=\"timeline-content\"] {\n  grid-column: 3;\n}\n[data-slot=\"timeline-connector\"] {\n  position: absolute;\n  left: 1rem;\n  top: 2rem;\n  bottom: 0;\n  border-left-width: 1px;\n}\n[data-time=\"column\"] > [data-slot=\"timeline-connector\"] {\n  left: 5.5rem;\n}\n[data-slot=\"timeline-item\"]:last-child > [data-slot=\"timeline-connector\"] {\n  display: none;\n}\n[data-slot=\"timeline-item\"]:last-child > [data-slot=\"timeline-content\"] {\n  padding-bottom: 0;\n}\n[data-slot=\"timeline-item\"][data-status=\"current\"]\n  > [data-slot=\"timeline-connector\"],\n[data-slot=\"timeline-item\"][data-status=\"upcoming\"]\n  > [data-slot=\"timeline-connector\"] {\n  border-style: dashed;\n}\n[data-slot=\"timeline-item\"][data-status=\"current\"]\n  > [data-slot=\"timeline-marker\"]:not([data-status]) {\n  outline: 4px solid color-mix(in oklch, var(--flow-tone) 12%, transparent);\n}\n[data-slot=\"timeline-item\"][data-status=\"upcoming\"]\n  > [data-slot=\"timeline-marker\"]:not([data-status]) {\n  border-style: dashed;\n  color: var(--muted-foreground);\n}\n@media (min-width: 640px) {\n  [data-slot=\"timeline\"][data-orientation=\"horizontal\"] {\n    display: flex;\n  }\n  [data-orientation=\"horizontal\"] > [data-slot=\"timeline-item\"] {\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    gap: 1rem;\n  }\n  [data-orientation=\"horizontal\"]\n    > [data-slot=\"timeline-item\"]\n    > [data-slot=\"timeline-content\"] {\n    padding: 0 1rem 0 0;\n  }\n  [data-orientation=\"horizontal\"]\n    > [data-slot=\"timeline-item\"]\n    > [data-slot=\"timeline-connector\"] {\n    top: 1rem;\n    left: 2rem;\n    right: 0;\n    bottom: auto;\n    border-left-width: 0;\n    border-top-width: 1px;\n  }\n  [data-orientation=\"vertical\"][data-align=\"alternate\"]\n    > [data-slot=\"timeline-item\"] {\n    grid-template-columns: minmax(0, 1fr) 2rem minmax(0, 1fr);\n  }\n  [data-orientation=\"vertical\"][data-align=\"alternate\"]\n    > [data-slot=\"timeline-item\"]\n    > [data-slot=\"timeline-marker\"] {\n    grid-column: 2;\n  }\n  [data-orientation=\"vertical\"][data-align=\"alternate\"]\n    > [data-slot=\"timeline-item\"]\n    > [data-slot=\"timeline-content\"] {\n    grid-column: 3;\n  }\n  [data-orientation=\"vertical\"][data-align=\"alternate\"]\n    > [data-slot=\"timeline-item\"]:nth-child(even)\n    > [data-slot=\"timeline-content\"] {\n    grid-column: 1;\n    text-align: right;\n  }\n  [data-orientation=\"vertical\"][data-align=\"alternate\"]\n    > [data-slot=\"timeline-item\"]\n    > [data-slot=\"timeline-connector\"] {\n    left: 50%;\n  }\n  [data-orientation=\"vertical\"][data-align=\"alternate\"]\n    > [data-slot=\"timeline-item\"]\n    > [data-slot=\"timeline-item-time\"] {\n    grid-column: 1;\n    text-align: right;\n  }\n  [data-orientation=\"vertical\"][data-align=\"alternate\"]\n    > [data-slot=\"timeline-item\"]:nth-child(even)\n    > [data-slot=\"timeline-item-time\"] {\n    grid-column: 3;\n    text-align: left;\n  }\n}\n@keyframes timeline-enter {\n  from {\n    transform: translateY(6px);\n  }\n  to {\n    transform: translateY(0);\n  }\n}\n@media (prefers-reduced-motion: reduce) {\n  [data-slot=\"timeline-item\"] {\n    animation: none;\n  }\n}\n",
      "type": "registry:ui",
      "target": "@ui/timeline/timeline.css"
    }
  ],
  "cssVars": {
    "theme": {
      "color-edge": "var(--edge)"
    },
    "light": {
      "edge": "color-mix(in oklch, var(--foreground) 12%, transparent)",
      "flow-blue": "oklch(0.56 0.2 258)",
      "flow-green": "oklch(0.62 0.16 150)",
      "flow-amber": "oklch(0.7 0.16 70)",
      "flow-rose": "oklch(0.62 0.21 5)",
      "flow-violet": "oklch(0.56 0.22 292)",
      "flow-cyan": "oklch(0.66 0.12 215)"
    },
    "dark": {
      "edge": "color-mix(in oklch, var(--foreground) 18%, transparent)",
      "flow-blue": "oklch(0.72 0.15 255)",
      "flow-green": "oklch(0.77 0.17 152)",
      "flow-amber": "oklch(0.82 0.15 82)",
      "flow-rose": "oklch(0.74 0.16 8)",
      "flow-violet": "oklch(0.74 0.15 295)",
      "flow-cyan": "oklch(0.82 0.11 208)"
    }
  },
  "type": "registry:ui"
}
