{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "progress",
  "title": "Progress",
  "description": "The linear progress indicator, determinate and indeterminate, with the M3 Expressive wave.",
  "dependencies": [
    "@base-ui/react@^1.7.0"
  ],
  "registryDependencies": [
    "https://guillermo-rebolledo.github.io/materialcn/r/materialcn-theme.json",
    "https://guillermo-rebolledo.github.io/materialcn/r/utils.json"
  ],
  "files": [
    {
      "path": "src/components/ui/progress.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Progress as ProgressPrimitive } from \"@base-ui/react/progress\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype ProgressVariant = \"flat\" | \"wavy\"\n\ntype ProgressContextValue = {\n  variant: ProgressVariant\n  /** 0–100, or null while indeterminate. */\n  percent: number | null\n}\n\nconst ProgressContext = React.createContext<ProgressContextValue>({\n  variant: \"flat\",\n  percent: null,\n})\n\n/**\n * Material 3 linear progress.\n *\n * `flat` is the classic 4dp bar. `wavy` is the Expressive one: the active\n * indicator is a travelling sine wave — 40dp wavelength, 6dp peak-to-peak\n * inside a 12dp box, matching the kit's `wave-increment` geometry. The wave\n * is a repeating mask rather than an inline path, so it tiles to any width at\n * no DOM cost and still takes its colour from the theme.\n *\n * Both variants keep the M3 details that make the bar read as Material: a gap\n * between the active indicator and the remaining track, and a stop dot marking\n * the full value.\n */\nfunction Progress({\n  className,\n  children,\n  value,\n  min = 0,\n  max = 100,\n  variant = \"flat\",\n  ...props\n}: ProgressPrimitive.Root.Props & { variant?: ProgressVariant }) {\n  // The wavy track has to know where the indicator ends, because — unlike the\n  // flat bar, which the indicator simply paints over — a sine leaves gaps that\n  // a full-width rule would show through.\n  const percent =\n    typeof value === \"number\" && max > min\n      ? Math.min(100, Math.max(0, ((value - min) / (max - min)) * 100))\n      : null\n\n  const context = React.useMemo(\n    () => ({ variant, percent }),\n    [variant, percent],\n  )\n\n  return (\n    <ProgressContext value={context}>\n      <ProgressPrimitive.Root\n        value={value}\n        min={min}\n        max={max}\n        data-slot=\"progress\"\n        data-variant={variant}\n        className={cn(\"flex flex-wrap gap-3\", className)}\n        {...props}\n      >\n        {children}\n        <ProgressTrack>\n          <ProgressIndicator />\n        </ProgressTrack>\n      </ProgressPrimitive.Root>\n    </ProgressContext>\n  )\n}\n\nfunction ProgressTrack({\n  className,\n  style,\n  ...props\n}: ProgressPrimitive.Track.Props) {\n  const { variant, percent } = React.use(ProgressContext)\n\n  return (\n    <ProgressPrimitive.Track\n      data-slot=\"progress-track\"\n      data-variant={variant}\n      style={{\n        ...({\n          \"--m3-progress\": percent === null ? \"0%\" : `${percent}%`,\n        } as React.CSSProperties),\n        ...style,\n      }}\n      className={cn(\n        \"relative flex w-full items-center\",\n        // The stop dot marking the end of the track.\n        \"after:absolute after:right-0 after:size-1 after:rounded-m3-full after:bg-m3-primary\",\n        // Flat: the track *is* the 4dp bar.\n        variant === \"flat\" &&\n          \"h-1 rounded-m3-full bg-m3-secondary-container\",\n        // Wavy: the box is 12dp tall to hold the wave, so the inactive track is\n        // drawn as a centred 4dp rule instead of as the container itself.\n        variant === \"wavy\" && [\n          \"h-3\",\n          // Starts where the indicator ends (plus the 4dp gap), so the rule\n          // never shows through the troughs of the wave.\n          \"before:absolute before:top-1/2 before:right-0 before:h-1 before:-translate-y-1/2\",\n          \"before:left-[calc(var(--m3-progress)+4px)]\",\n          \"before:rounded-m3-full before:bg-m3-secondary-container\",\n        ],\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ProgressIndicator({\n  className,\n  ...props\n}: ProgressPrimitive.Indicator.Props) {\n  const { variant } = React.use(ProgressContext)\n\n  return (\n    <ProgressPrimitive.Indicator\n      data-slot=\"progress-indicator\"\n      data-variant={variant}\n      className={cn(\n        \"h-full\",\n        // The gap between the active indicator and the remaining track is\n        // painted in the surface color, so a progress bar on a different\n        // container should override `shadow-*` to match it.\n        \"shadow-[4px_0_0_0_var(--m3-surface)]\",\n        // Width is clamped at zero, so the morph rides the effects spring.\n        \"transition-[width] duration-(--m3-spring-effects-default-duration) ease-(--m3-spring-effects-default)\",\n        variant === \"flat\" && \"rounded-m3-full bg-m3-primary\",\n        // `relative` lifts the wave above the track rule drawn by ::before.\n        variant === \"wavy\" && \"m3-wave relative bg-m3-primary\",\n        className,\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {\n  return (\n    <ProgressPrimitive.Label\n      className={cn(\"text-m3-label-lg text-m3-on-surface\", className)}\n      data-slot=\"progress-label\"\n      {...props}\n    />\n  )\n}\n\nfunction ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {\n  return (\n    <ProgressPrimitive.Value\n      className={cn(\n        \"ml-auto text-m3-label-lg text-m3-on-surface-variant tabular-nums\",\n        className,\n      )}\n      data-slot=\"progress-value\"\n      {...props}\n    />\n  )\n}\n\nexport {\n  Progress,\n  ProgressTrack,\n  ProgressIndicator,\n  ProgressLabel,\n  ProgressValue,\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "feedback"
  ],
  "type": "registry:ui"
}