{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"progress","title":"Progress","description":"Native determinate or indeterminate task progress.","dependencies":["@base-ui/react@1.7.0","class-variance-authority@0.7.1"],"registryDependencies":["@siteplane/base"],"files":[{"path":"packages/ui/src/components/progress/progress.tsx","content":"\"use client\";\n\nimport { Progress as ProgressPrimitive } from \"@base-ui/react/progress\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport {\n  type SiteplaneControlSize,\n  siteplaneUIDefaults,\n  useResolvedControlSize,\n} from \"../../system/provider/siteplane-ui-provider\";\nimport {\n  linearIndicatorClassNames,\n  linearIndicatorSizeClassNames,\n} from \"../../system/styles/surface\";\nimport { cn, type WithStringClassName } from \"../../system/utils/cn\";\nimport { slotClass } from \"../../system/utils/slot-class\";\n\nimport \"./progress.css\";\n\nconst PROGRESS_DEFAULT_LOCALE = \"en-US\";\n\n// Progress interpolates incoming values evenly (linear, 380 ms, the kit's\n// linger token) so a stream of updates never brakes at every step; the\n// transition is progress.css's, and so is the moving fill of an indeterminate\n// bar (`value={null}`). Meter keeps the shared indicator transition.\nconst progressIndicatorMotionClassName = \"min-w-0\";\n\nconst progressRootVariants = cva(linearIndicatorClassNames.root, {\n  defaultVariants: { size: siteplaneUIDefaults.controlSize },\n  variants: { size: linearIndicatorSizeClassNames.root },\n});\n\nconst progressLabelVariants = cva(linearIndicatorClassNames.label, {\n  defaultVariants: { size: siteplaneUIDefaults.controlSize },\n  variants: { size: linearIndicatorSizeClassNames.label },\n});\n\nconst progressTrackVariants = cva(linearIndicatorClassNames.track, {\n  defaultVariants: { size: siteplaneUIDefaults.controlSize },\n  variants: { size: linearIndicatorSizeClassNames.track },\n});\n\nconst progressValueVariants = cva(linearIndicatorClassNames.value, {\n  defaultVariants: { size: siteplaneUIDefaults.controlSize },\n  variants: { size: linearIndicatorSizeClassNames.value },\n});\n\nconst progressCircleVariants = cva(\"relative inline-flex shrink-0\", {\n  defaultVariants: { size: siteplaneUIDefaults.controlSize },\n  variants: {\n    size: { md: \"size-7\", lg: \"size-8\", sm: \"size-5\", xl: \"size-10\" },\n  },\n});\n\nconst ProgressSizeContext = React.createContext<SiteplaneControlSize | null>(\n  null,\n);\n\nexport interface ProgressProps\n  extends\n    Omit<WithStringClassName<ProgressPrimitive.Root.Props>, \"size\">,\n    VariantProps<typeof progressRootVariants> {}\n\nexport function Progress({\n  className,\n  children,\n  locale = PROGRESS_DEFAULT_LOCALE,\n  size,\n  ...props\n}: ProgressProps): React.ReactElement {\n  const resolvedSize = useResolvedControlSize(size);\n  return (\n    <ProgressSizeContext.Provider value={resolvedSize}>\n      <ProgressPrimitive.Root\n        className={cn(\n          slotClass(\"progress\", props),\n          progressRootVariants({ size: resolvedSize }),\n          className,\n        )}\n        data-size={resolvedSize}\n        data-slot=\"progress\"\n        locale={locale}\n        {...props}\n      >\n        {children ? (\n          children\n        ) : (\n          <ProgressTrack>\n            <ProgressIndicator />\n          </ProgressTrack>\n        )}\n      </ProgressPrimitive.Root>\n    </ProgressSizeContext.Provider>\n  );\n}\n\nexport interface ProgressLabelProps\n  extends\n    Omit<WithStringClassName<ProgressPrimitive.Label.Props>, \"size\">,\n    VariantProps<typeof progressLabelVariants> {}\n\nexport function ProgressLabel({\n  className,\n  size,\n  ...props\n}: ProgressLabelProps): React.ReactElement {\n  const groupSize = React.useContext(ProgressSizeContext);\n  const resolvedSize = useResolvedControlSize(size ?? groupSize);\n  return (\n    <ProgressPrimitive.Label\n      className={cn(\n        slotClass(\"progress-label\", props),\n        progressLabelVariants({ size: resolvedSize }),\n        className,\n      )}\n      data-size={resolvedSize}\n      data-slot=\"progress-label\"\n      {...props}\n    />\n  );\n}\n\nexport interface ProgressTrackProps\n  extends\n    Omit<WithStringClassName<ProgressPrimitive.Track.Props>, \"size\">,\n    VariantProps<typeof progressTrackVariants> {}\n\nexport function ProgressTrack({\n  className,\n  size,\n  ...props\n}: ProgressTrackProps): React.ReactElement {\n  const groupSize = React.useContext(ProgressSizeContext);\n  const resolvedSize = useResolvedControlSize(size ?? groupSize);\n  return (\n    <ProgressPrimitive.Track\n      className={cn(\n        slotClass(\"progress-track\", props),\n        progressTrackVariants({ size: resolvedSize }),\n        className,\n      )}\n      data-size={resolvedSize}\n      data-slot=\"progress-track\"\n      {...props}\n    />\n  );\n}\n\nexport function ProgressIndicator({\n  className,\n  ...props\n}: WithStringClassName<ProgressPrimitive.Indicator.Props>): React.ReactElement {\n  return (\n    <ProgressPrimitive.Indicator\n      className={cn(\n        slotClass(\"progress-indicator\", props),\n        linearIndicatorClassNames.fill,\n        progressIndicatorMotionClassName,\n        className,\n      )}\n      data-slot=\"progress-indicator\"\n      {...props}\n    />\n  );\n}\n\nexport interface ProgressValueProps\n  extends\n    Omit<WithStringClassName<ProgressPrimitive.Value.Props>, \"size\">,\n    VariantProps<typeof progressValueVariants> {}\n\nexport function ProgressValue({\n  className,\n  size,\n  ...props\n}: ProgressValueProps): React.ReactElement {\n  const groupSize = React.useContext(ProgressSizeContext);\n  const resolvedSize = useResolvedControlSize(size ?? groupSize);\n  return (\n    <ProgressPrimitive.Value\n      className={cn(\n        slotClass(\"progress-value\", props),\n        progressValueVariants({ size: resolvedSize }),\n        className,\n      )}\n      data-size={resolvedSize}\n      data-slot=\"progress-value\"\n      {...props}\n    />\n  );\n}\n\nexport interface ProgressCircleProps\n  extends\n    Omit<\n      WithStringClassName<ProgressPrimitive.Root.Props>,\n      \"children\" | \"size\" | \"value\"\n    >,\n    VariantProps<typeof progressCircleVariants> {\n  /**\n   * The completion between `min` and `max`. `null` shows an indeterminate\n   * circle, an arc that travels around it, while completion is unknown.\n   */\n  value: number | null;\n}\n\nexport function ProgressCircle({\n  className,\n  locale = PROGRESS_DEFAULT_LOCALE,\n  max = 100,\n  min = 0,\n  size,\n  value,\n  ...props\n}: ProgressCircleProps): React.ReactElement {\n  const resolvedSize = useResolvedControlSize(size);\n  const range = max - min;\n  const numericValue =\n    value === null ? null : Number.isFinite(value) ? value : min;\n  const percentage =\n    numericValue === null || range <= 0\n      ? 0\n      : Math.min(100, Math.max(0, ((numericValue - min) / range) * 100));\n\n  return (\n    <ProgressPrimitive.Root\n      className={cn(\n        slotClass(\"progress-circle\", props),\n        progressCircleVariants({ size: resolvedSize }),\n        className,\n      )}\n      data-size={resolvedSize}\n      data-slot=\"progress-circle\"\n      locale={locale}\n      max={max}\n      min={min}\n      value={numericValue}\n      {...props}\n    >\n      <svg\n        aria-hidden=\"true\"\n        className=\"size-full -rotate-90\"\n        viewBox=\"0 0 48 48\"\n      >\n        <circle\n          className=\"fill-none\"\n          cx=\"24\"\n          cy=\"24\"\n          pathLength=\"100\"\n          r=\"20\"\n          strokeWidth=\"4\"\n        />\n        <circle\n          className=\"fill-none\"\n          cx=\"24\"\n          cy=\"24\"\n          pathLength=\"100\"\n          r=\"20\"\n          strokeDasharray=\"100\"\n          strokeDashoffset={100 - percentage}\n          // A round cap draws a dot even for an empty arc, so an empty circle\n          // takes a flat one and shows nothing until it has progress.\n          strokeLinecap={\n            numericValue === null || percentage > 0 ? \"round\" : \"butt\"\n          }\n          strokeWidth=\"4\"\n        />\n      </svg>\n    </ProgressPrimitive.Root>\n  );\n}\n\nexport { ProgressPrimitive };\n","type":"registry:ui","target":"~/src/siteplane/components/progress/progress.tsx"},{"path":"packages/ui/src/components/progress/progress.css","content":"@layer theme, base, components, siteplane, utilities;\n\n@layer siteplane {\n  /* The value transitions: the bar moves linearly over the linger time, the\n   * circle's arc over the slow time. Zero specificity, so the reduced-motion\n   * rules below and a consumer's transition utility both win. */\n  :where(.sp-progress-indicator) {\n    transition-property: width;\n    transition-timing-function: var(--motion-ease-linear);\n    transition-duration: var(--motion-duration-linger);\n  }\n\n  :where(.sp-progress-circle circle:last-child) {\n    transition-property: stroke-dashoffset;\n    transition-timing-function: var(--motion-ease-linear);\n    transition-duration: var(--motion-duration-slow);\n  }\n\n  /* Indeterminate progress (`value={null}`): the bar fills from its start\n   * again and again, and an arc of a quarter circle travels around the circle.\n   * Base UI marks every part data-indeterminate. The continuous-fill class\n   * draws the same fill on an indicator of its own. */\n  :where(.sp-progress-indicator[data-indeterminate]),\n  :where(.sp-progress-continuous-fill) {\n    height: 100%;\n    animation: sp-progress-continuous-fill\n      calc(var(--motion-duration-linger) * 8) var(--motion-ease-linear) infinite;\n    transform: scaleX(0);\n    transform-origin: left center;\n    will-change: transform;\n  }\n\n  /* A right-to-left page: the dir attribute is named beside :dir(rtl), which a\n   * build for older browsers turns into a list of right-to-left languages, as\n   * Tailwind's rtl variant does. */\n  :where(\n    .sp-progress-indicator[data-indeterminate]:is(\n        :dir(rtl),\n        [dir=\"rtl\"],\n        [dir=\"rtl\"] *\n      )\n  ),\n  :where(\n    .sp-progress-continuous-fill:is(:dir(rtl), [dir=\"rtl\"], [dir=\"rtl\"] *)\n  ) {\n    transform-origin: right center;\n  }\n\n  @keyframes sp-progress-continuous-fill {\n    from {\n      transform: scaleX(0);\n    }\n    to {\n      transform: scaleX(1);\n    }\n  }\n\n  :where(.sp-progress-circle[data-indeterminate] circle:last-child) {\n    stroke-dasharray: 25 75;\n    animation: sp-progress-circle-indeterminate\n      calc(var(--motion-duration-loop) * 2) var(--motion-ease-linear) infinite;\n  }\n\n  @keyframes sp-progress-circle-indeterminate {\n    from {\n      stroke-dashoffset: 0;\n    }\n    to {\n      stroke-dashoffset: -100;\n    }\n  }\n\n  /* Reduced motion moves the fill and the arc to each new value at once and\n   * holds the indeterminate state still (M5): the bar keeps a segment in its\n   * middle and the circle an arc centred on its top, so neither reads as a\n   * value or as done. The document mode, the provider preset and the system\n   * setting each switch it. */\n  html[data-motion-reduce=\"true\"] .sp-progress-indicator,\n  html[data-motion-reduce=\"true\"] .sp-progress-circle circle,\n  [data-sp-motion=\"reduced\"] .sp-progress-indicator,\n  [data-sp-motion=\"reduced\"] .sp-progress-circle circle {\n    transition: none;\n  }\n\n  html[data-motion-reduce=\"true\"] .sp-progress-indicator[data-indeterminate],\n  html[data-motion-reduce=\"true\"] .sp-progress-continuous-fill,\n  [data-sp-motion=\"reduced\"] .sp-progress-indicator[data-indeterminate],\n  [data-sp-motion=\"reduced\"] .sp-progress-continuous-fill {\n    animation: none;\n    transform: none;\n    clip-path: inset(0 30% round 9999px);\n  }\n\n  html[data-motion-reduce=\"true\"]\n    .sp-progress-circle[data-indeterminate]\n    circle:last-child,\n  [data-sp-motion=\"reduced\"]\n    .sp-progress-circle[data-indeterminate]\n    circle:last-child {\n    animation: none;\n    stroke-dashoffset: -87.5;\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    :root .sp-progress-indicator,\n    :root .sp-progress-circle circle {\n      transition: none;\n    }\n\n    :root .sp-progress-indicator[data-indeterminate],\n    :root .sp-progress-continuous-fill {\n      animation: none;\n      transform: none;\n      clip-path: inset(0 30% round 9999px);\n    }\n\n    :root .sp-progress-circle[data-indeterminate] circle:last-child {\n      animation: none;\n      stroke-dashoffset: -87.5;\n    }\n  }\n}\n","type":"registry:ui","target":"~/src/siteplane/components/progress/progress.css"},{"path":"packages/ui/src/components/progress/index.ts","content":"export * from \"./progress\";\n","type":"registry:ui","target":"~/src/siteplane/components/progress/index.ts"}],"type":"registry:ui","docs":"https://ui.siteplane.io/docs-markdown/components/progress.md","categories":["Feedback"],"meta":{"avoidWhen":["Avoid for a measurement within a known range, such as storage used; use Meter.","Avoid for a short wait without measurable progress; use Spinner."],"description":"Native determinate or indeterminate task progress.","knownLimitations":["Timing, cancellation and failures belong to the application; keep the accessible label stable as the value changes."],"providerNotes":[],"releaseStatus":"stable","searchTerms":["progress"],"useWhen":["Use to show how much of an operation has completed, or that it is running when the amount is unknown."]}}