{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker-bs",
  "type": "registry:ui",
  "title": "Date Picker (BS / Nepali)",
  "description": "A Nepali Bikram Sambat date picker (AD value in, BS calendar UI) with year/month selectors.",
  "dependencies": [
    "lucide-react",
    "@remotemerge/nepali-date-converter"
  ],
  "files": [
    {
      "path": "components/ui/date-picker-bs.tsx",
      "type": "registry:ui",
      "target": "~/components/ui/date-picker-bs.tsx",
      "content": "\"use client\";\n\nimport { useState, useRef, useEffect, useMemo, useCallback } from \"react\";\nimport { Calendar } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  Select,\n  SelectTrigger,\n  SelectValue,\n  SelectContent,\n  SelectItem,\n} from \"@/components/ui/select\";\nimport {\n  adToBs,\n  bsToAd,\n  bsDaysInMonth,\n  bsWeekdayOffset,\n  bsMonthName,\n  formatBsDate,\n} from \"@/lib/nepali\";\n\ninterface DatePickerProps {\n  value: string; // AD 'YYYY-MM-DD'\n  onChange: (value: string) => void;\n  placeholder?: string;\n  className?: string;\n  id?: string;\n  name?: string;\n  required?: boolean;\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  id,\n  name,\n  required,\n}: DatePickerProps) {\n  const [open, setOpen] = useState(false);\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 selected = adToBs(value);\n\n  const [view, setView] = useState<{ year: number; month: number }>(() => {\n    const bs = adToBs(value) ?? adToBs(new Date().toISOString().slice(0, 10));\n    return { year: bs?.year ?? 2080, month: bs?.month ?? 1 };\n  });\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  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  const todayBs = useMemo(\n    () => adToBs(new Date().toISOString().slice(0, 10)),\n    [],\n  );\n\n  const daysInMonth = bsDaysInMonth(view.year, view.month);\n  const startOffset = bsWeekdayOffset(view.year, view.month);\n  const cells: (number | null)[] = [\n    ...Array(startOffset).fill(null),\n    ...Array.from({ length: daysInMonth }, (_, i) => i + 1),\n  ];\n\n  const yearRange = useMemo(() => {\n    const base = todayBs?.year ?? 2080;\n    const start = Math.max(1975, base - 15);\n    const end = base + 5;\n    return Array.from({ length: end - start + 1 }, (_, i) => start + i);\n  }, [todayBs]);\n\n  const displayLabel = selected ? formatBsDate(value) : placeholder;\n\n  return (\n    <div className={cn(\"relative\", className)} ref={wrapperRef}>\n      <input type=\"hidden\" name={name} value={value} required={required} />\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 gap-2\">\n            <Select\n              value={String(view.year)}\n              onValueChange={(v) =>\n                setView((prev) => ({ ...prev, year: Number(v) }))\n              }\n              className=\"flex-1\"\n            >\n              <SelectTrigger className=\"h-9 px-2 py-1 text-sm\">\n                <SelectValue placeholder=\"Year\" />\n              </SelectTrigger>\n              <SelectContent>\n                {yearRange.map((y) => (\n                  <SelectItem key={y} value={String(y)}>\n                    {y} BS\n                  </SelectItem>\n                ))}\n              </SelectContent>\n            </Select>\n            <Select\n              value={String(view.month)}\n              onValueChange={(v) =>\n                setView((prev) => ({ ...prev, month: Number(v) }))\n              }\n              className=\"flex-1\"\n            >\n              <SelectTrigger className=\"h-9 px-2 py-1 text-sm\">\n                <SelectValue placeholder=\"Month\" />\n              </SelectTrigger>\n              <SelectContent>\n                {Array.from({ length: 12 }, (_, i) => i + 1).map((m) => (\n                  <SelectItem key={m} value={String(m)}>\n                    {bsMonthName(m)}\n                  </SelectItem>\n                ))}\n              </SelectContent>\n            </Select>\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 adIso = bsToAd({\n                year: view.year,\n                month: view.month,\n                date: day,\n              });\n              const isSelected =\n                selected &&\n                selected.year === view.year &&\n                selected.month === view.month &&\n                selected.date === day;\n              const isToday =\n                todayBs &&\n                todayBs.year === view.year &&\n                todayBs.month === view.month &&\n                todayBs.date === day;\n              return (\n                <button\n                  key={day}\n                  type=\"button\"\n                  onClick={() => {\n                    if (adIso) onChange(adIso);\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                    isSelected\n                      ? \"bg-emerald-600 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 hover:text-zinc-900\"\n            >\n              Clear date\n            </button>\n          )}\n        </div>\n      )}\n    </div>\n  );\n}\n"
    },
    {
      "path": "components/ui/select.tsx",
      "type": "registry:ui",
      "target": "~/components/ui/select.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { ChevronDown } from \"lucide-react\";\n\ninterface SelectContextValue {\n  open: boolean;\n  setOpen: (v: boolean) => void;\n  value: string;\n  onValueChange: (v: string) => void;\n  label: string;\n  setLabel: (v: string) => void;\n  triggerRef: React.RefObject<HTMLButtonElement | null>;\n  dropdownPos: { top: number; left: number; width: number } | null;\n  updatePosition: () => void;\n}\n\nconst SelectCtx = React.createContext<SelectContextValue | null>(null);\n\nfunction useSelect() {\n  const ctx = React.useContext(SelectCtx);\n  if (!ctx)\n    throw new Error(\"Select compound components must be used within <Select>\");\n  return ctx;\n}\n\ninterface SelectProps {\n  value: string;\n  onValueChange: (value: string) => void;\n  children: React.ReactNode;\n  className?: string;\n}\n\nfunction Select({ value, onValueChange, children, className }: SelectProps) {\n  const [open, setOpen] = React.useState(false);\n  const [label, setLabel] = React.useState(\"\");\n  const ref = React.useRef<HTMLDivElement>(null);\n  const triggerRef = React.useRef<HTMLButtonElement>(null);\n  const [dropdownPos, setDropdownPos] = React.useState<{\n    top: number;\n    left: number;\n    width: number;\n  } | null>(null);\n\n  const updatePosition = React.useCallback(() => {\n    if (triggerRef.current) {\n      const rect = triggerRef.current.getBoundingClientRect();\n      setDropdownPos({\n        top: rect.bottom + 6,\n        left: rect.left,\n        width: rect.width,\n      });\n    }\n  }, []);\n\n  React.useEffect(() => {\n    function handleClick(e: MouseEvent) {\n      if (ref.current && !ref.current.contains(e.target as Node)) {\n        setOpen(false);\n      }\n    }\n    document.addEventListener(\"mousedown\", handleClick);\n    return () => document.removeEventListener(\"mousedown\", handleClick);\n  }, []);\n\n  return (\n    <SelectCtx.Provider\n      value={{\n        open,\n        setOpen,\n        value,\n        onValueChange,\n        label,\n        setLabel,\n        triggerRef,\n        dropdownPos,\n        updatePosition,\n      }}\n    >\n      <div ref={ref} className={cn(\"relative\", className)}>\n        {children}\n      </div>\n    </SelectCtx.Provider>\n  );\n}\n\ninterface SelectTriggerProps {\n  className?: string;\n  children?: React.ReactNode;\n}\n\nfunction SelectTrigger({ className, children }: SelectTriggerProps) {\n  const { open, setOpen, triggerRef, updatePosition } = useSelect();\n\n  return (\n    <button\n      type=\"button\"\n      ref={triggerRef}\n      onClick={() => {\n        if (!open) updatePosition();\n        setOpen(!open);\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 text-left\",\n        className,\n      )}\n    >\n      {children}\n      <ChevronDown className=\"ml-auto h-4 w-4 shrink-0 text-zinc-500\" />\n    </button>\n  );\n}\n\ninterface SelectValueProps {\n  placeholder?: string;\n  className?: string;\n}\n\nfunction SelectValue({ placeholder, className }: SelectValueProps) {\n  const { value, label } = useSelect();\n\n  if (value && label) {\n    return <span className={cn(\"truncate\", className)}>{label}</span>;\n  }\n\n  return (\n    <span className={cn(\"truncate text-zinc-500\", className)}>\n      {placeholder || \"Select...\"}\n    </span>\n  );\n}\n\ninterface SelectContentProps {\n  className?: string;\n  children: React.ReactNode;\n}\n\nfunction SelectContent({ className, children }: SelectContentProps) {\n  const { open, dropdownPos } = useSelect();\n\n  return (\n    <div\n      className={cn(\n        \"fixed z-50 max-h-60 overflow-auto rounded-md border border-zinc-200 bg-white py-1 shadow-lg\",\n        !open && \"hidden\",\n        className,\n      )}\n      style={\n        dropdownPos\n          ? {\n              top: dropdownPos.top,\n              left: dropdownPos.left,\n              width: dropdownPos.width,\n            }\n          : undefined\n      }\n    >\n      {children}\n    </div>\n  );\n}\n\ninterface SelectItemProps {\n  value: string;\n  children: React.ReactNode;\n  className?: string;\n}\n\nfunction SelectItem({ value, children, className }: SelectItemProps) {\n  const { onValueChange, setOpen, setLabel, value: currentValue } = useSelect();\n  const isSelected = currentValue === value;\n  const itemRef = React.useRef<HTMLDivElement>(null);\n\n  React.useEffect(() => {\n    if (isSelected && itemRef.current) {\n      setLabel(itemRef.current.textContent || \"\");\n    }\n  }, [isSelected, setLabel]);\n\n  return (\n    <div\n      ref={itemRef}\n      role=\"option\"\n      aria-selected={isSelected}\n      onClick={() => {\n        setLabel(itemRef.current?.textContent || \"\");\n        onValueChange(value);\n        setOpen(false);\n      }}\n      className={cn(\n        \"cursor-pointer px-3 py-2 text-sm transition-colors\",\n        isSelected\n          ? \"bg-zinc-100 font-medium text-zinc-900\"\n          : \"text-zinc-900 hover:bg-zinc-100\",\n        className,\n      )}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport { Select, SelectTrigger, SelectValue, SelectContent, SelectItem };\n"
    },
    {
      "path": "lib/nepali.ts",
      "type": "registry:ui",
      "target": "~/lib/nepali.ts",
      "content": "import DateConverter from \"@remotemerge/nepali-date-converter\";\n\nconst BS_MONTHS = [\n  \"Baishakh\",\n  \"Jestha\",\n  \"Ashadh\",\n  \"Shrawan\",\n  \"Bhadra\",\n  \"Ashwin\",\n  \"Kartik\",\n  \"Mangsir\",\n  \"Poush\",\n  \"Magh\",\n  \"Falgun\",\n  \"Chaitra\",\n] as const;\n\nexport type BsDate = { year: number; month: number; date: number };\n\nexport function adToBs(isoDate: string): BsDate | null {\n  try {\n    const bs = new DateConverter(isoDate).toBs();\n    return { year: bs.year, month: bs.month, date: bs.date };\n  } catch {\n    return null;\n  }\n}\n\nexport function bsToAd(bs: BsDate): string | null {\n  try {\n    const ad = new DateConverter(\n      `${bs.year}-${String(bs.month).padStart(2, \"0\")}-${String(\n        bs.date,\n      ).padStart(2, \"0\")}`,\n    ).toAd();\n    return `${ad.year}-${String(ad.month).padStart(2, \"0\")}-${String(\n      ad.date,\n    ).padStart(2, \"0\")}`;\n  } catch {\n    return null;\n  }\n}\n\nexport function bsDaysInMonth(year: number, month: number): number {\n  const start = bsToAd({ year, month, date: 1 });\n  const next =\n    month === 12\n      ? { year: year + 1, month: 1, date: 1 }\n      : { year, month: month + 1, date: 1 };\n  const nextAd = bsToAd(next);\n  if (!start || !nextAd) return 30;\n  const diff =\n    (new Date(`${nextAd}T00:00:00Z`).getTime() -\n      new Date(`${start}T00:00:00Z`).getTime()) /\n    86400000;\n  return Math.round(diff);\n}\n\nexport function bsWeekdayOffset(year: number, month: number): number {\n  const start = bsToAd({ year, month, date: 1 });\n  if (!start) return 0;\n  return new Date(`${start}T00:00:00Z`).getUTCDay();\n}\n\nexport function bsMonthName(month: number): string {\n  return BS_MONTHS[(month - 1 + 12) % 12] ?? \"\";\n}\n\nexport function formatBsDate(isoDate: string): string {\n  const bs = adToBs(isoDate);\n  if (!bs) return isoDate;\n  return `${bs.year} ${bsMonthName(bs.month)} ${bs.date}`;\n}\n"
    }
  ],
  "docs": "Usage:\n\n```tsx\nimport { DatePicker } from \"@/components/ui/date-picker-bs\";\n\nconst [date, setDate] = useState(\"\"); // AD 'YYYY-MM-DD'\n\n<DatePicker value={date} onChange={setDate} />\n```\n\nThe value stays an AD 'YYYY-MM-DD' string; the calendar renders in Bikram Sambat.\nAlso installs components/ui/select.tsx and lib/nepali.ts automatically."
}
