{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "price-range",
  "type": "registry:ui",
  "title": "PriceRange",
  "description": "فیلتر بازهٔ قیمت: دو فیلد «از» و «تا» با جداکنندهٔ هزارگان و یک اسلایدر دودستگیره که با هم هماهنگ‌اند",
  "dependencies": [
    "@kashi-ui/persian@^2.9.0"
  ],
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/cx.json",
    "https://kashi.sepantapouya.com/r/kashi-provider.json",
    "https://kashi.sepantapouya.com/r/slider.json"
  ],
  "files": [
    {
      "path": "registry/kashi/PriceRange.tsx",
      "type": "registry:ui",
      "target": "components/kashi/PriceRange.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useId, useState, type ReactNode } from \"react\";\nimport { formatNumber, toEnDigits } from \"@kashi-ui/persian\";\nimport { cx } from \"./cx\";\nimport { useKashi } from \"./KashiProvider\";\nimport { Slider } from \"./Slider\";\n\nexport interface PriceRangeProps {\n  /** کمترین و بیشترین قیمت ممکن */\n  min: number;\n  max: number;\n  /** [از، تا] */\n  value?: [number, number];\n  defaultValue?: [number, number];\n  onChange?: (value: [number, number]) => void;\n  /** گام اسلایدر و گرد کردن عددهای تایپ‌شده @default 1000 */\n  step?: number;\n  /** عنوان بالای فیلتر، مثل «قیمت» */\n  label?: ReactNode;\n  /** واحد کنار فیلدها @default \"تومان\" (در انگلیسی «Toman») */\n  unit?: ReactNode;\n  /** با name، دو فیلد name_min و name_max در فرم ارسال می‌شوند */\n  name?: string;\n  disabled?: boolean;\n  className?: string;\n  labels?: { from?: string; to?: string; min?: string; max?: string };\n}\n\n/** فیلتر بازهٔ قیمت: دو فیلد «از» و «تا» با جداکنندهٔ هزارگان و یک اسلایدر دودستگیره که با هم هماهنگ‌اند */\nexport function PriceRange({\n  min, max, value, defaultValue, onChange, step = 1000, label, unit, name, disabled, className, labels = {},\n}: PriceRangeProps) {\n  const { messages: M, digits: D, locale } = useKashi();\n  const id = useId();\n  const [inner, setInner] = useState<[number, number]>(defaultValue ?? [min, max]);\n  const [lo, hi] = value ?? inner;\n  const fmt = (n: number) => formatNumber(n, { digits: D });\n  const u = unit ?? (locale === \"en\" ? \"Toman\" : \"تومان\");\n  const { from = M.priceRange.from, to = M.priceRange.to, min: minLabel = M.priceRange.min, max: maxLabel = M.priceRange.max } = labels;\n\n  const set = (next: [number, number]) => {\n    if (value === undefined) setInner(next);\n    onChange?.(next);\n  };\n\n  // متن فیلدها جدا از عدد نگه داشته می‌شود تا هنگام تایپ پرش نکند؛ با خروج از فیلد یا Enter اعمال می‌شود\n  const [text, setText] = useState<[string, string]>([fmt(lo), fmt(hi)]);\n  useEffect(() => setText([fmt(lo), fmt(hi)]), [lo, hi, D]);\n\n  const parse = (s: string) => Number(toEnDigits(s).replace(/\\D/g, \"\") || \"0\");\n  const commit = (which: 0 | 1) => {\n    let n = Math.round(parse(text[which]) / step) * step;\n    n = Math.min(max, Math.max(min, n));\n    const next: [number, number] = which === 0 ? [Math.min(n, hi), hi] : [lo, Math.max(n, lo)];\n    set(next);\n    setText([fmt(next[0]), fmt(next[1])]);\n  };\n  const live = (which: 0 | 1, raw: string) => {\n    const digits = toEnDigits(raw).replace(/\\D/g, \"\");\n    const shown = digits ? fmt(Number(digits)) : \"\";\n    setText((t) => (which === 0 ? [shown, t[1]] : [t[0], shown]));\n  };\n\n  const field = (which: 0 | 1) => (\n    <label className=\"k-price-range__field\">\n      <span className=\"k-price-range__edge\">{which === 0 ? from : to}</span>\n      <input\n        className=\"k-price-range__input\"\n        inputMode=\"numeric\"\n        dir=\"ltr\"\n        autoComplete=\"off\"\n        value={text[which]}\n        disabled={disabled}\n        aria-label={`${which === 0 ? from : to}${typeof label === \"string\" ? ` ${label}` : \"\"}`}\n        onChange={(e) => live(which, e.target.value)}\n        onBlur={() => commit(which)}\n        onKeyDown={(e) => { if (e.key === \"Enter\") { e.preventDefault(); commit(which); } }}\n      />\n      <span className=\"k-price-range__unit\">{u}</span>\n    </label>\n  );\n\n  return (\n    <div className={cx(\"k-price-range\", className)} role=\"group\" aria-labelledby={label ? `${id}-l` : undefined} aria-label={label ? undefined : M.priceRange.range}>\n      {label && <span className=\"k-label k-price-range__label\" id={`${id}-l`}>{label}</span>}\n      <div className=\"k-price-range__fields\">{field(0)}{field(1)}</div>\n      <Slider\n        value={[lo, hi]}\n        onChange={(v) => set(v as [number, number])}\n        min={min}\n        max={max}\n        step={step}\n        showValue={false}\n        disabled={disabled}\n        name={name}\n        format={(n) => `${fmt(n)} ${typeof u === \"string\" ? u : \"\"}`.trim()}\n        labels={{ from, to }}\n        className=\"k-price-range__slider\"\n      />\n      <div className=\"k-price-range__ends\" aria-hidden=\"true\"><span>{minLabel}</span><span>{maxLabel}</span></div>\n    </div>\n  );\n}\n"
    }
  ]
}