{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "wizard-stepper", "type": "registry:ui", "title": "Wizard Stepper", "description": "A full-featured wizard stepper with titles and descriptions. Perfect for multi-step forms, checkout flows, and onboarding sequences.", "dependencies": [ "lucide-react", "motion" ], "files": [ { "path": "registry/ruixenui/wizard-stepper.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion } from \"motion/react\";\nimport { CheckIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\ntype WizardStepState = \"active\" | \"completed\" | \"pending\";\n\ninterface WizardStepData {\n id: string | number;\n title: string;\n description?: string;\n}\n\ninterface WizardContextValue {\n currentStep: number;\n setCurrentStep: (step: number) => void;\n totalSteps: number;\n isLoading: boolean;\n}\n\n// ── Context ─────────────────────────────────────────────────────────────────\n\nconst WizardContext = React.createContext(\n undefined,\n);\n\nfunction useWizard() {\n const context = React.useContext(WizardContext);\n if (!context) {\n throw new Error(\"useWizard must be used within a WizardStepper\");\n }\n return context;\n}\n\n// ── WizardStepper ───────────────────────────────────────────────────────────\n\ninterface WizardStepperProps extends React.HTMLAttributes {\n steps: WizardStepData[];\n defaultStep?: number;\n currentStep?: number;\n onStepChange?: (step: number) => void;\n loading?: boolean;\n orientation?: \"horizontal\" | \"vertical\";\n}\n\nfunction WizardStepper({\n steps,\n defaultStep = 0,\n currentStep: controlledStep,\n onStepChange,\n loading = false,\n orientation = \"horizontal\",\n className,\n ...props\n}: WizardStepperProps) {\n const [internalStep, setInternalStep] = React.useState(defaultStep);\n const currentStep = controlledStep ?? internalStep;\n\n const setCurrentStep = React.useCallback(\n (step: number) => {\n if (controlledStep === undefined) setInternalStep(step);\n onStepChange?.(step);\n },\n [controlledStep, onStepChange],\n );\n\n return (\n \n
\n \n {steps.map((step, index) => {\n const state: WizardStepState =\n index < currentStep\n ? \"completed\"\n : index === currentStep\n ? \"active\"\n : \"pending\";\n\n return (\n \n setCurrentStep(index)}\n />\n {index < steps.length - 1 && orientation === \"horizontal\" && (\n \n )}\n \n );\n })}\n
\n \n \n );\n}\n\n// ── WizardStepItem ──────────────────────────────────────────────────────────\n\ninterface WizardStepItemProps {\n step: number;\n state: WizardStepState;\n title: string;\n description?: string;\n isLast: boolean;\n orientation: \"horizontal\" | \"vertical\";\n isLoading: boolean;\n onClick: () => void;\n}\n\nfunction WizardStepItem({\n step,\n state,\n title,\n description,\n isLast,\n orientation,\n isLoading,\n onClick,\n}: WizardStepItemProps) {\n return (\n \n
\n {orientation === \"vertical\" && (\n
\n \n {!isLast && (\n \n )}\n
\n )}\n {orientation === \"horizontal\" && (\n \n )}\n
\n\n \n \n {title}\n

\n {description && (\n \n {description}\n

\n )}\n \n \n );\n}\n\n// ── WizardCircle ────────────────────────────────────────────────────────────\n\ninterface WizardCircleProps {\n step: number;\n state: WizardStepState;\n isLoading: boolean;\n}\n\nfunction WizardCircle({ step, state, isLoading }: WizardCircleProps) {\n return (\n
\n {state === \"active\" && (\n \n )}\n \n {isLoading ? (\n \n ) : state === \"completed\" ? (\n \n ) : (\n step\n )}\n \n
\n );\n}\n\n// ── WizardConnector ─────────────────────────────────────────────────────────\n\ninterface WizardConnectorProps {\n state: WizardStepState;\n}\n\nfunction WizardConnector({ state }: WizardConnectorProps) {\n return (\n
\n \n
\n );\n}\n\n// ── Exports ─────────────────────────────────────────────────────────────────\n\nexport { WizardStepper, useWizard };\nexport type { WizardStepData, WizardStepState };\n", "type": "registry:ui", "target": "components/ruixen/wizard-stepper.tsx" } ] }