{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stepper",
  "title": "Stepper",
  "description": "A lightweight, composable stepper primitive for shadcn/ui-style multi-step flows.",
  "dependencies": [
    "@radix-ui/react-slot"
  ],
  "files": [
    {
      "path": "registry/default/ui/stepper.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\n\nimport { cn } from \"@/lib/utils\";\n\n// ----------------------------------------------------------------------------\n// Types\n// ----------------------------------------------------------------------------\n\ntype StepperOrientation = \"horizontal\" | \"vertical\";\ntype StepperValue = string;\ntype StepperStepPosition = \"previous\" | \"current\" | \"next\";\ntype StepperStepState =\n  | \"inactive\"\n  | \"active\"\n  | \"completed\"\n  | \"disabled\"\n  | \"error\";\n\ntype StepperStepInput<TValue extends StepperValue = StepperValue> = {\n  value: TValue;\n  disabled?: boolean;\n};\n\ntype StepperStep<TValue extends StepperValue = StepperValue> = {\n  value: TValue;\n  disabled: boolean;\n};\n\ntype StepperStepsValue<TSteps extends readonly StepperStepInput[]> =\n  TSteps[number][\"value\"];\n\ntype RegisteredStep<TValue extends StepperValue = StepperValue> =\n  StepperStep<TValue> & {\n    id: string;\n  };\n\ntype StepperNavigationGuard = () => boolean | Promise<boolean>;\n\ntype StepperApi<TValue extends StepperValue = StepperValue> = {\n  value: TValue | undefined;\n  orientation: StepperOrientation;\n  steps: StepperStep<TValue>[];\n  currentIndex: number;\n  totalSteps: number;\n  setValue: (value: TValue) => void;\n  getStepIndex: (value: TValue) => number;\n  canGoPrevious: boolean;\n  canGoNext: boolean;\n  goPrevious: () => void;\n  goNext: () => void;\n};\n\ntype StepperItemApi<TValue extends StepperValue = StepperValue> = {\n  value: TValue;\n  index: number;\n  disabled: boolean;\n  completed: boolean;\n  isActive: boolean;\n  stepState: StepperStepState;\n  stepPosition: StepperStepPosition;\n  orientation: StepperOrientation;\n  setValue: (value: TValue) => void;\n  triggerId: string;\n  contentId: string;\n};\n\ntype StepperContextValue<TValue extends StepperValue = StepperValue> =\n  StepperApi<TValue> & {\n    isExplicitMode: boolean;\n    transitionFromIndex: number;\n    transitionToIndex: number;\n    registerStep: (step: RegisteredStep) => void;\n    unregisterStep: (id: string) => void;\n    getTriggerId: (value: TValue) => string;\n    getContentId: (value: TValue) => string;\n  };\n\ntype StepperItemContextValue<TValue extends StepperValue = StepperValue> =\n  StepperItemApi<TValue>;\n\ntype StepperProps<TValue extends StepperValue = StepperValue> =\n  React.ComponentPropsWithoutRef<\"div\"> & {\n    value?: TValue;\n    defaultValue?: TValue;\n    onValueChange?: (value: TValue) => void;\n    orientation?: StepperOrientation;\n    steps?: readonly StepperStepInput<TValue>[];\n  };\n\ntype StepperPropsWithSteps<\n  TSteps extends readonly StepperStepInput[],\n> = Omit<StepperProps<StepperStepsValue<TSteps>>, \"steps\"> & {\n  steps: TSteps;\n};\n\ntype StepperPropsWithoutSteps<\n  TValue extends StepperValue = StepperValue,\n> = Omit<StepperProps<TValue>, \"steps\"> & {\n  steps?: undefined;\n};\n\ntype StepperListProps = React.ComponentPropsWithoutRef<\"ol\">;\n\ntype StepperItemProps<TValue extends StepperValue = StepperValue> = Omit<\n  React.ComponentPropsWithoutRef<\"li\">,\n  \"value\"\n> & {\n  value: TValue;\n  completed?: boolean;\n  defaultTrigger?: boolean;\n  disabled?: boolean;\n  error?: boolean;\n  separator?: boolean;\n};\n\ntype StepperTriggerProps = React.ComponentPropsWithoutRef<\"button\"> & {\n  asChild?: boolean;\n};\n\ntype StepperIndicatorProps = React.ComponentPropsWithoutRef<\"span\">;\n\ntype StepperLabelProps = React.ComponentPropsWithoutRef<\"span\">;\n\ntype StepperDescriptionProps = React.ComponentPropsWithoutRef<\"span\">;\n\ntype StepperSeparatorProps = React.ComponentPropsWithoutRef<\"span\">;\n\ntype StepperContentProps<TValue extends StepperValue = StepperValue> =\n  React.ComponentPropsWithoutRef<\"div\"> & {\n    value: TValue;\n    forceMount?: boolean;\n    keepMounted?: boolean;\n    asChild?: boolean;\n  };\n\ntype StepperButtonProps = React.ComponentPropsWithoutRef<\"button\"> & {\n  asChild?: boolean;\n};\n\ntype StepperPreviousProps = StepperButtonProps & {\n  onBeforePrevious?: StepperNavigationGuard;\n};\n\ntype StepperNextProps = StepperButtonProps & {\n  onBeforeNext?: StepperNavigationGuard;\n};\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\nconst isDevelopment = process.env.NODE_ENV !== \"production\";\n\nfunction warnDev(message: string) {\n  if (isDevelopment) {\n    console.warn(message);\n  }\n}\n\nfunction getSafeId(value: StepperValue) {\n  return value.replace(/[^a-zA-Z0-9_-]/g, \"-\");\n}\n\nfunction normalizeStep<TValue extends StepperValue>(\n  step: StepperStepInput<TValue>\n): StepperStep<TValue> {\n  return {\n    value: step.value,\n    disabled: Boolean(step.disabled),\n  };\n}\n\nfunction normalizeSteps<TValue extends StepperValue>(\n  steps: readonly StepperStepInput<TValue>[] | undefined\n) {\n  return steps?.map(normalizeStep) ?? [];\n}\n\nfunction getDuplicateStepValues<TValue extends StepperValue>(\n  steps: ReadonlyArray<{ value: TValue }>\n) {\n  const seenValues = new Set<TValue>();\n  const duplicateValues = new Set<TValue>();\n\n  steps.forEach((step) => {\n    if (seenValues.has(step.value)) {\n      duplicateValues.add(step.value);\n      return;\n    }\n\n    seenValues.add(step.value);\n  });\n\n  return Array.from(duplicateValues);\n}\n\nfunction getStepByValue<TValue extends StepperValue>(\n  steps: readonly StepperStep<TValue>[],\n  value: TValue | undefined\n) {\n  return steps.find((step) => step.value === value);\n}\n\nfunction getCurrentStepValue<TValue extends StepperValue>(\n  steps: readonly StepperStep<TValue>[],\n  selectedValue: TValue | undefined\n) {\n  const selectedStep = getStepByValue(steps, selectedValue);\n\n  if (selectedStep && !selectedStep.disabled) {\n    return selectedStep.value;\n  }\n\n  return steps.find((step) => !step.disabled)?.value;\n}\n\nfunction getNextEnabledStep<TValue extends StepperValue>(\n  steps: readonly StepperStep<TValue>[],\n  currentValue: TValue | undefined\n) {\n  const currentIndex = steps.findIndex((step) => step.value === currentValue);\n\n  if (currentIndex === -1) {\n    return undefined;\n  }\n\n  return steps.slice(currentIndex + 1).find((step) => !step.disabled);\n}\n\nfunction getPreviousEnabledStep<TValue extends StepperValue>(\n  steps: readonly StepperStep<TValue>[],\n  currentValue: TValue | undefined\n) {\n  const currentIndex = steps.findIndex((step) => step.value === currentValue);\n\n  if (currentIndex === -1) {\n    return undefined;\n  }\n\n  return steps\n    .slice(0, currentIndex)\n    .reverse()\n    .find((step) => !step.disabled);\n}\n\nfunction getKeyboardNavigationStepValue<TValue extends StepperValue>({\n  key,\n  orientation,\n  steps,\n  value,\n}: {\n  key: string;\n  orientation: StepperOrientation;\n  steps: readonly StepperStep<TValue>[];\n  value: TValue;\n}) {\n  const enabledSteps = steps.filter((step) => !step.disabled);\n  const currentIndex = enabledSteps.findIndex((step) => step.value === value);\n\n  if (currentIndex < 0) {\n    return undefined;\n  }\n\n  if (key === \"Home\") {\n    return enabledSteps.at(0)?.value;\n  }\n\n  if (key === \"End\") {\n    return enabledSteps.at(-1)?.value;\n  }\n\n  if (\n    (orientation === \"horizontal\" && key === \"ArrowRight\") ||\n    (orientation === \"vertical\" && key === \"ArrowDown\")\n  ) {\n    return enabledSteps[Math.min(currentIndex + 1, enabledSteps.length - 1)]\n      ?.value;\n  }\n\n  if (\n    (orientation === \"horizontal\" && key === \"ArrowLeft\") ||\n    (orientation === \"vertical\" && key === \"ArrowUp\")\n  ) {\n    return enabledSteps[Math.max(currentIndex - 1, 0)]?.value;\n  }\n\n  return undefined;\n}\n\nasync function resolveNavigationGuard(\n  guard: StepperNavigationGuard | undefined\n) {\n  if (!guard) {\n    return true;\n  }\n\n  return (await guard()) !== false;\n}\n\nfunction setDisplayName(component: object, displayName: string) {\n  (component as { displayName?: string }).displayName = displayName;\n}\n\n// ----------------------------------------------------------------------------\n// Context\n// ----------------------------------------------------------------------------\n\nconst StepperContext = React.createContext<StepperContextValue | null>(null);\nconst StepperItemContext =\n  React.createContext<StepperItemContextValue | null>(null);\nconst StepperListContext = React.createContext(false);\n\nfunction useStepperContext(component: string) {\n  const context = React.useContext(StepperContext);\n\n  if (!context) {\n    throw new Error(`${component} must be used within Stepper`);\n  }\n\n  return context;\n}\n\nfunction useStepperItemContext(component: string) {\n  const context = React.useContext(StepperItemContext);\n\n  if (!context) {\n    throw new Error(`${component} must be used within StepperItem`);\n  }\n\n  return context;\n}\n\nfunction useStepperListContext() {\n  return React.useContext(StepperListContext);\n}\n\n// ----------------------------------------------------------------------------\n// Internal Hooks\n// ----------------------------------------------------------------------------\n\nfunction useRegisteredSteps() {\n  const [registeredSteps, setRegisteredSteps] = React.useState<\n    RegisteredStep[]\n  >([]);\n\n  const registerStep = React.useCallback((step: RegisteredStep) => {\n    setRegisteredSteps((currentSteps) => {\n      const existingStepIndex = currentSteps.findIndex(\n        (currentStep) => currentStep.id === step.id\n      );\n\n      if (existingStepIndex === -1) {\n        return [...currentSteps, step];\n      }\n\n      const existingStep = currentSteps[existingStepIndex];\n\n      if (\n        existingStep.value === step.value &&\n        existingStep.disabled === step.disabled\n      ) {\n        return currentSteps;\n      }\n\n      const nextSteps = [...currentSteps];\n      nextSteps[existingStepIndex] = step;\n\n      return nextSteps;\n    });\n  }, []);\n\n  const unregisterStep = React.useCallback((stepId: string) => {\n    setRegisteredSteps((currentSteps) =>\n      currentSteps.filter((step) => step.id !== stepId)\n    );\n  }, []);\n\n  return {\n    registeredSteps,\n    registerStep,\n    unregisterStep,\n  };\n}\n\nfunction useStepperSteps(\n  stepsProp: readonly StepperStepInput[] | undefined,\n  registeredSteps: RegisteredStep[]\n) {\n  const isExplicitMode = stepsProp !== undefined;\n  const explicitSteps = React.useMemo(\n    () => (isExplicitMode ? normalizeSteps(stepsProp) : undefined),\n    [isExplicitMode, stepsProp]\n  );\n  const steps = React.useMemo(\n    () =>\n      explicitSteps ??\n      registeredSteps.map((step) => ({\n        value: step.value,\n        disabled: step.disabled,\n      })),\n    [explicitSteps, registeredSteps]\n  );\n\n  return {\n    isExplicitMode,\n    steps,\n  };\n}\n\nfunction useStepperValue({\n  value,\n  defaultValue,\n  onValueChange,\n  steps,\n}: {\n  value: StepperValue | undefined;\n  defaultValue: StepperValue | undefined;\n  onValueChange: ((value: StepperValue) => void) | undefined;\n  steps: StepperStep[];\n}) {\n  const isControlled = value !== undefined;\n  const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);\n  const selectedValue = isControlled ? value : uncontrolledValue;\n  const currentValue = React.useMemo(\n    () => getCurrentStepValue(steps, selectedValue),\n    [selectedValue, steps]\n  );\n  const currentIndex = React.useMemo(\n    () =>\n      currentValue === undefined\n        ? -1\n        : steps.findIndex((step) => step.value === currentValue),\n    [currentValue, steps]\n  );\n\n  const setValue = React.useCallback(\n    (nextValue: StepperValue) => {\n      const nextStepRecord = getStepByValue(steps, nextValue);\n\n      if (!nextStepRecord || nextStepRecord.disabled) {\n        return;\n      }\n\n      if (!isControlled) {\n        setUncontrolledValue(nextValue);\n      }\n\n      onValueChange?.(nextValue);\n    },\n    [isControlled, onValueChange, steps]\n  );\n\n  return {\n    currentValue,\n    currentIndex,\n    setValue,\n  };\n}\n\nfunction useStepperNavigation({\n  currentValue,\n  setValue,\n  steps,\n}: {\n  currentValue: StepperValue | undefined;\n  setValue: (value: StepperValue) => void;\n  steps: StepperStep[];\n}) {\n  const previousStep = React.useMemo(\n    () => getPreviousEnabledStep(steps, currentValue),\n    [currentValue, steps]\n  );\n  const nextStep = React.useMemo(\n    () => getNextEnabledStep(steps, currentValue),\n    [currentValue, steps]\n  );\n  const goPrevious = React.useCallback(() => {\n    if (previousStep) {\n      setValue(previousStep.value);\n    }\n  }, [previousStep, setValue]);\n  const goNext = React.useCallback(() => {\n    if (nextStep) {\n      setValue(nextStep.value);\n    }\n  }, [nextStep, setValue]);\n\n  return {\n    canGoPrevious: Boolean(previousStep),\n    canGoNext: Boolean(nextStep),\n    goPrevious,\n    goNext,\n  };\n}\n\nfunction useStepperTransition(currentIndex: number) {\n  const previousIndexRef = React.useRef(currentIndex);\n  const [transition, setTransition] = React.useState(() => ({\n    from: currentIndex,\n    to: currentIndex,\n  }));\n\n  React.useLayoutEffect(() => {\n    const previousIndex = previousIndexRef.current;\n\n    if (previousIndex === currentIndex) {\n      return;\n    }\n\n    setTransition({\n      from: previousIndex,\n      to: currentIndex,\n    });\n    previousIndexRef.current = currentIndex;\n  }, [currentIndex]);\n\n  return transition;\n}\n\nfunction getStepTransitionDelay({\n  from,\n  index,\n  to,\n}: {\n  from: number;\n  index: number;\n  to: number;\n}) {\n  if (from < 0 || to < 0 || index < 0 || from === to) {\n    return 0;\n  }\n\n  if (to > from) {\n    return index >= from && index < to ? (index - from) * 80 : 0;\n  }\n\n  return index >= to && index < from ? (from - index - 1) * 80 : 0;\n}\n\nfunction useDuplicateStepWarning(steps: StepperStep[]) {\n  const duplicateStepValues = React.useMemo(\n    () => getDuplicateStepValues(steps),\n    [steps]\n  );\n  const duplicateStepValuesKey = duplicateStepValues.join(\"\\0\");\n\n  React.useEffect(() => {\n    if (!isDevelopment || duplicateStepValues.length === 0) {\n      return;\n    }\n\n    duplicateStepValues.forEach((stepValue) => {\n      warnDev(\n        `StepperItem value \"${stepValue}\" is duplicated. Step values must be unique within a Stepper.`\n      );\n    });\n  }, [duplicateStepValues, duplicateStepValuesKey]);\n}\n\nfunction useNavigationButton({\n  canNavigate,\n  disabled,\n  navigate,\n  onBeforeNavigate,\n}: {\n  canNavigate: boolean;\n  disabled: boolean | undefined;\n  navigate: () => void;\n  onBeforeNavigate: StepperNavigationGuard | undefined;\n}) {\n  const [isPending, setIsPending] = React.useState(false);\n  const pendingRef = React.useRef(false);\n  const isDisabled = disabled || !canNavigate || isPending;\n\n  const handleClick = React.useCallback(\n    async (\n      event: React.MouseEvent<HTMLButtonElement>,\n      onClick: React.MouseEventHandler<HTMLButtonElement> | undefined\n    ) => {\n      onClick?.(event);\n\n      if (isDisabled || pendingRef.current) {\n        event.preventDefault();\n        return;\n      }\n\n      if (event.defaultPrevented) {\n        return;\n      }\n\n      pendingRef.current = true;\n      setIsPending(true);\n\n      try {\n        const canCompleteNavigation =\n          await resolveNavigationGuard(onBeforeNavigate);\n\n        if (canCompleteNavigation) {\n          navigate();\n        }\n      } finally {\n        pendingRef.current = false;\n        setIsPending(false);\n      }\n    },\n    [isDisabled, navigate, onBeforeNavigate]\n  );\n\n  return {\n    isDisabled,\n    handleClick,\n  };\n}\n\nfunction useStepper<\n  TValue extends StepperValue = StepperValue,\n>(): StepperApi<TValue> {\n  const context = useStepperContext(\n    \"useStepper\"\n  ) as unknown as StepperContextValue<TValue>;\n\n  return {\n    value: context.value,\n    orientation: context.orientation,\n    steps: context.steps,\n    currentIndex: context.currentIndex,\n    totalSteps: context.totalSteps,\n    setValue: context.setValue,\n    getStepIndex: context.getStepIndex,\n    canGoPrevious: context.canGoPrevious,\n    canGoNext: context.canGoNext,\n    goPrevious: context.goPrevious,\n    goNext: context.goNext,\n  };\n}\n\nfunction useStepperItem<\n  TValue extends StepperValue = StepperValue,\n>(): StepperItemApi<TValue> {\n  return useStepperItemContext(\n    \"useStepperItem\"\n  ) as unknown as StepperItemContextValue<TValue>;\n}\n\n// ----------------------------------------------------------------------------\n// Root\n// ----------------------------------------------------------------------------\n\nfunction Stepper<const TSteps extends readonly StepperStepInput[]>(\n  props: StepperPropsWithSteps<TSteps>\n): React.ReactElement;\nfunction Stepper<TValue extends StepperValue = StepperValue>(\n  props: StepperPropsWithoutSteps<TValue>\n): React.ReactElement;\nfunction Stepper({\n  value,\n  defaultValue,\n  onValueChange,\n  orientation = \"horizontal\",\n  steps: stepsProp,\n  className,\n  children,\n  ...props\n}: StepperProps) {\n  const id = React.useId();\n  const { registeredSteps, registerStep, unregisterStep } =\n    useRegisteredSteps();\n  const { isExplicitMode, steps } = useStepperSteps(\n    stepsProp,\n    registeredSteps\n  );\n  const {\n    currentValue,\n    currentIndex,\n    setValue: setStepperValue,\n  } = useStepperValue({\n    value,\n    defaultValue,\n    onValueChange,\n    steps,\n  });\n  const { canGoPrevious, canGoNext, goPrevious, goNext } =\n    useStepperNavigation({\n      currentValue,\n      setValue: setStepperValue,\n      steps,\n    });\n  const { from: transitionFromIndex, to: transitionToIndex } =\n    useStepperTransition(currentIndex);\n\n  useDuplicateStepWarning(steps);\n\n  const context = React.useMemo<StepperContextValue>(\n    () => ({\n      value: currentValue,\n      orientation,\n      steps,\n      isExplicitMode,\n      currentIndex,\n      transitionFromIndex,\n      transitionToIndex,\n      totalSteps: steps.length,\n      registerStep,\n      unregisterStep,\n      setValue: setStepperValue,\n      getStepIndex: (stepValue) =>\n        steps.findIndex((step) => step.value === stepValue),\n      getTriggerId: (stepValue) => `${id}-trigger-${getSafeId(stepValue)}`,\n      getContentId: (stepValue) => `${id}-content-${getSafeId(stepValue)}`,\n      canGoPrevious,\n      canGoNext,\n      goPrevious,\n      goNext,\n    }),\n    [\n      canGoNext,\n      canGoPrevious,\n      currentValue,\n      currentIndex,\n      transitionFromIndex,\n      transitionToIndex,\n      goNext,\n      goPrevious,\n      id,\n      isExplicitMode,\n      orientation,\n      registerStep,\n      setStepperValue,\n      steps,\n      unregisterStep,\n    ]\n  );\n\n  return (\n    <StepperContext.Provider value={context}>\n      <div\n        data-slot=\"stepper\"\n        data-orientation={orientation}\n        className={cn(\"flex flex-col gap-6\", className)}\n        {...props}\n      >\n        {children}\n      </div>\n    </StepperContext.Provider>\n  );\n}\n\n// ----------------------------------------------------------------------------\n// List\n// ----------------------------------------------------------------------------\n\nfunction StepperList({\n  className,\n  \"aria-label\": ariaLabel = \"Progress steps\",\n  children,\n  ...props\n}: StepperListProps) {\n  const { orientation } = useStepperContext(\"StepperList\");\n\n  return (\n    <StepperListContext.Provider value={true}>\n      <ol\n        aria-label={props[\"aria-labelledby\"] ? undefined : ariaLabel}\n        data-slot=\"stepper-list\"\n        data-orientation={orientation}\n        className={cn(\n          \"flex min-w-0\",\n          \"data-[orientation=horizontal]:w-full data-[orientation=horizontal]:gap-0 data-[orientation=horizontal]:overflow-x-auto data-[orientation=horizontal]:pb-2\",\n          \"data-[orientation=vertical]:flex-col data-[orientation=vertical]:gap-4\",\n          \"[&>li:last-child_[data-slot=stepper-separator]]:hidden\",\n          className\n        )}\n        {...props}\n      >\n        {children}\n      </ol>\n    </StepperListContext.Provider>\n  );\n}\n\n// ----------------------------------------------------------------------------\n// Item\n// ----------------------------------------------------------------------------\n\nfunction StepperItem<TValue extends StepperValue = StepperValue>({\n  value,\n  completed = false,\n  defaultTrigger = true,\n  disabled = false,\n  error = false,\n  separator = true,\n  className,\n  style,\n  children,\n  ...props\n}: StepperItemProps<TValue>) {\n  const {\n    value: currentValue,\n    orientation,\n    steps,\n    isExplicitMode,\n    transitionFromIndex,\n    transitionToIndex,\n    registerStep,\n    unregisterStep,\n    setValue,\n    getStepIndex,\n    getTriggerId,\n    getContentId,\n  } = useStepperContext(\"StepperItem\");\n  const registrationId = React.useId();\n  const isInsideStepperList = useStepperListContext();\n  const index = getStepIndex(value);\n  const step = getStepByValue(steps, value);\n  const isDisabled = step?.disabled ?? disabled;\n  const currentIndex =\n    currentValue === undefined ? -1 : getStepIndex(currentValue);\n  const isActive = currentValue === value;\n  const stepPosition: StepperStepPosition =\n    currentIndex < 0 || index < 0\n      ? \"next\"\n      : index < currentIndex\n        ? \"previous\"\n        : index === currentIndex\n          ? \"current\"\n          : \"next\";\n  const stepState: StepperStepState = isDisabled\n    ? \"disabled\"\n    : error\n      ? \"error\"\n      : isActive\n        ? \"active\"\n        : completed\n          ? \"completed\"\n          : \"inactive\";\n  const renderDefaultTrigger = defaultTrigger;\n  const isLastStep = index >= 0 && index === steps.length - 1;\n  const shouldRenderSeparator =\n    isInsideStepperList && separator && !isLastStep;\n  const separatorTransitionDelay = getStepTransitionDelay({\n    from: transitionFromIndex,\n    index,\n    to: transitionToIndex,\n  });\n  const itemStyle = {\n    ...style,\n    \"--stepper-separator-delay\": `${separatorTransitionDelay}ms`,\n  } as React.CSSProperties;\n\n  React.useLayoutEffect(() => {\n    if (!isInsideStepperList || isExplicitMode) {\n      return;\n    }\n\n    registerStep({\n      id: registrationId,\n      value,\n      disabled,\n    });\n\n    return () => unregisterStep(registrationId);\n  }, [\n    disabled,\n    isExplicitMode,\n    isInsideStepperList,\n    registerStep,\n    registrationId,\n    unregisterStep,\n    value,\n  ]);\n\n  React.useEffect(() => {\n    if (!isDevelopment || isInsideStepperList) {\n      return;\n    }\n\n    const timeout = window.setTimeout(() => {\n      warnDev(\n        `StepperItem with value \"${value}\" must be rendered inside StepperList to participate in step order.`\n      );\n    }, 0);\n\n    return () => window.clearTimeout(timeout);\n  }, [isInsideStepperList, value]);\n\n  React.useEffect(() => {\n    if (\n      !isDevelopment ||\n      !isInsideStepperList ||\n      index >= 0\n    ) {\n      return;\n    }\n\n    const timeout = window.setTimeout(() => {\n      warnDev(\n        `StepperItem with value \"${value}\" could not be found in the Stepper order. Check that it is rendered inside StepperList. For dynamic or conditional step order, pass an explicit steps prop to Stepper.`\n      );\n    }, 0);\n\n    return () => window.clearTimeout(timeout);\n  }, [index, isInsideStepperList, value]);\n\n  const itemContext = React.useMemo<StepperItemContextValue>(\n    () => ({\n      value,\n      index,\n      disabled: isDisabled,\n      completed,\n      isActive,\n      stepState,\n      stepPosition,\n      orientation,\n      setValue,\n      triggerId: getTriggerId(value),\n      contentId: getContentId(value),\n    }),\n    [\n      value,\n      index,\n      isDisabled,\n      completed,\n      isActive,\n      stepState,\n      stepPosition,\n      orientation,\n      setValue,\n      getTriggerId,\n      getContentId,\n    ]\n  );\n\n  return (\n    <li\n      data-slot=\"stepper-item\"\n      data-orientation={orientation}\n      data-state={stepState}\n      data-position={stepPosition}\n      data-disabled={isDisabled ? \"\" : undefined}\n      data-error={error ? \"\" : undefined}\n      data-completed={completed ? \"\" : undefined}\n      className={cn(\n        \"group/stepper-item relative flex min-w-0\",\n        \"[--stepper-indicator-size:1.75rem] [--stepper-separator-offset:calc(var(--stepper-indicator-size)_/_2)] [--stepper-separator-y:calc(var(--stepper-indicator-size)_/_2)] sm:[--stepper-indicator-size:2.25rem]\",\n        \"data-[orientation=horizontal]:min-w-16 data-[orientation=horizontal]:flex-1 data-[orientation=horizontal]:flex-col data-[orientation=horizontal]:items-center sm:data-[orientation=horizontal]:min-w-28\",\n        \"data-[orientation=vertical]:items-start data-[orientation=vertical]:gap-3\",\n        className\n      )}\n      style={itemStyle}\n      {...props}\n    >\n      <StepperItemContext.Provider value={itemContext}>\n        {renderDefaultTrigger ? (\n          <>\n            <StepperTrigger>\n              <StepperIndicator />\n              <StepperLabel>{children}</StepperLabel>\n            </StepperTrigger>\n            {shouldRenderSeparator ? <StepperSeparator /> : null}\n          </>\n        ) : (\n          <>\n            {children}\n            {shouldRenderSeparator ? <StepperSeparator /> : null}\n          </>\n        )}\n      </StepperItemContext.Provider>\n    </li>\n  );\n}\n\n// ----------------------------------------------------------------------------\n// Trigger\n// ----------------------------------------------------------------------------\n\nfunction StepperTrigger({\n  asChild = false,\n  className,\n  children,\n  disabled,\n  onClick,\n  onKeyDown,\n  tabIndex,\n  ...props\n}: StepperTriggerProps) {\n  const {\n    value,\n    disabled: itemDisabled,\n    isActive,\n    stepState,\n    orientation,\n    setValue,\n    triggerId,\n    contentId,\n  } = useStepperItemContext(\"StepperTrigger\");\n  const { steps, getTriggerId } = useStepperContext(\"StepperTrigger\");\n  const isDisabled = itemDisabled || disabled;\n  const Comp = asChild ? Slot : \"button\";\n\n  return (\n    <Comp\n      id={triggerId}\n      type={asChild ? undefined : \"button\"}\n      aria-current={isActive ? \"step\" : undefined}\n      aria-controls={isActive ? contentId : undefined}\n      aria-disabled={isDisabled ? true : undefined}\n      disabled={asChild ? undefined : isDisabled}\n      tabIndex={asChild && isDisabled ? -1 : tabIndex}\n      data-slot=\"stepper-trigger\"\n      data-state={stepState}\n      data-disabled={isDisabled ? \"\" : undefined}\n      className={cn(\n        \"group inline-flex min-h-10 min-w-0 items-center gap-2 rounded-lg text-left text-sm font-medium outline-none\",\n        \"text-muted-foreground transition-[color,background-color,border-color,box-shadow,transform] hover:text-foreground active:scale-[0.96]\",\n        \"focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-none\",\n        \"disabled:pointer-events-none disabled:opacity-50\",\n        \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n        \"data-[state=active]:text-foreground\",\n        \"data-[state=completed]:text-foreground\",\n        \"data-[state=error]:text-destructive\",\n        orientation === \"horizontal\" && \"w-full flex-col items-center text-center\",\n        orientation === \"vertical\" && \"justify-start\",\n        className\n      )}\n      onClick={(event: React.MouseEvent<HTMLButtonElement>) => {\n        onClick?.(event);\n\n        if (isDisabled) {\n          event.preventDefault();\n          return;\n        }\n\n        if (!event.defaultPrevented) {\n          setValue(value);\n        }\n      }}\n      onKeyDown={(event: React.KeyboardEvent<HTMLButtonElement>) => {\n        onKeyDown?.(event);\n\n        if (event.defaultPrevented || isDisabled) {\n          return;\n        }\n\n        const targetStepValue = getKeyboardNavigationStepValue({\n          key: event.key,\n          orientation,\n          steps,\n          value,\n        });\n\n        if (!targetStepValue || targetStepValue === value) {\n          return;\n        }\n\n        const targetTrigger = document.getElementById(\n          getTriggerId(targetStepValue)\n        );\n\n        if (targetTrigger instanceof HTMLElement) {\n          event.preventDefault();\n          targetTrigger.focus();\n        }\n      }}\n      {...props}\n    >\n      {children}\n    </Comp>\n  );\n}\n\nfunction StepperIndicator({\n  className,\n  children,\n  ...props\n}: StepperIndicatorProps) {\n  const { index, stepState } = useStepperItemContext(\"StepperIndicator\");\n  const stepNumber = index >= 0 ? index + 1 : undefined;\n  const content =\n    children !== undefined\n      ? children\n      : stepState === \"error\"\n        ? \"!\"\n        : stepState === \"completed\"\n          ? \"\\u2713\"\n          : stepNumber\n            ? stepNumber\n            : null;\n\n  return (\n    <>\n      {stepNumber ? (\n        <span className=\"sr-only\">Step {stepNumber}:</span>\n      ) : null}\n      <span\n        aria-hidden=\"true\"\n        data-slot=\"stepper-indicator\"\n        className={cn(\n          \"flex size-(--stepper-indicator-size) shrink-0 items-center justify-center rounded-full border border-border bg-background text-xs font-semibold text-muted-foreground shadow-sm\",\n          \"transition-[color,background-color,border-color,box-shadow,transform]\",\n          \"group-hover:border-foreground/30\",\n          \"group-data-[state=active]:border-primary group-data-[state=active]:bg-primary group-data-[state=active]:text-primary-foreground group-data-[state=active]:shadow-md\",\n          \"group-data-[state=completed]:border-primary group-data-[state=completed]:bg-primary group-data-[state=completed]:text-primary-foreground\",\n          \"group-data-[state=error]:border-destructive group-data-[state=error]:bg-destructive group-data-[state=error]:text-destructive-foreground\",\n          \"group-data-[position=previous]:text-foreground\",\n          \"[transition-delay:var(--stepper-separator-delay)] motion-reduce:[transition-delay:0ms]\",\n          \"[&>svg]:size-3.5 [&>svg]:shrink-0 sm:[&>svg]:size-4\",\n          className\n        )}\n        {...props}\n      >\n        {content}\n      </span>\n    </>\n  );\n}\n\nfunction StepperLabel({ className, ...props }: StepperLabelProps) {\n  const { orientation } = useStepperItemContext(\"StepperLabel\");\n\n  return (\n    <span\n      data-slot=\"stepper-label\"\n      className={cn(\n        \"min-w-0 text-xs leading-tight font-medium sm:text-sm\",\n        orientation === \"horizontal\" && \"max-w-40 text-balance\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction StepperDescription({ className, ...props }: StepperDescriptionProps) {\n  const { orientation } = useStepperItemContext(\"StepperDescription\");\n\n  return (\n    <span\n      data-slot=\"stepper-description\"\n      className={cn(\n        \"text-xs leading-snug font-normal text-muted-foreground\",\n        orientation === \"horizontal\" && \"max-w-44 text-balance\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction StepperSeparator({ className, ...props }: StepperSeparatorProps) {\n  const { orientation } = useStepperItemContext(\"StepperSeparator\");\n\n  return (\n    <span\n      aria-hidden=\"true\"\n      data-slot=\"stepper-separator\"\n      className={cn(\n        \"overflow-hidden bg-muted-foreground/25 after:absolute after:inset-0 after:bg-primary after:content-['']\",\n        \"after:transition-transform after:duration-[220ms] after:ease-out after:[transition-delay:var(--stepper-separator-delay)] motion-reduce:after:transition-none motion-reduce:after:[transition-delay:0ms]\",\n        orientation === \"horizontal\" &&\n          \"absolute left-[calc(50%_+_var(--stepper-separator-offset))] right-[calc(-50%_+_var(--stepper-separator-offset))] top-(--stepper-separator-y) h-px after:origin-left after:scale-x-0 group-data-[completed]/stepper-item:after:scale-x-100 group-data-[position=previous]/stepper-item:after:scale-x-100 group-data-[state=completed]/stepper-item:after:scale-x-100\",\n        orientation === \"vertical\" &&\n          \"absolute left-[calc(var(--stepper-indicator-size)_/_2)] top-[calc(var(--stepper-indicator-size)_+_0.5rem)] h-[calc(100%_-_var(--stepper-indicator-size)_+_0.75rem)] w-px after:origin-top after:scale-y-0 group-data-[completed]/stepper-item:after:scale-y-100 group-data-[position=previous]/stepper-item:after:scale-y-100 group-data-[state=completed]/stepper-item:after:scale-y-100\",\n        className\n      )}\n      {...props}\n    />\n  );\n}\n\n// ----------------------------------------------------------------------------\n// Content\n// ----------------------------------------------------------------------------\n\nfunction StepperContent<TValue extends StepperValue = StepperValue>({\n  value,\n  forceMount = false,\n  keepMounted = false,\n  asChild = false,\n  className,\n  children,\n  ...props\n}: StepperContentProps<TValue>) {\n  const {\n    value: currentValue,\n    steps,\n    getTriggerId,\n    getContentId,\n  } = useStepperContext(\"StepperContent\");\n  const isActive = currentValue === value;\n  const hasMatchingStep = steps.some((step) => step.value === value);\n  const shouldKeepMounted = forceMount || keepMounted;\n\n  React.useEffect(() => {\n    if (!isDevelopment || hasMatchingStep) {\n      return;\n    }\n\n    const timeout = window.setTimeout(() => {\n      warnDev(\n        `StepperContent value \"${value}\" does not match any StepperItem. Content values should map to a step value.`\n      );\n    }, 0);\n\n    return () => window.clearTimeout(timeout);\n  }, [hasMatchingStep, value]);\n\n  if (!shouldKeepMounted && !isActive) {\n    return null;\n  }\n\n  const Comp = asChild ? Slot : \"div\";\n\n  return (\n    <Comp\n      id={getContentId(value)}\n      role=\"region\"\n      aria-labelledby={getTriggerId(value)}\n      data-slot=\"stepper-content\"\n      data-state={isActive ? \"active\" : \"inactive\"}\n      hidden={shouldKeepMounted ? !isActive : undefined}\n      className={cn(\n        \"rounded-lg border border-border/70 bg-muted/25 p-4 text-sm text-foreground shadow-sm\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n    </Comp>\n  );\n}\n\n// ----------------------------------------------------------------------------\n// Navigation\n// ----------------------------------------------------------------------------\n\nfunction StepperPrevious({\n  asChild = false,\n  className,\n  children = \"Previous\",\n  disabled,\n  onBeforePrevious,\n  onClick,\n  tabIndex,\n  ...props\n}: StepperPreviousProps) {\n  const { canGoPrevious, goPrevious } = useStepperContext(\"StepperPrevious\");\n  const { handleClick, isDisabled } = useNavigationButton({\n    canNavigate: canGoPrevious,\n    disabled,\n    navigate: goPrevious,\n    onBeforeNavigate: onBeforePrevious,\n  });\n  const Comp = asChild ? Slot : \"button\";\n\n  return (\n    <Comp\n      type={asChild ? undefined : \"button\"}\n      disabled={asChild ? undefined : isDisabled}\n      aria-disabled={isDisabled ? true : undefined}\n      tabIndex={asChild && isDisabled ? -1 : tabIndex}\n      data-slot=\"stepper-previous\"\n      data-disabled={isDisabled ? \"\" : undefined}\n      className={\n        asChild\n          ? className\n          : cn(\n              \"inline-flex h-9 min-w-24 items-center justify-center gap-2 rounded-none border border-border bg-background px-3 text-sm font-medium text-foreground\",\n              \"transition-[color,background-color,border-color,box-shadow,transform] hover:border-foreground/20 hover:bg-muted hover:text-foreground active:scale-[0.97]\",\n              \"focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-none\",\n              \"disabled:pointer-events-none disabled:opacity-50\",\n              \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n              \"[&>svg]:size-4 [&>svg]:shrink-0\",\n              className\n            )\n      }\n      onClick={(event: React.MouseEvent<HTMLButtonElement>) =>\n        handleClick(event, onClick)\n      }\n      {...props}\n    >\n      {children}\n    </Comp>\n  );\n}\n\nfunction StepperNext({\n  asChild = false,\n  className,\n  children = \"Next\",\n  disabled,\n  onBeforeNext,\n  onClick,\n  tabIndex,\n  ...props\n}: StepperNextProps) {\n  const { canGoNext, goNext } = useStepperContext(\"StepperNext\");\n  const { handleClick, isDisabled } = useNavigationButton({\n    canNavigate: canGoNext,\n    disabled,\n    navigate: goNext,\n    onBeforeNavigate: onBeforeNext,\n  });\n  const Comp = asChild ? Slot : \"button\";\n\n  return (\n    <Comp\n      type={asChild ? undefined : \"button\"}\n      disabled={asChild ? undefined : isDisabled}\n      aria-disabled={isDisabled ? true : undefined}\n      tabIndex={asChild && isDisabled ? -1 : tabIndex}\n      data-slot=\"stepper-next\"\n      data-disabled={isDisabled ? \"\" : undefined}\n      className={\n        asChild\n          ? className\n          : cn(\n              \"inline-flex h-9 min-w-24 items-center justify-center gap-2 rounded-none border border-primary bg-primary px-3 text-sm font-medium text-primary-foreground\",\n              \"transition-[background-color,border-color,box-shadow,transform] hover:bg-primary/90 active:scale-[0.97]\",\n              \"focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-none\",\n              \"disabled:pointer-events-none disabled:opacity-50\",\n              \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n              \"[&>svg]:size-4 [&>svg]:shrink-0\",\n              className\n            )\n      }\n      onClick={(event: React.MouseEvent<HTMLButtonElement>) =>\n        handleClick(event, onClick)\n      }\n      {...props}\n    >\n      {children}\n    </Comp>\n  );\n}\n\n// ----------------------------------------------------------------------------\n// Exports\n// ----------------------------------------------------------------------------\n\nsetDisplayName(Stepper, \"Stepper\");\nsetDisplayName(StepperList, \"StepperList\");\nsetDisplayName(StepperItem, \"StepperItem\");\nsetDisplayName(StepperTrigger, \"StepperTrigger\");\nsetDisplayName(StepperIndicator, \"StepperIndicator\");\nsetDisplayName(StepperLabel, \"StepperLabel\");\nsetDisplayName(StepperDescription, \"StepperDescription\");\nsetDisplayName(StepperSeparator, \"StepperSeparator\");\nsetDisplayName(StepperContent, \"StepperContent\");\nsetDisplayName(StepperPrevious, \"StepperPrevious\");\nsetDisplayName(StepperNext, \"StepperNext\");\n\nexport {\n  Stepper,\n  StepperContent,\n  StepperDescription,\n  StepperIndicator,\n  StepperItem,\n  StepperLabel,\n  StepperList,\n  StepperNext,\n  StepperPrevious,\n  StepperSeparator,\n  StepperTrigger,\n  useStepper,\n  useStepperItem,\n};\n\nexport type {\n  StepperApi,\n  StepperButtonProps,\n  StepperContentProps,\n  StepperDescriptionProps,\n  StepperIndicatorProps,\n  StepperItemApi,\n  StepperItemProps,\n  StepperLabelProps,\n  StepperListProps,\n  StepperNavigationGuard,\n  StepperNextProps,\n  StepperOrientation,\n  StepperPreviousProps,\n  StepperProps,\n  StepperSeparatorProps,\n  StepperStep,\n  StepperStepInput,\n  StepperStepPosition,\n  StepperStepState,\n  StepperStepsValue,\n  StepperTriggerProps,\n  StepperValue,\n};\n",
      "type": "registry:ui",
      "target": "@ui/stepper.tsx"
    }
  ],
  "type": "registry:ui"
}