{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "progress",
  "title": "Progress",
  "description": "Displays an indicator showing the completion progress of a task.",
  "dependencies": [
    "react-native-reanimated"
  ],
  "registryDependencies": [
    "@tetra-ui/text"
  ],
  "files": [
    {
      "path": "ui/progress.tsx",
      "content": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport { createContext, useContext, useEffect, useMemo } from \"react\";\nimport { View } from \"react-native\";\nimport Animated, {\n  Easing,\n  useAnimatedStyle,\n  useSharedValue,\n  withTiming,\n} from \"react-native-reanimated\";\nimport { cn } from \"@/lib/utils\";\nimport { Text } from \"./text\";\n\n// Constants\nconst ANIMATION_DURATION = 320;\nconst ANIMATION_EASING = Easing.out(Easing.cubic);\n\n// Types\ntype ProgressContextValue = VariantProps<typeof progressVariants> & {\n  value: number;\n  max: number;\n};\n\ntype ProgressProps = React.ComponentProps<typeof View> &\n  VariantProps<typeof progressVariants> & {\n    value?: number | null;\n    max?: number;\n  };\n\n// Context\nconst ProgressContext = createContext<ProgressContextValue | null>(null);\n\nconst useProgress = () => {\n  const context = useContext(ProgressContext);\n  if (!context) {\n    throw new Error(\"Progress components must be used within Progress\");\n  }\n  return context;\n};\n\nexport function Progress({\n  className,\n  children,\n  value,\n  max = 100,\n  variant = \"linear\",\n  ...props\n}: ProgressProps) {\n  const normalizedValue = value ?? 0;\n\n  const ctx = useMemo(\n    () => ({\n      max,\n      value: normalizedValue,\n      variant,\n    }),\n    [max, normalizedValue, variant]\n  );\n\n  return (\n    <ProgressContext.Provider value={ctx}>\n      <View\n        accessibilityRole=\"progressbar\"\n        accessibilityValue={{\n          max,\n          min: 0,\n          now: normalizedValue,\n        }}\n        className={cn(\"flex w-full flex-wrap gap-3\", className)}\n        data-slot=\"progress\"\n        {...props}\n      >\n        {children}\n        {variant === \"steps\" ? (\n          <ProgressSteps />\n        ) : (\n          <ProgressTrack>\n            <ProgressIndicator />\n          </ProgressTrack>\n        )}\n      </View>\n    </ProgressContext.Provider>\n  );\n}\n\nfunction ProgressTrack({\n  className,\n  ...props\n}: React.ComponentProps<typeof View>) {\n  return (\n    <View\n      className={cn(\n        progressVariants({ variant: \"linear\" }),\n        \"relative w-full overflow-hidden rounded-full bg-muted\",\n        className\n      )}\n      data-slot=\"progress-track\"\n      style={{ borderCurve: \"continuous\" }}\n      {...props}\n    />\n  );\n}\n\nfunction ProgressIndicator({\n  className,\n  ...props\n}: React.ComponentProps<typeof Animated.View>) {\n  const { max, value } = useProgress();\n  const targetProgress = max > 0 ? value / max : 0;\n  const fillProgress = useSharedValue(targetProgress);\n\n  useEffect(() => {\n    fillProgress.value = withTiming(targetProgress, {\n      duration: ANIMATION_DURATION,\n      easing: ANIMATION_EASING,\n    });\n  }, [fillProgress, targetProgress]);\n\n  const fillStyle = useAnimatedStyle(() => ({\n    width: `${fillProgress.value * 100}%`,\n  }));\n\n  return (\n    <Animated.View\n      className={cn(\"h-full bg-primary\", className)}\n      data-slot=\"progress-indicator\"\n      style={[fillStyle, { borderCurve: \"continuous\" }]}\n      {...props}\n    />\n  );\n}\n\nfunction ProgressSteps({ className }: { className?: string }) {\n  const { max, value } = useProgress();\n  const stepCount = Math.max(1, Math.round(max));\n  const clampedValue = Math.max(0, Math.min(value, max));\n  const fullSteps = Math.floor(clampedValue);\n  const partialStepFill = clampedValue - fullSteps;\n\n  const steps = useMemo(\n    () =>\n      Array.from({ length: stepCount }, (_, stepIndex) => {\n        const stepNumber = stepIndex + 1;\n        let fill = 0;\n\n        if (stepIndex < fullSteps) {\n          fill = 1;\n        } else if (stepIndex === fullSteps) {\n          fill = partialStepFill;\n        }\n\n        return { fill, stepNumber };\n      }),\n    [fullSteps, partialStepFill, stepCount]\n  );\n\n  return (\n    <View\n      className={cn(\n        progressVariants({ variant: \"steps\" }),\n        \"w-full flex-row gap-1\",\n        className\n      )}\n      data-slot=\"progress-track\"\n    >\n      {steps.map((step) => (\n        <ProgressStep fill={step.fill} key={step.stepNumber} />\n      ))}\n    </View>\n  );\n}\n\nfunction ProgressStep({ fill }: { fill: number }) {\n  const fillProgress = useSharedValue(fill);\n\n  useEffect(() => {\n    fillProgress.value = withTiming(fill, {\n      duration: ANIMATION_DURATION,\n      easing: ANIMATION_EASING,\n    });\n  }, [fill, fillProgress]);\n\n  const fillStyle = useAnimatedStyle(() => ({\n    width: `${fillProgress.value * 100}%`,\n  }));\n\n  return (\n    <View\n      className=\"relative h-full flex-1 overflow-hidden rounded-full bg-muted\"\n      data-slot=\"progress-step\"\n      style={{ borderCurve: \"continuous\" }}\n    >\n      <Animated.View\n        className=\"h-full bg-primary\"\n        data-slot=\"progress-indicator\"\n        style={[fillStyle, { borderCurve: \"continuous\" }]}\n      />\n    </View>\n  );\n}\n\nexport function ProgressLabel({\n  className,\n  ...props\n}: React.ComponentProps<typeof Text>) {\n  return (\n    <Text\n      className={cn(\"font-medium text-sm\", className)}\n      data-slot=\"progress-label\"\n      {...props}\n    />\n  );\n}\n\nexport function ProgressValue({\n  className,\n  ...props\n}: React.ComponentProps<typeof Text>) {\n  const { max, value } = useProgress();\n  const percent = max > 0 ? Math.round((value / max) * 100) : 0;\n\n  return (\n    <Text\n      className={cn(\n        \"ml-auto text-muted-foreground text-sm tabular-nums\",\n        className\n      )}\n      data-slot=\"progress-value\"\n      {...props}\n    >\n      {percent}%\n    </Text>\n  );\n}\n\n// Styles\nexport const progressVariants = cva(\"h-1.5 w-full flex-row gap-1\", {\n  defaultVariants: {\n    variant: \"linear\",\n  },\n  variants: {\n    variant: {\n      linear: \"\",\n      steps: \"\",\n    },\n  },\n});\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}
