{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "progress-bar", "type": "registry:ui", "title": "ProgressBar", "description": "The accent progress primitive: determinate fill, busy sweep, open-ended indicator, or segment accumulation against a tally.", "categories": [ "display" ], "dependencies": [ "class-variance-authority" ], "registryDependencies": [ "https://whiskeyjack.net/r/use-reduced-motion.json", "https://whiskeyjack.net/r/utils.json" ], "files": [ { "path": "components/ui/progress-bar.tsx", "type": "registry:ui", "target": "components/ui/progress-bar.tsx", "content": "import * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@/lib/utils'\nimport { useReducedMotion } from '@/hooks/use-reduced-motion'\n\nconst progressBarVariants = cva('relative block w-full overflow-hidden rounded-full', {\n variants: {\n size: {\n xs: 'h-[3px]',\n sm: 'h-1',\n md: 'h-2',\n lg: 'h-3',\n },\n },\n defaultVariants: { size: 'md' },\n})\n\n// Dot diameter per size -- the segment geometry (gaps, pill width, the faint\n// filler pattern) derives from it. Keep in sync with the height classes above.\nconst DOT_PX = { xs: 3, sm: 4, md: 8, lg: 12 } as const\n\n// Segment accumulation: every SEGMENT_SPAN filled dots collapse into one solid\n// pill of PILL_DOT_FACTOR dot-widths, so an open-ended tally stays compact.\nconst SEGMENT_SPAN = 10\nconst PILL_DOT_FACTOR = 3\n\nexport interface ProgressBarProps extends VariantProps {\n /** Progress as a fraction from 0 to 1 (clamped). Ignored when `indeterminate`. */\n value?: number\n /**\n * Open-ended / no-target progress. With `count` it renders segment\n * accumulation (see `count`); without one it renders the plain dotted accent\n * line. The static counterpart to a native with no value -- it\n * means \"unbounded\", not \"loading\", so it does not animate. Used for\n * Chip Away's free-streak / count goals.\n */\n indeterminate?: boolean\n /**\n * The tally behind an `indeterminate` bar (check-ins, streak days, ...).\n * Renders segment accumulation: each unit fills one solid accent dot on a row\n * of faint track-colored dots, and every 10 filled dots collapse into a solid\n * pill a few dot-widths wide -- so the tally can grow past the track width.\n * 0 shows the all-faint default state. Ignored without `indeterminate`.\n */\n count?: number\n /**\n * Work is in flight and its next step cannot be counted yet.\n *\n * Distinct from `indeterminate`, which means *unbounded* and renders a static\n * dotted line: `busy` is *loading*, and lays a sweeping accent band over the\n * track. Combine it with a `value` for a run whose stages are only partly\n * countable -- the bar holds its fill and keeps a sign of life through the\n * stage that cannot report a count. Reduced motion holds the band still, so\n * the surrounding text has to carry the meaning either way.\n */\n busy?: boolean\n /**\n * Accessible label. With a value the bar exposes role=\"progressbar\" +\n * aria-valuenow (indeterminate omits aria-valuenow, per ARIA). Omit, or pass\n * `decorative`, when adjacent text already conveys the value (e.g. the TabBar\n * underline beneath a \"Day 6 of 10\" line).\n */\n label?: string\n /** Render purely decorative (aria-hidden, no role). */\n decorative?: boolean\n className?: string\n /** Inline styles for the root element. */\n style?: React.CSSProperties\n}\n\nconst trackColor =\n 'var(--wj-progress-track, color-mix(in srgb, var(--color-accent-500) 35%, transparent))'\n\n/**\n * Progress bar / indicator. Rendered with s (display fixed via classes)\n * so it can sit inside a