"use client"; import { forwardRef, type KeyboardEvent } from "react"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; import { cn } from "@/lib/utils"; export interface StepperProps { steps: { id: string; title: string; description?: string }[]; currentStep: number; onStepChange?: (step: number) => void; disabled?: boolean; orientation?: "auto" | "horizontal" | "vertical"; showDescriptions?: boolean; animated?: boolean; className?: string; ariaLabel?: string; } export const Stepper = forwardRef(function Stepper( { steps, currentStep, onStepChange, disabled = false, orientation = "auto", showDescriptions = true, animated = true, className, ariaLabel = "Progress", }, ref, ) { const prefersReducedMotion = useReducedMotion(); const reduced = prefersReducedMotion || !animated; const current = Number.isFinite(currentStep) ? Math.max(0, Math.min(steps.length, Math.trunc(currentStep))) : 0; const transition = reduced ? { duration: 0 } : { type: "spring" as const, stiffness: 420, damping: 32 }; const handleKeyDown = (event: KeyboardEvent) => { if (event.altKey || event.ctrlKey || event.metaKey) return; if ( ![ "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End", ].includes(event.key) ) return; const buttons = Array.from( event.currentTarget .closest("ol") ?.querySelectorAll("button:not(:disabled)") ?? [], ); const index = buttons.indexOf(event.currentTarget); const target = event.key === "Home" ? 0 : event.key === "End" ? buttons.length - 1 : index + (["ArrowLeft", "ArrowUp"].includes(event.key) ? -1 : 1); event.preventDefault(); buttons[Math.max(0, Math.min(buttons.length - 1, target))]?.focus(); }; if (steps.length === 0) return null; return ( ); }); Stepper.displayName = "Stepper";