{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-range-picker",
  "type": "registry:ui",
  "title": "DateRangePicker",
  "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/field.json",
    "https://kashi.sepantapouya.com/r/kashi-provider.json"
  ],
  "files": [
    {
      "path": "registry/kashi/DateRangePicker.tsx",
      "type": "registry:ui",
      "target": "components/kashi/DateRangePicker.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport { faMessages, formatJalali, toFaDigits, toGregorian, toISODate, toJalali, type KashiMessages } from \"@kashi-ui/persian\";\nimport { Calendar, type CalendarLabels, type CalendarProps } from \"./Calendar\";\nimport { Field } from \"./Field\";\nimport { cx } from \"./cx\";\nimport { useKashi } from \"./KashiProvider\";\n\nexport interface DateRange {\n  start: Date | null;\n  end: Date | null;\n}\n\nexport interface DateRangePreset {\n  label: string;\n  /** بازه را بر اساس امروز می‌سازد */\n  range: (today: Date) => { start: Date; end: Date };\n}\n\nconst day = (d: Date, add = 0) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + add);\n\n/** میان‌برهای پیش‌فرض با ماه‌های شمسی، با متن‌های یک زبان */\nconst presetsFor = (m: KashiMessages[\"dateRange\"], n: (v: number) => string): DateRangePreset[] => [\n  { label: m.last7(n(7)), range: (t) => ({ start: day(t, -6), end: t }) },\n  { label: m.last30(n(30)), range: (t) => ({ start: day(t, -29), end: t }) },\n  {\n    label: m.thisMonth,\n    range: (t) => {\n      const { jy, jm } = toJalali(t);\n      return { start: toGregorian(jy, jm, 1), end: t };\n    },\n  },\n  {\n    label: m.lastMonth,\n    range: (t) => {\n      const { jy, jm } = toJalali(t);\n      const [py, pm] = jm === 1 ? [jy - 1, 12] : [jy, jm - 1];\n      return { start: toGregorian(py, pm, 1), end: day(toGregorian(jy, jm, 1), -1) };\n    },\n  },\n];\n\n/** میان‌برهای پیش‌فرض به فارسی؛ بدون presets، کامپوننت همین‌ها را به زبان KashiProvider نشان می‌دهد */\nexport const defaultRangePresets: DateRangePreset[] = presetsFor(faMessages.dateRange, (v) => toFaDigits(v));\n\nexport interface DateRangePickerProps {\n  label?: ReactNode;\n  hint?: ReactNode;\n  error?: ReactNode;\n  value?: DateRange;\n  defaultValue?: DateRange;\n  onChange?: (range: DateRange) => void;\n  min?: Date;\n  max?: Date;\n  isDateDisabled?: (date: Date) => boolean;\n  /** میان‌برهای کنار تقویم؛ [] برای حذف */\n  presets?: DateRangePreset[];\n  /** @default \"fa\" */\n  digits?: \"fa\" | \"latin\";\n  /** با name، دو فیلد پنهان name_start و name_end به شکل YYYY-MM-DD ساخته می‌شود */\n  name?: string;\n  disabled?: boolean;\n  id?: string;\n  className?: string;\n  calendarLabels?: CalendarLabels;\n  /** تعطیلات در تقویم؛ مثل Calendar */\n  holidays?: CalendarProps[\"holidays\"];\n  labels?: { placeholder?: string; separator?: string; apply?: string; cancel?: string; presets?: string; pickEnd?: string };\n}\n\n/**\n * انتخاب بازهٔ تاریخ شمسی: کلیک اول شروع، کلیک دوم پایان؛ پیش از تأیید، بازه با حرکت موس پیش‌نمایش می‌شود.\n * تغییرها فقط با «تأیید» ثبت می‌شوند.\n */\nexport function DateRangePicker({\n  label, hint, error, value, defaultValue = { start: null, end: null }, onChange, min, max, isDateDisabled, holidays,\n  presets, digits, name, disabled, id, className, calendarLabels, labels = {},\n}: DateRangePickerProps) {\n  const { messages: M, digits: D, locale, n: N } = useKashi();\n  digits ??= D;\n  presets ??= presetsFor(M.dateRange, N);\n  const autoId = useId();\n  const triggerId = id ?? autoId;\n  const popoverId = `${triggerId}-popover`;\n  const {\n    placeholder = M.dateRange.placeholder, separator = M.common.range, apply = M.common.confirm, cancel = M.common.cancel,\n    presets: presetsLabel = M.dateRange.presets, pickEnd = M.dateRange.pickEnd,\n  } = labels;\n  const [inner, setInner] = useState<DateRange>(defaultValue);\n  const current = value ?? inner;\n  const [draft, setDraft] = useState<DateRange>(current);\n  const [open, setOpen] = useState(false);\n  // با انتخاب میان‌بر، تقویم دوباره ساخته می‌شود تا به ماه آن بازه برود\n  const [nav, setNav] = useState(0);\n  const wrap = useRef<HTMLDivElement>(null);\n  const trigger = useRef<HTMLButtonElement>(null);\n\n  const fmt = (d: Date) => formatJalali(d, { digits, locale });\n  const text = current.start && current.end ? `${fmt(current.start)}${separator}${fmt(current.end)}` : null;\n\n  const close = (focus = true) => {\n    setOpen(false);\n    if (focus) trigger.current?.focus();\n  };\n\n  useEffect(() => {\n    if (!open) return;\n    setDraft(current);\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 pick = (d: Date) => {\n    // کلیک اول یا کلیک پس از بازهٔ کامل: شروع تازه؛ کلیک دوم پایان (اگر قبل از شروع بود، جابه‌جا می‌شوند)\n    if (!draft.start || draft.end) setDraft({ start: d, end: null });\n    else setDraft(d < draft.start ? { start: d, end: draft.start } : { start: draft.start, end: d });\n  };\n\n  const commit = (r: DateRange) => {\n    if (value === undefined) setInner(r);\n    onChange?.(r);\n    close();\n  };\n\n  const today = day(new Date());\n  const activePreset = presets.findIndex((p) => {\n    const r = p.range(today);\n    return draft.start?.getTime() === r.start.getTime() && draft.end?.getTime() === r.end.getTime();\n  });\n\n  return (\n    <Field id={triggerId} label={label} hint={hint} error={error} className={className}>\n      <div className=\"k-datepicker k-daterange\" ref={wrap}>\n        <div className=\"k-select\">\n          <button\n            ref={trigger}\n            id={triggerId}\n            type=\"button\"\n            className=\"k-select__control k-daterange__trigger\"\n            aria-haspopup=\"dialog\"\n            aria-expanded={open}\n            aria-controls={open ? popoverId : undefined}\n            aria-invalid={error ? true : undefined}\n            disabled={disabled}\n            data-placeholder={text ? 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            <span>{text ?? placeholder}</span>\n          </button>\n        </div>\n        {name && (\n          <>\n            <input type=\"hidden\" name={`${name}_start`} value={current.start ? toISODate(current.start) : \"\"} />\n            <input type=\"hidden\" name={`${name}_end`} value={current.end ? toISODate(current.end) : \"\"} />\n          </>\n        )}\n        {open && (\n          <div\n            id={popoverId}\n            role=\"dialog\"\n            aria-label={typeof label === \"string\" ? label : placeholder}\n            className=\"k-datepicker__popover k-daterange__popover\"\n            onKeyDown={(e) => {\n              if (e.key === \"Escape\") {\n                e.stopPropagation();\n                close();\n              }\n            }}\n            onBlur={(e) => {\n              const next = e.relatedTarget as Node | null;\n              if (next && !wrap.current?.contains(next)) setOpen(false);\n            }}\n          >\n            {presets.length > 0 && (\n              <ul className=\"k-daterange__presets\" role=\"list\" aria-label={presetsLabel}>\n                {presets.map((p, i) => (\n                  <li key={p.label}>\n                    <button type=\"button\" className=\"k-daterange__preset\" aria-pressed={i === activePreset} onClick={() => {\n                      setDraft(p.range(today));\n                      setNav((n) => n + 1);\n                    }}>\n                      {p.label}\n                    </button>\n                  </li>\n                ))}\n              </ul>\n            )}\n            <div className=\"k-daterange__main\">\n              <Calendar\n                key={nav}\n                range={draft}\n                value={draft.end ?? draft.start}\n                onChange={pick}\n                min={min}\n                max={max}\n                isDateDisabled={isDateDisabled}\n            holidays={holidays}\n                digits={digits}\n                labels={calendarLabels}\n                autoFocus\n              />\n              <div className=\"k-daterange__footer\">\n                <span className=\"k-daterange__summary\" aria-live=\"polite\">\n                  {draft.start && draft.end ? `${fmt(draft.start)}${separator}${fmt(draft.end)}` : draft.start ? pickEnd : \"\"}\n                </span>\n                <button type=\"button\" className=\"k-btn k-btn--secondary k-btn--sm\" onClick={() => close()}>{cancel}</button>\n                <button type=\"button\" className={cx(\"k-btn k-btn--primary k-btn--sm\")} disabled={!draft.start || !draft.end} onClick={() => commit(draft)}>{apply}</button>\n              </div>\n            </div>\n          </div>\n        )}\n      </div>\n    </Field>\n  );\n}\n"
    }
  ]
}