"use client";
import { Check } from "lucide-react";
import { cn } from "@/lib/utils";
export interface StepperStep {
label: string;
/** A short line under the label. */
description?: string;
}
export interface StepperProps {
steps: StepperStep[];
/** The step in progress, counting from 0. Steps before it show as done. */
current: number;
/** Lets people jump back to a finished step. Leave unset to make the steps display only. */
onStepClick?: (index: number) => void;
className?: string;
}
/**
* Numbered steps joined by a line that fills as you move forward. Finished steps trade their
* number for a check, and the line between two steps fills from the left.
*/
export function Stepper({ steps, current, onStepClick, className }: StepperProps) {
return (
{steps.map((step, index) => {
const done = index < current;
const active = index === current;
const clickable = Boolean(onStepClick) && done;
const Tag = clickable ? "button" : "div";
return (
-
{index < steps.length - 1 ? (
// The line to the next step. It sits behind the circles and fills from the left.
) : null}
onStepClick?.(index) } : {})}
aria-current={active ? "step" : undefined}
className={cn(
"relative flex flex-col items-center gap-2 rounded-lg px-1 text-center outline-none",
clickable && "cursor-pointer focus-visible:ring-2 focus-visible:ring-foreground/40",
)}
>
{/* Number and check share one spot and crossfade, so the circle never changes size. */}
{index + 1}
{step.label}
{step.description ? {step.description} : null}
{done ? ", done" : active ? ", current step" : ""}
);
})}
);
}