{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "timeline",
  "title": "Timeline",
  "description": "Events on a line, past ones in the brand hue.",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "https://ui.ndekocode.dev/r/theme.json",
    "https://ui.ndekocode.dev/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/ndeko/ui/timeline.tsx",
      "content": "'use client';\n\nimport { mergeProps } from '@base-ui/react/merge-props';\nimport { useRender } from '@base-ui/react/use-render';\nimport {\n  Children,\n  createContext,\n  useCallback,\n  useContext,\n  useState,\n} from 'react';\n\nimport { cn } from '@/registry/ndeko/lib/utils';\n\ntype TimelineContextValue = {\n  activeStep: number;\n  setActiveStep: (step: number) => void;\n};\n\nconst TimelineContext = createContext<TimelineContextValue | undefined>(\n  undefined\n);\n\nfunction useTimeline() {\n  const context = useContext(TimelineContext);\n  if (!context) {\n    throw new Error('useTimeline must be used within a Timeline');\n  }\n  return context;\n}\n\ntype TimelineProps = React.ComponentProps<'div'> & {\n  defaultValue?: number;\n  value?: number;\n  onValueChange?: (value: number) => void;\n  orientation?: 'horizontal' | 'vertical';\n};\n\nfunction Timeline({\n  defaultValue = 1,\n  value,\n  onValueChange,\n  orientation = 'vertical',\n  className,\n  ...props\n}: TimelineProps) {\n  const [internalStep, setInternalStep] = useState(defaultValue);\n  const isControlled = value !== undefined;\n  const activeStep = value ?? internalStep;\n\n  const setActiveStep = useCallback(\n    (step: number) => {\n      if (!isControlled) {\n        setInternalStep(step);\n      }\n      onValueChange?.(step);\n    },\n    [isControlled, onValueChange]\n  );\n\n  return (\n    <TimelineContext.Provider value={{ activeStep, setActiveStep }}>\n      <div\n        data-slot='timeline'\n        data-orientation={orientation}\n        className={cn(\n          'group/timeline flex data-[orientation=horizontal]:w-full data-[orientation=horizontal]:flex-row data-[orientation=vertical]:flex-col',\n          className\n        )}\n        {...props}\n      />\n    </TimelineContext.Provider>\n  );\n}\n\nfunction TimelineContent({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot='timeline-content'\n      className={cn('text-muted-foreground text-[13px]', className)}\n      {...props}\n    />\n  );\n}\n\nfunction TimelineDate({\n  className,\n  render,\n  ...props\n}: useRender.ComponentProps<'time'>) {\n  const defaultProps = {\n    'data-slot': 'timeline-date',\n    className: cn(\n      'mb-1 block text-xs font-medium tabular-nums text-muted-foreground group-data-[orientation=vertical]/timeline:max-sm:h-4',\n      className\n    ),\n  };\n\n  return useRender({\n    defaultTagName: 'time',\n    render,\n    props: mergeProps<'time'>(defaultProps, props),\n  });\n}\n\nfunction TimelineHeader({ className, ...props }: React.ComponentProps<'div'>) {\n  return (\n    <div data-slot='timeline-header' className={cn(className)} {...props} />\n  );\n}\n\nconst INDICATOR = [\n  'border-foreground/15 bg-card absolute size-4 rounded-full border-2',\n  'transition-[background-color,border-color,color,scale] duration-200 ease-(--ease-out-strong)',\n  'group-data-completed/timeline-item:border-primary group-data-completed/timeline-item:duration-300',\n  'group-data-[orientation=horizontal]/timeline:-top-6 group-data-[orientation=horizontal]/timeline:left-0 group-data-[orientation=horizontal]/timeline:-translate-y-1/2',\n  'group-data-[orientation=vertical]/timeline:top-0 group-data-[orientation=vertical]/timeline:-left-6 group-data-[orientation=vertical]/timeline:-translate-x-1/2',\n].join(' ');\n\nconst EMPTY_DOT =\n  'scale-75 group-data-completed/timeline-item:bg-primary group-data-completed/timeline-item:scale-100';\n\nfunction hasNoContent(children: React.ReactNode) {\n  const contentNodes = Children.toArray(children);\n  return contentNodes.length === 0;\n}\n\n/**\n * Without children it is a dot: a neutral ring ahead, the brand fill growing into place once its event is done.\n * With children (an icon, an avatar) it is a frame and the content decides its fill.\n */\nfunction TimelineIndicator({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<'div'>) {\n  const isEmptyDot = hasNoContent(children);\n\n  return (\n    <div\n      data-slot='timeline-indicator'\n      aria-hidden='true'\n      className={cn(INDICATOR, isEmptyDot && EMPTY_DOT, className)}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n\ntype TimelineItemProps = React.ComponentProps<'div'> & {\n  step: number;\n};\n\nfunction TimelineItem({ step, className, ...props }: TimelineItemProps) {\n  const { activeStep } = useTimeline();\n  const isCompleted = step <= activeStep;\n\n  return (\n    <div\n      data-slot='timeline-item'\n      data-completed={isCompleted || undefined}\n      className={cn(\n        'group/timeline-item has-[+[data-completed]]:[&_[data-slot=timeline-separator]]:bg-primary relative flex flex-1 flex-col gap-0.5',\n        'group-data-[orientation=horizontal]/timeline:mt-8 group-data-[orientation=horizontal]/timeline:not-last:pe-8',\n        'group-data-[orientation=vertical]/timeline:ms-8 group-data-[orientation=vertical]/timeline:not-last:pb-12',\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction TimelineSeparator({\n  className,\n  ...props\n}: React.ComponentProps<'div'>) {\n  return (\n    <div\n      data-slot='timeline-separator'\n      aria-hidden='true'\n      className={cn(\n        'bg-foreground/10 absolute self-start rounded-full transition-colors duration-300 group-last/timeline-item:hidden',\n        'group-data-[orientation=horizontal]/timeline:-top-6 group-data-[orientation=horizontal]/timeline:h-0.5 group-data-[orientation=horizontal]/timeline:w-[calc(100%-1rem-0.25rem)] group-data-[orientation=horizontal]/timeline:translate-x-4.5 group-data-[orientation=horizontal]/timeline:-translate-y-1/2',\n        'group-data-[orientation=vertical]/timeline:-left-6 group-data-[orientation=vertical]/timeline:h-[calc(100%-1rem-0.25rem)] group-data-[orientation=vertical]/timeline:w-0.5 group-data-[orientation=vertical]/timeline:-translate-x-1/2 group-data-[orientation=vertical]/timeline:translate-y-4.5',\n        className\n      )}\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('text-foreground text-[13px] font-medium', className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Timeline,\n  TimelineContent,\n  TimelineDate,\n  TimelineHeader,\n  TimelineIndicator,\n  TimelineItem,\n  TimelineSeparator,\n  TimelineTitle,\n  useTimeline,\n};\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "primitives"
  ],
  "type": "registry:ui"
}