{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "countdown",
  "title": "Countdown",
  "description": "Countdown from the Get IT Done design system.",
  "dependencies": [],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/labels-context.json",
    "https://zero-design.itdone.de/r/utils.json",
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/ui/countdown.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\n\nimport { cn } from '@/lib/utils';\nimport { useLabels } from '@/components/ui/labels-context';\n\nexport interface CountdownProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  // Target moment as Date, ISO string or epoch milliseconds.\n  to: Date | string | number;\n  // Called once when the target is reached.\n  onComplete?: () => void;\n  // Clock source, injectable for tests and server-synchronised time.\n  now?: () => number;\n  // Tick interval in ms. Default 1000.\n  intervalMs?: number;\n  // Hides the days segment when it is zero.\n  hideEmptyDays?: boolean;\n  // Visible title or announcement text, e.g. \"Launch in\". Added to the accessible name.\n  label?: string;\n  // Visible content once the target is reached, in place of the 00 digits. Defaults to the \"Time's up\" label. Pass false to keep the zeroed digits.\n  finishedText?: React.ReactNode;\n  // `blocks` is the segmented display (default). `inline` is one text line such as \"2d 4h 12m\" that drops leading zero units, for headers and badges.\n  variant?: 'blocks' | 'inline';\n}\n\nconst pad = (value: number) => String(value).padStart(2, '0');\n\n// Split a remaining duration in ms into whole units. Negative values count as zero.\nexport const splitDuration = (ms: number) => {\n  const total = Math.max(0, Math.floor(ms / 1000));\n  return { days: Math.floor(total / 86400), hours: Math.floor((total % 86400) / 3600), minutes: Math.floor((total % 3600) / 60), seconds: total % 60, done: total === 0 };\n};\n\n// Countdown to a moment. The ticking digits are hidden from screen readers. A polite live region announces the remaining time once a minute and the end.\nconst Countdown = React.forwardRef<HTMLDivElement, CountdownProps>(\n  ({ className, to, onComplete, now = Date.now, intervalMs = 1000, hideEmptyDays = false, label, finishedText, variant = 'blocks', ...props }, ref) => {\n    const l = useLabels();\n    const target = React.useMemo(() => new Date(to).getTime(), [to]);\n    const [remaining, setRemaining] = React.useState(() => target - now());\n    const completedRef = React.useRef(false);\n    const onCompleteRef = React.useRef(onComplete);\n    onCompleteRef.current = onComplete;\n    const nowRef = React.useRef(now);\n    nowRef.current = now;\n\n    React.useEffect(() => {\n      completedRef.current = false;\n      const tick = () => {\n        const next = target - nowRef.current();\n        setRemaining(next);\n        if (next <= 0 && !completedRef.current) {\n          completedRef.current = true;\n          onCompleteRef.current?.();\n        }\n      };\n      tick();\n      const id = setInterval(tick, intervalMs);\n      return () => clearInterval(id);\n    }, [target, intervalMs]);\n\n    const { days, hours, minutes, seconds, done } = splitDuration(remaining);\n    const segments = [\n      { key: 'days', value: days, text: l.countdownDays, hidden: hideEmptyDays && days === 0 },\n      { key: 'hours', value: hours, text: l.countdownHours, hidden: false },\n      { key: 'minutes', value: minutes, text: l.countdownMinutes, hidden: false },\n      { key: 'seconds', value: seconds, text: l.countdownSeconds, hidden: false },\n    ].filter((segment) => !segment.hidden);\n    // Coarse announcement: changes once per minute so the live region stays calm.\n    const announcement = done\n      ? l.countdownEnded\n      : [label, `${days} ${l.countdownDays}, ${hours} ${l.countdownHours}, ${minutes} ${l.countdownMinutes}`].filter(Boolean).join(' ');\n\n    if (variant === 'inline') {\n      // Largest units first, leading zero units dropped: d h m, then h m, then m s.\n      const parts =\n        days > 0\n          ? [`${days}${l.countdownDaysShort}`, `${hours}${l.countdownHoursShort}`, `${minutes}${l.countdownMinutesShort}`]\n          : hours > 0\n            ? [`${hours}${l.countdownHoursShort}`, `${minutes}${l.countdownMinutesShort}`]\n            : [`${minutes}${l.countdownMinutesShort}`, `${seconds}${l.countdownSecondsShort}`];\n      return (\n        <div ref={ref} role=\"timer\" data-done={done || undefined} data-variant=\"inline\" className={cn('inline-flex items-baseline gap-1 whitespace-nowrap', className)} {...props}>\n          <span className=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n            {announcement}\n          </span>\n          {done && finishedText !== false ? (\n            <span data-countdown-finished=\"\" className=\"typography-ui-semibold text-text-default\">\n              {finishedText ?? l.countdownFinished}\n            </span>\n          ) : (\n            <span aria-hidden=\"true\" className=\"typography-ui-semibold tabular-nums text-text-default\">\n              {parts.join(' ')}\n            </span>\n          )}\n        </div>\n      );\n    }\n\n    return (\n      <div ref={ref} role=\"timer\" data-done={done || undefined} className={cn('flex flex-col gap-2', className)} {...props}>\n        <span className=\"sr-only\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n          {announcement}\n        </span>\n        {done && finishedText !== false ? (\n          <p data-countdown-finished=\"\" className=\"typography-display-sm m-0 text-text-default\">\n            {finishedText ?? l.countdownFinished}\n          </p>\n        ) : (\n        <div aria-hidden=\"true\" className=\"flex items-start gap-4\">\n          {segments.map((segment) => (\n            <div key={segment.key} className=\"flex w-16 flex-col items-center gap-1\">\n              <span className=\"typography-display-sm tabular-nums text-text-default\">{pad(segment.value)}</span>\n              <span className=\"typography-text-extra-small text-text-subtle\">{segment.text}</span>\n            </div>\n          ))}\n        </div>\n        )}\n      </div>\n    );\n  },\n);\nCountdown.displayName = 'Countdown';\n\nexport { Countdown };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}