{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "timer",
  "type": "registry:ui",
  "description": "Countdown timer component with customizable duration and visual styles",
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/default/ui/timer.tsx",
      "content": "\"use client\"\n\nimport React, { useCallback, useEffect, useRef, useState } from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Clock } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst timerVariants = cva(\n  [\n    \"inline-flex items-center gap-2 rounded-full font-medium transition-all duration-200\",\n    \"\",\n  ],\n  {\n    variants: {\n      variant: {\n        default:\n          \"bg-background text-foreground border-border border shadow-[0_2px_4px_rgba(0,0,0,0.02),_0px_1px_2px_rgba(0,0,0,0.04)] shadow-[inset_0px_-2.10843px_0px_0px_hsl(var(--muted)),_0px_1.20482px_6.3253px_0px_hsl(var(--muted))]\",\n        outline:\n          \"border-input bg-background text-foreground border shadow-[0px_1px_0px_0px_hsla(0,_0%,_0%,_0.02)_inset,_0px_0px_0px_1px_hsla(0,_0%,_0%,_0.02)_inset,_0px_0px_0px_1px_rgba(255,_255,_255,_0.25)]\",\n        ghost: \"text-foreground bg-transparent\",\n        destructive:\n          \"bg-destructive/10 text-destructive border-destructive/20 border\",\n      },\n      size: {\n        sm: \"h-6 gap-1.5 px-2 py-1 text-xs\",\n        md: \"h-7 gap-2 px-2.5 py-1.5 text-sm\",\n        lg: \"h-8 gap-2.5 px-3 py-2 text-base\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"md\",\n    },\n  }\n)\n\nconst timerIconVariants = cva(\"transition-transform duration-[2000ms]\", {\n  variants: {\n    size: {\n      sm: \"h-3 w-3\",\n      md: \"h-3.5 w-3.5\",\n      lg: \"h-4 w-4\",\n    },\n    loading: {\n      true: \"animate-spin\",\n      false: \"\",\n    },\n  },\n  defaultVariants: {\n    size: \"md\",\n    loading: false,\n  },\n})\n\nconst timerDisplayVariants = cva(\"font-mono tracking-tight tabular-nums\", {\n  variants: {\n    size: {\n      sm: \"text-xs\",\n      md: \"text-sm\",\n      lg: \"text-base\",\n    },\n  },\n  defaultVariants: {\n    size: \"md\",\n  },\n})\n\nexport type TimerRootProps = {\n  /** Whether the timer is in loading/running state */\n  loading?: boolean\n} & VariantProps<typeof timerVariants> &\n  React.HTMLAttributes<HTMLDivElement>\n\nexport type TimerIconProps = {\n  /** Custom icon to display instead of default Clock */\n  icon?: React.ComponentType<{ className?: string }>\n} & VariantProps<typeof timerIconVariants> &\n  React.HTMLAttributes<HTMLDivElement>\n\nexport type TimerDisplayProps = {\n  /** Time value to display */\n  time: string\n  /** Optional label for accessibility */\n  label?: string\n} & VariantProps<typeof timerDisplayVariants> &\n  React.HTMLAttributes<HTMLDivElement>\n\nexport type UseTimerOptions = {\n  /** Whether the timer should be running */\n  loading?: boolean\n  /** Callback fired on each tick with elapsed time */\n  onTick?: (seconds: number, milliseconds: number) => void\n  /** Whether to reset timer when loading state changes */\n  resetOnLoadingChange?: boolean\n  /** Time format to use */\n  format?: \"SS.MS\" | \"MM:SS\" | \"HH:MM:SS\"\n}\n\nexport type UseTimerReturn = {\n  /** Total elapsed seconds */\n  elapsedTime: number\n  /** Current milliseconds (0-999) */\n  milliseconds: number\n  /** Formatted time strings */\n  formattedTime: {\n    seconds: string\n    milliseconds: string\n    display: string\n  }\n  /** Whether timer is currently running */\n  isRunning: boolean\n  /** Reset timer to 0 */\n  reset: () => void\n  /** Start the timer */\n  start: () => void\n  /** Stop the timer */\n  stop: () => void\n}\n\n/**\n * Root container for timer components\n */\nexport const TimerRoot = React.forwardRef<HTMLDivElement, TimerRootProps>(\n  ({ variant, size, loading, className, children, ...props }, ref) => {\n    return (\n      <div\n        ref={ref}\n        className={cn(timerVariants({ variant, size }), className)}\n        role=\"timer\"\n        aria-live=\"polite\"\n        aria-atomic=\"true\"\n        {...props}\n      >\n        {children}\n      </div>\n    )\n  }\n)\nTimerRoot.displayName = \"TimerRoot\"\n\n/**\n * Icon component for timer with loading animation\n */\nexport const TimerIcon = React.forwardRef<HTMLDivElement, TimerIconProps>(\n  ({ size, loading, icon: Icon = Clock, className, ...props }, ref) => {\n    return (\n      <div\n        ref={ref}\n        className={cn(timerIconVariants({ size, loading }), className)}\n        {...props}\n      >\n        <Icon className=\"h-full w-full\" />\n      </div>\n    )\n  }\n)\nTimerIcon.displayName = \"TimerIcon\"\n\n/**\n * Display component for formatted time\n */\nexport const TimerDisplay = React.forwardRef<HTMLDivElement, TimerDisplayProps>(\n  ({ size, time, label, className, ...props }, ref) => {\n    return (\n      <div\n        ref={ref}\n        className={cn(timerDisplayVariants({ size }), className)}\n        aria-label={label || `Timer: ${time}`}\n        {...props}\n      >\n        {time}\n      </div>\n    )\n  }\n)\nTimerDisplay.displayName = \"TimerDisplay\"\n\nexport type TimerProps = TimerRootProps &\n  UseTimerOptions & {\n    /** Time format to display */\n    format?: \"SS.MS\" | \"MM:SS\" | \"HH:MM:SS\"\n  } & React.HTMLAttributes<HTMLDivElement>\n\nexport const Timer = React.forwardRef<HTMLDivElement, TimerProps>(\n  (\n    {\n      loading = false,\n      onTick,\n      resetOnLoadingChange = true,\n      format = \"SS.MS\",\n      variant,\n      size,\n      className,\n      ...props\n    },\n    ref\n  ) => {\n    const { formattedTime } = useTimer({\n      loading,\n      onTick,\n      resetOnLoadingChange,\n      format,\n    })\n\n    return (\n      <TimerRoot\n        ref={ref}\n        variant={variant}\n        size={size}\n        loading={loading}\n        className={className}\n        {...props}\n      >\n        <TimerIcon size={size} loading={loading} />\n        <TimerDisplay size={size} time={formattedTime.display} />\n      </TimerRoot>\n    )\n  }\n)\nTimer.displayName = \"Timer\"\n\n/**\n * Hook for managing timer state and formatting\n */\nexport function useTimer({\n  loading = false,\n  onTick,\n  resetOnLoadingChange = true,\n  format = \"SS.MS\",\n}: UseTimerOptions = {}): UseTimerReturn {\n  const [elapsedTime, setElapsedTime] = useState(0)\n  const [milliseconds, setMilliseconds] = useState(0)\n  const [isRunning, setIsRunning] = useState(false)\n  const startTimeRef = useRef<number>(0)\n  const rafRef = useRef<number | null>(null)\n\n  const reset = useCallback(() => {\n    setElapsedTime(0)\n    setMilliseconds(0)\n    startTimeRef.current = 0\n  }, [])\n\n  const start = useCallback(() => {\n    setIsRunning(true)\n    startTimeRef.current = performance.now()\n  }, [])\n\n  const stop = useCallback(() => {\n    setIsRunning(false)\n    if (rafRef.current) {\n      cancelAnimationFrame(rafRef.current)\n    }\n  }, [])\n\n  useEffect(() => {\n    if (!isRunning) return\n\n    const updateTimer = () => {\n      const now = performance.now()\n      const elapsed = now - startTimeRef.current\n\n      const newElapsedTime = Math.floor(elapsed / 1000)\n      const newMilliseconds = Math.floor(elapsed % 1000)\n\n      setElapsedTime(newElapsedTime)\n      setMilliseconds(newMilliseconds)\n\n      rafRef.current = requestAnimationFrame(updateTimer)\n    }\n\n    rafRef.current = requestAnimationFrame(updateTimer)\n\n    return () => {\n      if (rafRef.current) {\n        cancelAnimationFrame(rafRef.current)\n      }\n    }\n  }, [isRunning])\n\n  useEffect(() => {\n    if (loading) {\n      if (resetOnLoadingChange) {\n        reset()\n      }\n      start()\n    } else {\n      stop()\n    }\n  }, [loading, resetOnLoadingChange, reset, start, stop])\n\n  useEffect(() => {\n    if (onTick) {\n      onTick(elapsedTime, milliseconds)\n    }\n  }, [elapsedTime, milliseconds, onTick])\n\n  const formatTime = useCallback(\n    (totalSeconds: number, ms: number) => {\n      const hours = Math.floor(totalSeconds / 3600)\n      const minutes = Math.floor((totalSeconds % 3600) / 60)\n      const seconds = totalSeconds % 60\n\n      switch (format) {\n        case \"HH:MM:SS\":\n          return {\n            seconds: seconds.toString().padStart(2, \"0\"),\n            milliseconds: Math.floor(ms / 10)\n              .toString()\n              .padStart(2, \"0\"),\n            display: `${hours.toString().padStart(2, \"0\")}:${minutes\n              .toString()\n              .padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`,\n          }\n        case \"MM:SS\":\n          const totalMinutes = Math.floor(totalSeconds / 60)\n          const remainingSeconds = totalSeconds % 60\n          return {\n            seconds: remainingSeconds.toString().padStart(2, \"0\"),\n            milliseconds: Math.floor(ms / 10)\n              .toString()\n              .padStart(2, \"0\"),\n            display: `${totalMinutes\n              .toString()\n              .padStart(2, \"0\")}:${remainingSeconds\n              .toString()\n              .padStart(2, \"0\")}`,\n          }\n        case \"SS.MS\":\n        default:\n          return {\n            seconds: totalSeconds.toString().padStart(2, \"0\"),\n            milliseconds: Math.floor(ms / 10)\n              .toString()\n              .padStart(2, \"0\"),\n            display: `${totalSeconds.toString().padStart(2, \"0\")}.${Math.floor(\n              ms / 10\n            )\n              .toString()\n              .padStart(2, \"0\")}`,\n          }\n      }\n    },\n    [format]\n  )\n\n  const formattedTime = formatTime(elapsedTime, milliseconds)\n\n  return {\n    elapsedTime,\n    milliseconds,\n    formattedTime,\n    isRunning,\n    reset,\n    start,\n    stop,\n  }\n}\n",
      "type": "registry:ui"
    }
  ]
}