{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "countdown",
  "title": "Countdown",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/countdown/Countdown.tsx",
      "content": "import * as React from 'react'\nimport { cn } from '@/lib/utils'\n\ntype Format = 'DD:HH:MM:SS' | 'HH:MM:SS' | 'MM:SS' | 'SS'\n\nexport interface CountdownRenderProps {\n  days: number\n  hours: number\n  minutes: number\n  seconds: number\n  display: string\n  finished: boolean\n}\n\nexport interface CountdownProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n  /** Target date/time. Accepts a Date, ISO string, or epoch ms number. */\n  target: Date | string | number\n  /** Display format. Custom tokens: DD days, HH hours, MM minutes, SS seconds. */\n  format?: Format | string\n  /** Pause the countdown. */\n  paused?: boolean\n  /** Optional label rendered above the countdown. */\n  label?: string\n  /** Show leading zeros (e.g. 05 vs 5). */\n  pad?: boolean\n  /** Separator between units. */\n  separator?: string\n  /** Fired once when the countdown reaches zero. */\n  onFinish?: () => void\n  /** Fired every tick with the remaining ms. */\n  onTick?: (remaining: number) => void\n  /** Render-prop override for the whole display. Receives all parts + display string. */\n  children?: ((props: CountdownRenderProps) => React.ReactNode) | React.ReactNode\n  /** Render-prop override for the days unit. */\n  renderDays?: (days: number) => React.ReactNode\n  /** Render-prop override for the hours unit. */\n  renderHours?: (hours: number) => React.ReactNode\n  /** Render-prop override for the minutes unit. */\n  renderMinutes?: (minutes: number) => React.ReactNode\n  /** Render-prop override for the seconds unit. */\n  renderSeconds?: (seconds: number) => React.ReactNode\n}\n\nconst Countdown = React.forwardRef<HTMLDivElement, CountdownProps>(\n  (\n    {\n      target,\n      format = 'DD:HH:MM:SS',\n      paused = false,\n      label = '',\n      pad = true,\n      separator = ':',\n      onFinish,\n      onTick,\n      children,\n      renderDays,\n      renderHours,\n      renderMinutes,\n      renderSeconds,\n      className,\n      ...props\n    },\n    ref,\n  ) => {\n    const [now, setNow] = React.useState(() => Date.now())\n    const [finished, setFinished] = React.useState(false)\n\n    const targetMs = React.useMemo(() => {\n      if (target instanceof Date) return target.getTime()\n      if (typeof target === 'number') return target\n      return new Date(target).getTime()\n    }, [target])\n\n    const remainingMs = Math.max(0, targetMs - now)\n\n    const parts = React.useMemo(() => {\n      const total = remainingMs\n      const days = Math.floor(total / 86_400_000)\n      const hours = Math.floor((total % 86_400_000) / 3_600_000)\n      const minutes = Math.floor((total % 3_600_000) / 60_000)\n      const seconds = Math.floor((total % 60_000) / 1000)\n      return { days, hours, minutes, seconds }\n    }, [remainingMs])\n\n    const pad2 = React.useCallback((n: number) => (pad ? String(n).padStart(2, '0') : String(n)), [pad])\n\n    const display = React.useMemo(() => {\n      const f = format\n      const { days, hours, minutes, seconds } = parts\n      const sep = separator\n      if (f === 'DD:HH:MM:SS') return `${pad2(days)}${sep}${pad2(hours)}${sep}${pad2(minutes)}${sep}${pad2(seconds)}`\n      if (f === 'HH:MM:SS') return `${pad2(days * 24 + hours)}${sep}${pad2(minutes)}${sep}${pad2(seconds)}`\n      if (f === 'MM:SS') return `${pad2(days * 24 * 60 + hours * 60 + minutes)}${sep}${pad2(seconds)}`\n      if (f === 'SS') return pad2(Math.floor(remainingMs / 1000))\n      // Custom token format: replace DD, HH, MM, SS tokens.\n      return f\n        .replace('DD', pad2(days))\n        .replace('HH', pad2(hours))\n        .replace('MM', pad2(minutes))\n        .replace('SS', pad2(seconds))\n    }, [format, parts, separator, pad2, remainingMs])\n\n    // Keep latest callbacks without retriggering the interval effect.\n    const onFinishRef = React.useRef(onFinish)\n    const onTickRef = React.useRef(onTick)\n    onFinishRef.current = onFinish\n    onTickRef.current = onTick\n\n    React.useEffect(() => {\n      if (paused) return\n      const timer = setInterval(() => {\n        const next = Date.now()\n        const remaining = Math.max(0, targetMs - next)\n        onTickRef.current?.(remaining)\n        if (remaining <= 0) {\n          setFinished(true)\n          onFinishRef.current?.()\n        }\n        setNow(next)\n      }, 1000)\n      return () => clearInterval(timer)\n    }, [paused, targetMs])\n\n    // Reset finished state when the target changes.\n    React.useEffect(() => {\n      setFinished(false)\n      setNow(Date.now())\n    }, [targetMs])\n\n    const renderProps: CountdownRenderProps = {\n      ...parts,\n      display,\n      finished,\n    }\n\n    const renderDefault = () => {\n      const f = format\n      return (\n        <>\n          {f.includes('DD') &&\n            (renderDays ? (\n              renderDays(parts.days)\n            ) : (\n              <span data-slot=\"countdown-days\" className=\"text-foreground text-2xl font-semibold\">\n                {pad2(parts.days)}\n              </span>\n            ))}\n          {f.includes('DD') && f.includes('HH') && <span className=\"text-muted-foreground text-2xl\">{separator}</span>}\n          {f.includes('HH') &&\n            (renderHours ? (\n              renderHours(parts.hours)\n            ) : (\n              <span data-slot=\"countdown-hours\" className=\"text-foreground text-2xl font-semibold\">\n                {pad2(parts.hours)}\n              </span>\n            ))}\n          {f.includes('HH') && f.includes('MM') && <span className=\"text-muted-foreground text-2xl\">{separator}</span>}\n          {f.includes('MM') &&\n            (renderMinutes ? (\n              renderMinutes(parts.minutes)\n            ) : (\n              <span data-slot=\"countdown-minutes\" className=\"text-foreground text-2xl font-semibold\">\n                {pad2(parts.minutes)}\n              </span>\n            ))}\n          {f.includes('MM') && f.includes('SS') && <span className=\"text-muted-foreground text-2xl\">{separator}</span>}\n          {f.includes('SS') &&\n            (renderSeconds ? (\n              renderSeconds(parts.seconds)\n            ) : (\n              <span data-slot=\"countdown-seconds\" className=\"text-foreground text-2xl font-semibold\">\n                {pad2(parts.seconds)}\n              </span>\n            ))}\n        </>\n      )\n    }\n\n    return (\n      <div\n        ref={ref}\n        data-uipkge=\"\"\n        data-slot=\"countdown\"\n        data-finished={finished}\n        data-paused={paused}\n        className={cn('inline-flex flex-col gap-1', className)}\n        {...props}\n      >\n        {label && (\n          <span\n            data-slot=\"countdown-label\"\n            className=\"text-muted-foreground text-xs font-medium tracking-wide uppercase\"\n          >\n            {label}\n          </span>\n        )}\n        <div data-slot=\"countdown-display\" className=\"flex items-baseline gap-1 font-mono tabular-nums\">\n          {typeof children === 'function' ? children(renderProps) : (children ?? renderDefault())}\n        </div>\n      </div>\n    )\n  },\n)\nCountdown.displayName = 'Countdown'\n\nexport { Countdown }\n",
      "type": "registry:ui",
      "target": "~/components/ui/countdown/Countdown.tsx"
    },
    {
      "path": "packages/registry-react/components/countdown/index.ts",
      "content": "export { Countdown, type CountdownProps, type CountdownRenderProps } from './Countdown'\n",
      "type": "registry:ui",
      "target": "~/components/ui/countdown/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Countdown timer that shows time remaining to a target date. Supports DD:HH:MM:SS, HH:MM:SS, MM:SS, and SS formats plus custom token formats, render props for days/hours/minutes/seconds, a label, leading-zero padding, a custom separator, paused state, and on-finish/tick events.",
  "categories": [
    "display",
    "utility"
  ]
}