{ "$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 = {\n value: TValue;\n disabled?: boolean;\n};\n\ntype StepperStep = {\n value: TValue;\n disabled: boolean;\n};\n\ntype StepperStepsValue =\n TSteps[number][\"value\"];\n\ntype RegisteredStep =\n StepperStep & {\n id: string;\n };\n\ntype StepperNavigationGuard = () => boolean | Promise;\n\ntype StepperApi = {\n value: TValue | undefined;\n orientation: StepperOrientation;\n steps: StepperStep[];\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 = {\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 =\n StepperApi & {\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 =\n StepperItemApi;\n\ntype StepperProps =\n React.ComponentPropsWithoutRef<\"div\"> & {\n value?: TValue;\n defaultValue?: TValue;\n onValueChange?: (value: TValue) => void;\n orientation?: StepperOrientation;\n steps?: readonly StepperStepInput[];\n };\n\ntype StepperPropsWithSteps<\n TSteps extends readonly StepperStepInput[],\n> = Omit>, \"steps\"> & {\n steps: TSteps;\n};\n\ntype StepperPropsWithoutSteps<\n TValue extends StepperValue = StepperValue,\n> = Omit, \"steps\"> & {\n steps?: undefined;\n};\n\ntype StepperListProps = React.ComponentPropsWithoutRef<\"ol\">;\n\ntype StepperItemProps = 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 =\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(\n step: StepperStepInput\n): StepperStep {\n return {\n value: step.value,\n disabled: Boolean(step.disabled),\n };\n}\n\nfunction normalizeSteps(\n steps: readonly StepperStepInput[] | undefined\n) {\n return steps?.map(normalizeStep) ?? [];\n}\n\nfunction getDuplicateStepValues(\n steps: ReadonlyArray<{ value: TValue }>\n) {\n const seenValues = new Set();\n const duplicateValues = new Set();\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(\n steps: readonly StepperStep[],\n value: TValue | undefined\n) {\n return steps.find((step) => step.value === value);\n}\n\nfunction getCurrentStepValue(\n steps: readonly StepperStep[],\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(\n steps: readonly StepperStep[],\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(\n steps: readonly StepperStep[],\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({\n key,\n orientation,\n steps,\n value,\n}: {\n key: string;\n orientation: StepperOrientation;\n steps: readonly StepperStep[];\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(null);\nconst StepperItemContext =\n React.createContext(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,\n onClick: React.MouseEventHandler | 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 {\n const context = useStepperContext(\n \"useStepper\"\n ) as unknown as StepperContextValue;\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 {\n return useStepperItemContext(\n \"useStepperItem\"\n ) as unknown as StepperItemContextValue;\n}\n\n// ----------------------------------------------------------------------------\n// Root\n// ----------------------------------------------------------------------------\n\nfunction Stepper(\n props: StepperPropsWithSteps\n): React.ReactElement;\nfunction Stepper(\n props: StepperPropsWithoutSteps\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(\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 \n \n {children}\n \n \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 \n li:last-child_[data-slot=stepper-separator]]:hidden\",\n className\n )}\n {...props}\n >\n {children}\n \n \n );\n}\n\n// ----------------------------------------------------------------------------\n// Item\n// ----------------------------------------------------------------------------\n\nfunction StepperItem({\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) {\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(\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 \n \n {renderDefaultTrigger ? (\n <>\n \n \n {children}\n \n {shouldRenderSeparator ? : null}\n \n ) : (\n <>\n {children}\n {shouldRenderSeparator ? : null}\n \n )}\n \n \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 ) => {\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) => {\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 \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 Step {stepNumber}:\n ) : null}\n svg]:size-3.5 [&>svg]:shrink-0 sm:[&>svg]:size-4\",\n className\n )}\n {...props}\n >\n {content}\n \n \n );\n}\n\nfunction StepperLabel({ className, ...props }: StepperLabelProps) {\n const { orientation } = useStepperItemContext(\"StepperLabel\");\n\n return (\n \n );\n}\n\nfunction StepperDescription({ className, ...props }: StepperDescriptionProps) {\n const { orientation } = useStepperItemContext(\"StepperDescription\");\n\n return (\n \n );\n}\n\nfunction StepperSeparator({ className, ...props }: StepperSeparatorProps) {\n const { orientation } = useStepperItemContext(\"StepperSeparator\");\n\n return (\n \n );\n}\n\n// ----------------------------------------------------------------------------\n// Content\n// ----------------------------------------------------------------------------\n\nfunction StepperContent({\n value,\n forceMount = false,\n keepMounted = false,\n asChild = false,\n className,\n children,\n ...props\n}: StepperContentProps) {\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