{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stepper",
  "type": "registry:component",
  "title": "Stepper",
  "description": "Sequenced steps with complete, current, and upcoming states.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/stepper/stepper.tsx",
      "content": "import { Check, Circle } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\nexport type StepperStep = {\n  description?: string;\n  id: string;\n  meta?: string;\n  title: string;\n};\n\nexport type StepperProps = {\n  className?: string;\n  currentStep: number;\n  onStepClick?: (step: StepperStep, stepIndex: number) => void;\n  orientation?: \"horizontal\" | \"vertical\";\n  showNumbers?: boolean;\n  steps: StepperStep[];\n};\n\ntype StepperItemProps = {\n  index: number;\n  isVertical: boolean;\n  onStepClick?: (step: StepperStep, stepIndex: number) => void;\n  showNumbers: boolean;\n  step: StepperStep;\n  stepState: \"complete\" | \"current\" | \"upcoming\";\n  totalSteps: number;\n};\n\nfunction getStepState(\n  index: number,\n  currentStep: number,\n): \"complete\" | \"current\" | \"upcoming\" {\n  const stepNumber = index + 1;\n\n  if (stepNumber < currentStep) return \"complete\";\n  if (stepNumber === currentStep) return \"current\";\n  return \"upcoming\";\n}\n\nfunction StepIcon({\n  showNumbers,\n  state,\n  stepNumber,\n}: {\n  showNumbers: boolean;\n  state: \"complete\" | \"current\" | \"upcoming\";\n  stepNumber: number;\n}): ReactNode {\n  if (state === \"complete\") {\n    return <Check className=\"size-4\" />;\n  }\n\n  if (!showNumbers) {\n    return <Circle className=\"size-3.5 fill-current stroke-none\" />;\n  }\n\n  return <span className=\"text-xs font-semibold\">{stepNumber}</span>;\n}\n\nfunction StepperItem({\n  index,\n  isVertical,\n  onStepClick,\n  showNumbers,\n  step,\n  stepState,\n  totalSteps,\n}: StepperItemProps): ReactNode {\n  const clickable = Boolean(onStepClick);\n  const stepNumber = index + 1;\n\n  return (\n    <li className={cn(\"relative\", !isVertical && \"min-w-0\")}>\n      {!isVertical && index < totalSteps - 1 ? (\n        <div className=\"absolute left-[calc(50%+1rem)] right-[calc(-50%+1rem)] top-4 hidden h-px bg-border md:block\" />\n      ) : null}\n      <button\n        className={cn(\n          \"flex w-full items-start gap-3 rounded-lg text-left transition-colors\",\n          clickable\n            ? \"hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n            : \"cursor-default\",\n          isVertical ? \"p-2\" : \"flex-col items-start p-2 md:p-0\",\n        )}\n        disabled={!clickable}\n        onClick={() => {\n          onStepClick?.(step, index);\n        }}\n        type=\"button\"\n      >\n        <span\n          aria-current={stepState === \"current\" ? \"step\" : undefined}\n          className={cn(\n            \"relative z-10 flex size-8 items-center justify-center rounded-full border text-sm transition-colors\",\n            stepState === \"complete\" &&\n              \"border-primary bg-primary text-primary-foreground\",\n            stepState === \"current\" &&\n              \"border-primary bg-primary/10 text-primary shadow-sm\",\n            stepState === \"upcoming\" &&\n              \"border-border bg-background text-muted-foreground\",\n          )}\n        >\n          <StepIcon\n            showNumbers={showNumbers}\n            state={stepState}\n            stepNumber={stepNumber}\n          />\n        </span>\n        <span className=\"min-w-0 space-y-1\">\n          <span className=\"flex flex-wrap items-center gap-2\">\n            <span className=\"text-sm font-medium text-foreground\">\n              {step.title}\n            </span>\n            {step.meta ? (\n              <span className=\"rounded-full bg-muted px-2 py-0.5 text-[11px] font-medium text-muted-foreground\">\n                {step.meta}\n              </span>\n            ) : null}\n          </span>\n          {step.description ? (\n            <span className=\"block text-sm text-muted-foreground\">\n              {step.description}\n            </span>\n          ) : null}\n        </span>\n      </button>\n    </li>\n  );\n}\n\nexport function Stepper({\n  className,\n  currentStep,\n  onStepClick,\n  orientation = \"horizontal\",\n  showNumbers = true,\n  steps,\n}: StepperProps): ReactNode {\n  const isVertical = orientation === \"vertical\";\n\n  if (steps.length === 0) {\n    return null;\n  }\n\n  return (\n    <div className={cn(\"my-6 rounded-xl border bg-card p-4\", className)}>\n      <ol\n        className={cn(\"gap-3\", isVertical ? \"flex flex-col\" : \"grid gap-4\")}\n        style={\n          isVertical\n            ? undefined\n            : { gridTemplateColumns: `repeat(${steps.length}, minmax(0, 1fr))` }\n        }\n      >\n        {steps.map((step, index) => (\n          <StepperItem\n            index={index}\n            isVertical={isVertical}\n            key={step.id}\n            onStepClick={onStepClick}\n            showNumbers={showNumbers}\n            step={step}\n            stepState={getStepState(index, currentStep)}\n            totalSteps={steps.length}\n          />\n        ))}\n      </ol>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
