{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-picker",
  "type": "registry:ui",
  "title": "DatePicker",
  "description": "دیت‌پیکر شمسی",
  "dependencies": [
    "@kashi-ui/persian@^1.8.0"
  ],
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/calendar.json",
    "https://kashi.sepantapouya.com/r/cx.json",
    "https://kashi.sepantapouya.com/r/input.json",
    "https://kashi.sepantapouya.com/r/kashi-provider.json",
    "https://kashi.sepantapouya.com/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/kashi/DatePicker.tsx",
      "type": "registry:ui",
      "target": "components/kashi/DatePicker.tsx",
      "content": "\"use client\";\n\nimport { forwardRef, useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport { formatJalali, parseJalali, toISODate } from \"@kashi-ui/persian\";\nimport { Calendar, sameDay, type CalendarLabels, type CalendarProps } from \"./Calendar\";\nimport { Input, type InputProps } from \"./Input\";\nimport { mergeRefs, useIsomorphicLayoutEffect } from \"./utils\";\nimport { useKashi } from \"./KashiProvider\";\n\ntype Base = Omit<InputProps, \"value\" | \"defaultValue\" | \"onChange\" | \"type\" | \"addonEnd\" | \"min\" | \"max\">;\n\nexport interface DatePickerProps extends Base {\n  value?: Date | null;\n  defaultValue?: Date | null;\n  onChange?: (date: Date | null) => void;\n  min?: Date;\n  max?: Date;\n  isDateDisabled?: (date: Date) => boolean;\n  /** @default \"fa\" */\n  digits?: \"fa\" | \"latin\";\n  yearRange?: [number, number];\n  /** @default \"این تاریخ معتبر نیست؛ آن را به شکل ۱۴۰۳/۰۵/۱۲ وارد کنید.\" */\n  invalidMessage?: ReactNode;\n  /** نام دکمهٔ باز کردن تقویم @default \"انتخاب تاریخ از تقویم\" */\n  openLabel?: string;\n  /** @default \"امروز\" */\n  todayLabel?: string;\n  calendarLabels?: CalendarLabels;\n  /** تعطیلات در تقویم؛ مثل Calendar */\n  holidays?: CalendarProps[\"holidays\"];\n}\n\n/**\n * انتخاب تاریخ شمسی: تایپ مستقیم (۱۴۰۳/۰۵/۱۲) یا انتخاب از تقویم.\n * با name، مقدار میلادی به شکل YYYY-MM-DD در فرم ارسال می‌شود.\n */\nexport const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(function DatePicker(\n  {\n    value, defaultValue = null, onChange, min, max, isDateDisabled, holidays, digits, yearRange,\n    invalidMessage, openLabel,\n    todayLabel, calendarLabels, error, name, onBlur, placeholder, ...rest\n  },\n  ref,\n) {\n  const { messages: M, digits: D, locale } = useKashi();\n  digits ??= D;\n  invalidMessage ??= M.datePicker.invalid;\n  openLabel ??= M.datePicker.open;\n  todayLabel ??= M.common.today;\n  const id = useId();\n  const popoverId = `${id}-calendar`;\n  const [inner, setInner] = useState<Date | null>(defaultValue);\n  const selected = value !== undefined ? value : inner;\n  const fmt = (d: Date | null) => (d ? formatJalali(d, { digits }) : \"\");\n  const [text, setText] = useState(() => fmt(selected));\n  const [invalid, setInvalid] = useState(false);\n  const [open, setOpen] = useState(false);\n  const [top, setTop] = useState(0);\n  const wrap = useRef<HTMLDivElement>(null);\n  const inputRef = useRef<HTMLInputElement>(null);\n  const button = useRef<HTMLButtonElement>(null);\n\n  // همگام‌سازی متن با مقدار کنترل‌شده\n  useEffect(() => {\n    setText(fmt(selected));\n    setInvalid(false);\n  }, [selected?.getTime(), digits]);\n\n  // تقویم درست زیر کادر فیلد باز می‌شود (نه زیر متن راهنما)\n  useIsomorphicLayoutEffect(() => {\n    if (!open || !wrap.current) return;\n    const box = wrap.current.querySelector(\".k-input\") as HTMLElement | null;\n    if (box) setTop(box.offsetTop + box.offsetHeight + 6);\n  }, [open]);\n\n  // بستن با کلیک بیرون\n  useEffect(() => {\n    if (!open) return;\n    const onDown = (e: PointerEvent) => {\n      if (!wrap.current?.contains(e.target as Node)) setOpen(false);\n    };\n    document.addEventListener(\"pointerdown\", onDown);\n    return () => document.removeEventListener(\"pointerdown\", onDown);\n  }, [open]);\n\n  const commit = (d: Date | null) => {\n    if (value === undefined) setInner(d);\n    setText(fmt(d));\n    setInvalid(false);\n    if (!sameDay(d, selected) || (d === null) !== (selected === null)) onChange?.(d);\n  };\n\n  const close = () => {\n    setOpen(false);\n    button.current?.focus();\n  };\n\n  return (\n    <div className=\"k-datepicker\" ref={wrap}>\n      <Input\n        ref={mergeRefs(ref, inputRef)}\n        type=\"text\"\n        dir=\"ltr\"\n        autoComplete=\"off\"\n        placeholder={placeholder ?? (digits === \"fa\" ? \"۱۴۰۳/۰۱/۰۱\" : \"1403/01/01\")}\n        value={text}\n        onChange={(e) => setText(e.target.value)}\n        onBlur={(e) => {\n          const t = e.target.value.trim();\n          if (!t) commit(null);\n          else {\n            const d = parseJalali(t);\n            if (d && !(min && d < min) && !(max && d > max) && !isDateDisabled?.(d)) commit(d);\n            else setInvalid(true);\n          }\n          onBlur?.(e);\n        }}\n        error={error ?? (invalid ? invalidMessage : undefined)}\n        addonEnd={\n          <button\n            ref={button}\n            type=\"button\"\n            className=\"k-btn k-btn--tertiary k-btn--icon k-btn--sm k-datepicker__toggle\"\n            aria-label={selected ? `${openLabel}${M.common.list}${formatJalali(selected, { style: \"full\", digits, locale })}` : openLabel}\n            aria-haspopup=\"dialog\"\n            aria-expanded={open}\n            aria-controls={open ? popoverId : undefined}\n            onClick={() => setOpen((o) => !o)}\n          >\n            <svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" strokeWidth={1.7} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n              <rect x=\"3\" y=\"4.5\" width=\"14\" height=\"12.5\" rx=\"2\" /><path d=\"M3 8.5h14M7 3v3M13 3v3\" />\n            </svg>\n          </button>\n        }\n        {...rest}\n      />\n      {name && <input type=\"hidden\" name={name} value={selected ? toISODate(selected) : \"\"} />}\n\n      {open && (\n        <div\n          id={popoverId}\n          className=\"k-datepicker__popover\"\n          role=\"dialog\"\n          aria-label={openLabel}\n          style={{ insetBlockStart: top }}\n          onKeyDown={(e) => {\n            if (e.key === \"Escape\") {\n              e.stopPropagation();\n              close();\n            }\n          }}\n          onBlur={(e) => {\n            // اگر فوکوس (با Tab) از تقویم و کل فیلد بیرون رفت، ببند؛ کلیک بیرون را pointerdown می‌گیرد\n            const next = e.relatedTarget as Node | null;\n            if (next && !wrap.current?.contains(next)) setOpen(false);\n          }}\n        >\n          <Calendar\n            value={selected}\n            onChange={(d) => {\n              commit(d);\n              close();\n            }}\n            min={min}\n            max={max}\n            isDateDisabled={isDateDisabled}\n            holidays={holidays}\n            digits={digits}\n            yearRange={yearRange}\n            labels={calendarLabels}\n            autoFocus\n            footer={\n              <button\n                type=\"button\"\n                className=\"k-btn k-btn--link\"\n                onClick={() => {\n                  const t = new Date();\n                  commit(new Date(t.getFullYear(), t.getMonth(), t.getDate()));\n                  close();\n                }}\n              >\n                {todayLabel}\n              </button>\n            }\n          />\n        </div>\n      )}\n    </div>\n  );\n});\n"
    }
  ]
}