{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "Stepper-TS-CSS",
	"title": "Stepper",
	"description": "Animated multi-step progress indicator with active state transitions.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "Stepper.css",
			"target": "@components/Stepper.css",
			"content": ".outer-container {\n  display: flex;\n  min-height: 100%;\n  flex: 1 1 0%;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  padding: 1rem;\n}\n\n@media (min-width: 640px) {\n  .outer-container {\n    aspect-ratio: 4 / 3;\n  }\n}\n\n@media (min-width: 768px) {\n  .outer-container {\n    aspect-ratio: 2 / 1;\n  }\n}\n\n.step-circle-container {\n  margin-left: auto;\n  margin-right: auto;\n  width: 100%;\n  max-width: 28rem;\n  border-radius: 2rem;\n  box-shadow:\n    0 20px 25px -5px rgba(0, 0, 0, 0.1),\n    0 10px 10px -5px rgba(0, 0, 0, 0.04);\n}\n\n.step-indicator-row {\n  display: flex;\n  width: 100%;\n  align-items: center;\n  padding: 2rem;\n}\n\n.step-content-default {\n  position: relative;\n  overflow: hidden;\n}\n\n.step-default {\n  padding-left: 2rem;\n  padding-right: 2rem;\n}\n\n.footer-container {\n  padding-left: 2rem;\n  padding-right: 2rem;\n  padding-bottom: 2rem;\n}\n\n.footer-nav {\n  margin-top: 2.5rem;\n  display: flex;\n}\n\n.footer-nav.spread {\n  justify-content: space-between;\n}\n\n.footer-nav.end {\n  justify-content: flex-end;\n}\n\n.back-button {\n  transition: all 350ms;\n  border-radius: 0.25rem;\n  padding: 0.25rem 0.5rem;\n  color: #a3a3a3;\n  cursor: pointer;\n}\n\n.back-button:hover {\n  color: #52525b;\n}\n\n.back-button.inactive {\n  pointer-events: none;\n  opacity: 0.5;\n  color: #a3a3a3;\n}\n\n.next-button {\n  transition: all 350ms;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border-radius: 9999px;\n  background-color: #5227ff;\n  color: #fff;\n  font-weight: 500;\n  letter-spacing: -0.025em;\n  padding: 0.375rem 0.875rem;\n  cursor: pointer;\n}\n\n.next-button:hover {\n  background-color: #5227ff;\n}\n\n.next-button:active {\n  background-color: #5227ff;\n}\n\n.step-indicator {\n  position: relative;\n  cursor: pointer;\n  outline: none;\n}\n\n.step-indicator-inner {\n  display: flex;\n  height: 2rem;\n  width: 2rem;\n  align-items: center;\n  justify-content: center;\n  border-radius: 9999px;\n  font-weight: 600;\n}\n\n.active-dot {\n  height: 0.75rem;\n  width: 0.75rem;\n  border-radius: 9999px;\n  background-color: #fff;\n}\n\n.step-number {\n  font-size: 0.875rem;\n}\n\n.step-connector {\n  position: relative;\n  margin-left: 0.5rem;\n  margin-right: 0.5rem;\n  height: 0.125rem;\n  flex: 1;\n  overflow: hidden;\n  border-radius: 0.25rem;\n  background-color: #52525b;\n}\n\n.step-connector-inner {\n  position: absolute;\n  left: 0;\n  top: 0;\n  height: 100%;\n}\n\n.check-icon {\n  height: 1rem;\n  width: 1rem;\n  color: #fff;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "Stepper.tsx",
			"content": "import { AnimatePresence, motion, type Variants } from 'motion/react';\nimport React, { Children, type HTMLAttributes, type JSX, type ReactNode, useLayoutEffect, useRef, useState } from 'react';\n\nimport './Stepper.css';\n\ninterface StepperProps extends HTMLAttributes<HTMLDivElement> {\n  children: ReactNode;\n  initialStep?: number;\n  onStepChange?: (step: number) => void;\n  onFinalStepCompleted?: () => void;\n  stepCircleContainerClassName?: string;\n  stepContainerClassName?: string;\n  contentClassName?: string;\n  footerClassName?: string;\n  backButtonProps?: React.ButtonHTMLAttributes<HTMLButtonElement>;\n  nextButtonProps?: React.ButtonHTMLAttributes<HTMLButtonElement>;\n  backButtonText?: string;\n  nextButtonText?: string;\n  disableStepIndicators?: boolean;\n  renderStepIndicator?: (props: RenderStepIndicatorProps) => ReactNode;\n}\n\ninterface RenderStepIndicatorProps {\n  step: number;\n  currentStep: number;\n  onStepClick: (clicked: number) => void;\n}\n\nexport default function Stepper({\n  children,\n  initialStep = 1,\n  onStepChange = () => {},\n  onFinalStepCompleted = () => {},\n  stepCircleContainerClassName = '',\n  stepContainerClassName = '',\n  contentClassName = '',\n  footerClassName = '',\n  backButtonProps = {},\n  nextButtonProps = {},\n  backButtonText = 'Back',\n  nextButtonText = 'Continue',\n  disableStepIndicators = false,\n  renderStepIndicator,\n  ...rest\n}: StepperProps) {\n  const [currentStep, setCurrentStep] = useState<number>(initialStep);\n  const [direction, setDirection] = useState<number>(0);\n  const stepsArray = Children.toArray(children);\n  const totalSteps = stepsArray.length;\n  const isCompleted = currentStep > totalSteps;\n  const isLastStep = currentStep === totalSteps;\n\n  const updateStep = (newStep: number) => {\n    setCurrentStep(newStep);\n    if (newStep > totalSteps) {\n      onFinalStepCompleted();\n    } else {\n      onStepChange(newStep);\n    }\n  };\n\n  const handleBack = () => {\n    if (currentStep > 1) {\n      setDirection(-1);\n      updateStep(currentStep - 1);\n    }\n  };\n\n  const handleNext = () => {\n    if (!isLastStep) {\n      setDirection(1);\n      updateStep(currentStep + 1);\n    }\n  };\n\n  const handleComplete = () => {\n    setDirection(1);\n    updateStep(totalSteps + 1);\n  };\n\n  return (\n    <div className=\"outer-container\" {...rest}>\n      <div\n        className={`step-circle-container ${stepCircleContainerClassName}`}\n        style={{ border: '1px solid var(--border-primary, #222)' }}\n      >\n        <div className={`step-indicator-row ${stepContainerClassName}`}>\n          {stepsArray.map((_, index) => {\n            const stepNumber = index + 1;\n            const isNotLastStep = index < totalSteps - 1;\n            return (\n              <React.Fragment key={stepNumber}>\n                {renderStepIndicator ? (\n                  renderStepIndicator({\n                    step: stepNumber,\n                    currentStep,\n                    onStepClick: clicked => {\n                      setDirection(clicked > currentStep ? 1 : -1);\n                      updateStep(clicked);\n                    }\n                  })\n                ) : (\n                  <StepIndicator\n                    step={stepNumber}\n                    disableStepIndicators={disableStepIndicators}\n                    currentStep={currentStep}\n                    onClickStep={clicked => {\n                      setDirection(clicked > currentStep ? 1 : -1);\n                      updateStep(clicked);\n                    }}\n                  />\n                )}\n                {isNotLastStep && <StepConnector isComplete={currentStep > stepNumber} />}\n              </React.Fragment>\n            );\n          })}\n        </div>\n\n        <StepContentWrapper\n          isCompleted={isCompleted}\n          currentStep={currentStep}\n          direction={direction}\n          className={`step-content-default ${contentClassName}`}\n        >\n          {stepsArray[currentStep - 1]}\n        </StepContentWrapper>\n\n        {!isCompleted && (\n          <div className={`footer-container ${footerClassName}`}>\n            <div className={`footer-nav ${currentStep !== 1 ? 'spread' : 'end'}`}>\n              {currentStep !== 1 && (\n                <button\n                  onClick={handleBack}\n                  className={`back-button ${currentStep === 1 ? 'inactive' : ''}`}\n                  {...backButtonProps}\n                >\n                  {backButtonText}\n                </button>\n              )}\n              <button onClick={isLastStep ? handleComplete : handleNext} className=\"next-button\" {...nextButtonProps}>\n                {isLastStep ? 'Complete' : nextButtonText}\n              </button>\n            </div>\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n\ninterface StepContentWrapperProps {\n  isCompleted: boolean;\n  currentStep: number;\n  direction: number;\n  children: ReactNode;\n  className?: string;\n}\n\nfunction StepContentWrapper({ isCompleted, currentStep, direction, children, className }: StepContentWrapperProps) {\n  const [parentHeight, setParentHeight] = useState<number>(0);\n\n  return (\n    <motion.div\n      className={className}\n      style={{ position: 'relative', overflow: 'hidden' }}\n      animate={{ height: isCompleted ? 0 : parentHeight }}\n      transition={{ type: 'spring', duration: 0.4 }}\n    >\n      <AnimatePresence initial={false} mode=\"sync\" custom={direction}>\n        {!isCompleted && (\n          <SlideTransition key={currentStep} direction={direction} onHeightReady={h => setParentHeight(h)}>\n            {children}\n          </SlideTransition>\n        )}\n      </AnimatePresence>\n    </motion.div>\n  );\n}\n\ninterface SlideTransitionProps {\n  children: ReactNode;\n  direction: number;\n  onHeightReady: (h: number) => void;\n}\n\nfunction SlideTransition({ children, direction, onHeightReady }: SlideTransitionProps) {\n  const containerRef = useRef<HTMLDivElement | null>(null);\n\n  useLayoutEffect(() => {\n    if (containerRef.current) {\n      onHeightReady(containerRef.current.offsetHeight);\n    }\n  }, [children, onHeightReady]);\n\n  return (\n    <motion.div\n      ref={containerRef}\n      custom={direction}\n      variants={stepVariants}\n      initial=\"enter\"\n      animate=\"center\"\n      exit=\"exit\"\n      transition={{ duration: 0.4 }}\n      style={{ position: 'absolute', left: 0, right: 0, top: 0 }}\n    >\n      {children}\n    </motion.div>\n  );\n}\n\nconst stepVariants: Variants = {\n  enter: (dir: number) => ({\n    x: dir >= 0 ? '-100%' : '100%',\n    opacity: 0\n  }),\n  center: {\n    x: '0%',\n    opacity: 1\n  },\n  exit: (dir: number) => ({\n    x: dir >= 0 ? '50%' : '-50%',\n    opacity: 0\n  })\n};\n\ninterface StepProps {\n  children: ReactNode;\n}\n\nexport function Step({ children }: StepProps): JSX.Element {\n  return <div className=\"step-default\">{children}</div>;\n}\n\ninterface StepIndicatorProps {\n  step: number;\n  currentStep: number;\n  onClickStep: (step: number) => void;\n  disableStepIndicators?: boolean;\n}\n\nfunction StepIndicator({ step, currentStep, onClickStep, disableStepIndicators }: StepIndicatorProps) {\n  const status = currentStep === step ? 'active' : currentStep < step ? 'inactive' : 'complete';\n\n  const handleClick = () => {\n    if (step !== currentStep && !disableStepIndicators) {\n      onClickStep(step);\n    }\n  };\n\n  return (\n    <motion.div onClick={handleClick} className=\"step-indicator\" style={disableStepIndicators ? { pointerEvents: 'none', opacity: 0.5 } : {}} animate={status} initial={false}>\n      <motion.div\n        variants={{\n          inactive: { scale: 1, backgroundColor: '#222', color: '#a3a3a3' },\n          active: { scale: 1, backgroundColor: '#5227FF', color: '#5227FF' },\n          complete: { scale: 1, backgroundColor: '#5227FF', color: '#3b82f6' }\n        }}\n        transition={{ duration: 0.3 }}\n        className=\"step-indicator-inner\"\n      >\n        {status === 'complete' ? (\n          <CheckIcon className=\"check-icon\" />\n        ) : status === 'active' ? (\n          <div className=\"active-dot\" />\n        ) : (\n          <span className=\"step-number\">{step}</span>\n        )}\n      </motion.div>\n    </motion.div>\n  );\n}\n\ninterface StepConnectorProps {\n  isComplete: boolean;\n}\n\nfunction StepConnector({ isComplete }: StepConnectorProps) {\n  const lineVariants: Variants = {\n    incomplete: { width: 0, backgroundColor: 'transparent' },\n    complete: { width: '100%', backgroundColor: '#5227FF' }\n  };\n\n  return (\n    <div className=\"step-connector\">\n      <motion.div\n        className=\"step-connector-inner\"\n        variants={lineVariants}\n        initial={false}\n        animate={isComplete ? 'complete' : 'incomplete'}\n        transition={{ duration: 0.4 }}\n      />\n    </div>\n  );\n}\n\ninterface CheckIconProps extends React.SVGProps<SVGSVGElement> {}\n\nfunction CheckIcon(props: CheckIconProps) {\n  return (\n    <svg {...props} fill=\"none\" stroke=\"currentColor\" strokeWidth={2} viewBox=\"0 0 24 24\">\n      <motion.path\n        initial={{ pathLength: 0 }}\n        animate={{ pathLength: 1 }}\n        transition={{ delay: 0.1, type: 'tween', ease: 'easeOut', duration: 0.3 }}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        d=\"M5 13l4 4L19 7\"\n      />\n    </svg>\n  );\n}\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"motion@^12.23.12"
	]
}