{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "type": "registry:ui",
  "title": "Date Picker (AD)",
  "description": "A single date picker (Gregorian/AD) with month navigation and a minimum date option.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "components/ui/date-picker.tsx",
      "type": "registry:ui",
      "target": "~/components/ui/date-picker.tsx",
      "content": "\"use client\";\n\nimport { useState, useRef, useEffect, useMemo, useCallback } from \"react\";\nimport { Calendar, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface DatePickerProps {\n  value: string; // 'YYYY-MM-DD'\n  onChange: (value: string) => void;\n  placeholder?: string;\n  className?: string;\n  min?: string; // 'YYYY-MM-DD'\n  id?: string;\n}\n\nfunction toLocalDate(value: string): Date | null {\n  if (!value) return null;\n  const [y, m, d] = value.split(\"-\").map(Number);\n  const date = new Date(y, m - 1, d);\n  return isNaN(date.getTime()) ? null : date;\n}\n\nfunction toValue(date: Date): string {\n  const y = date.getFullYear();\n  const m = String(date.getMonth() + 1).padStart(2, \"0\");\n  const d = String(date.getDate()).padStart(2, \"0\");\n  return `${y}-${m}-${d}`;\n}\n\nfunction stripTime(date: Date): Date {\n  return new Date(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\nfunction isSameDay(a: Date, b: Date): boolean {\n  return (\n    a.getFullYear() === b.getFullYear() &&\n    a.getMonth() === b.getMonth() &&\n    a.getDate() === b.getDate()\n  );\n}\n\nconst WEEKDAYS = [\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"];\n\nexport function DatePicker({\n  value,\n  onChange,\n  placeholder = \"Select date\",\n  className,\n  min,\n  id,\n}: DatePickerProps) {\n  const minDate = toLocalDate(min || \"\");\n  const [open, setOpen] = useState(false);\n  const selected = toLocalDate(value);\n  const [monthNav, setMonthNav] = useState(0);\n  const wrapperRef = useRef<HTMLDivElement>(null);\n  const buttonRef = useRef<HTMLButtonElement>(null);\n  const [dropdownPos, setDropdownPos] = useState<{\n    top: number;\n    left: number;\n  } | null>(null);\n\n  const updatePosition = useCallback(() => {\n    if (buttonRef.current) {\n      const rect = buttonRef.current.getBoundingClientRect();\n      setDropdownPos({ top: rect.bottom + 6, left: rect.left });\n    }\n  }, []);\n\n  const viewDate = useMemo(() => {\n    const base = toLocalDate(value) || new Date();\n    return new Date(base.getFullYear(), base.getMonth() + monthNav, 1);\n  }, [value, monthNav]);\n\n  useEffect(() => {\n    function handleClick(e: MouseEvent) {\n      if (\n        wrapperRef.current &&\n        !wrapperRef.current.contains(e.target as Node)\n      ) {\n        setOpen(false);\n      }\n    }\n    document.addEventListener(\"mousedown\", handleClick);\n    return () => document.removeEventListener(\"mousedown\", handleClick);\n  }, []);\n\n  useEffect(() => {\n    if (!open) return;\n    const t = window.setTimeout(updatePosition, 0);\n    return () => window.clearTimeout(t);\n  }, [open, updatePosition]);\n\n  const year = viewDate.getFullYear();\n  const month = viewDate.getMonth();\n  const startOffset = new Date(year, month, 1).getDay();\n  const daysInMonth = new Date(year, month + 1, 0).getDate();\n\n  const today = new Date();\n\n  const cells: (number | null)[] = [\n    ...Array(startOffset).fill(null),\n    ...Array.from({ length: daysInMonth }, (_, i) => i + 1),\n  ];\n\n  const monthLabel = viewDate.toLocaleDateString(\"en-US\", {\n    month: \"long\",\n    year: \"numeric\",\n  });\n\n  const displayLabel = selected\n    ? selected.toLocaleDateString(\"en-US\", {\n        month: \"short\",\n        day: \"numeric\",\n        year: \"numeric\",\n      })\n    : placeholder;\n\n  return (\n    <div className={cn(\"relative\", className)} ref={wrapperRef}>\n      <button\n        type=\"button\"\n        id={id}\n        ref={buttonRef}\n        onClick={(e) => {\n          e.preventDefault();\n          e.stopPropagation();\n          setOpen((o) => {\n            if (!o) updatePosition();\n            return !o;\n          });\n        }}\n        className={cn(\n          \"flex h-10 w-full items-center gap-2 rounded-md border border-zinc-300 bg-white px-3 text-sm text-zinc-900\",\n          \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-500 focus-visible:ring-offset-2\",\n          \"transition-colors hover:border-zinc-400\",\n        )}\n      >\n        <Calendar className=\"h-4 w-4 shrink-0 text-zinc-500\" />\n        <span className={cn(\"truncate\", !selected && \"text-zinc-500\")}>\n          {displayLabel}\n        </span>\n      </button>\n\n      {open && dropdownPos && (\n        <div\n          className=\"fixed z-50 w-72 rounded-md border border-zinc-200 bg-white p-3 shadow-lg\"\n          style={{ top: dropdownPos.top, left: dropdownPos.left }}\n        >\n          <div className=\"mb-2 flex items-center justify-between\">\n            <button\n              type=\"button\"\n              onClick={() => setMonthNav((m) => m - 1)}\n              className=\"rounded-sm p-1 text-zinc-600 transition-colors hover:bg-zinc-100\"\n              aria-label=\"Previous month\"\n            >\n              <ChevronLeft className=\"h-4 w-4\" />\n            </button>\n            <span className=\"text-sm font-medium text-zinc-900\">\n              {monthLabel}\n            </span>\n            <button\n              type=\"button\"\n              onClick={() => setMonthNav((m) => m + 1)}\n              className=\"rounded-sm p-1 text-zinc-600 transition-colors hover:bg-zinc-100\"\n              aria-label=\"Next month\"\n            >\n              <ChevronRight className=\"h-4 w-4\" />\n            </button>\n          </div>\n\n          <div className=\"mb-1 grid grid-cols-7 gap-1\">\n            {WEEKDAYS.map((d) => (\n              <div\n                key={d}\n                className=\"py-1 text-center text-[11px] font-medium text-zinc-500\"\n              >\n                {d}\n              </div>\n            ))}\n          </div>\n\n          <div className=\"grid grid-cols-7 gap-1\">\n            {cells.map((day, i) => {\n              if (day === null) return <div key={`empty-${i}`} />;\n              const cellDate = new Date(year, month, day);\n              const isSelected = selected && isSameDay(cellDate, selected);\n              const isToday = isSameDay(cellDate, today);\n              const isDisabled = minDate\n                ? stripTime(cellDate) < stripTime(minDate)\n                : false;\n              return (\n                <button\n                  key={day}\n                  type=\"button\"\n                  disabled={isDisabled}\n                  onClick={() => {\n                    onChange(toValue(cellDate));\n                    setMonthNav(0);\n                    setOpen(false);\n                  }}\n                  className={cn(\n                    \"flex h-8 w-8 items-center justify-center rounded-sm text-sm text-zinc-900 transition-colors\",\n                    isDisabled && \"cursor-not-allowed text-zinc-300\",\n                    !isDisabled &&\n                      (isSelected\n                        ? \"bg-zinc-900 font-semibold text-white\"\n                        : isToday\n                          ? \"bg-zinc-100 font-medium text-zinc-900\"\n                          : \"hover:bg-zinc-100\"),\n                  )}\n                >\n                  {day}\n                </button>\n              );\n            })}\n          </div>\n\n          {selected && (\n            <button\n              type=\"button\"\n              onClick={() => {\n                onChange(\"\");\n                setOpen(false);\n              }}\n              className=\"mt-2 w-full border-t border-zinc-200 py-1.5 text-center text-xs text-zinc-500 transition-colors hover:text-zinc-900\"\n            >\n              Clear date\n            </button>\n          )}\n        </div>\n      )}\n    </div>\n  );\n}\n"
    }
  ],
  "docs": "Usage:\n\n```tsx\nimport { DatePicker } from \"@/components/ui/date-picker\";\n\nconst [date, setDate] = useState(\"\"); // 'YYYY-MM-DD'\n\n<DatePicker value={date} onChange={setDate} min=\"2024-01-01\" />\n```\n\nProps: value (string, 'YYYY-MM-DD'), onChange, placeholder, min ('YYYY-MM-DD' disables earlier dates), id, className."
}
