{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "time-input",
  "type": "registry:ui",
  "title": "TimeInput",
  "description": "ساعت ۲۴ساعته با ارقام فارسی: «۹۳۰» یا «۹:۳۰» تایپ کنید، ↑↓ گام به گام",
  "dependencies": [
    "@kashi-ui/persian@^1.8.0"
  ],
  "registryDependencies": [
    "https://kashi.sepantapouya.com/r/cx.json",
    "https://kashi.sepantapouya.com/r/input.json",
    "https://kashi.sepantapouya.com/r/kashi-provider.json"
  ],
  "files": [
    {
      "path": "registry/kashi/TimeInput.tsx",
      "type": "registry:ui",
      "target": "components/kashi/TimeInput.tsx",
      "content": "\"use client\";\n\nimport { forwardRef, useEffect, useState, type KeyboardEvent, type ReactNode } from \"react\";\nimport { formatTime, parseTime, toFaDigits, type Time } from \"@kashi-ui/persian\";\nimport { Input, type InputProps } from \"./Input\";\nimport { useKashi } from \"./KashiProvider\";\n\ntype Base = Omit<InputProps, \"value\" | \"defaultValue\" | \"onChange\" | \"type\" | \"min\" | \"max\" | \"step\">;\n\nexport interface TimeInputProps extends Base {\n  /** \"HH:MM\" با ارقام لاتین */\n  value?: string | null;\n  defaultValue?: string | null;\n  onChange?: (value: string | null) => void;\n  /** گام ↑↓ به دقیقه @default 15 */\n  step?: number;\n  /** \"08:00\" */\n  min?: string;\n  max?: string;\n  /** @default \"fa\" */\n  digits?: \"fa\" | \"latin\";\n  /** @default \"ساعت معتبر نیست؛ آن را به شکل ۰۹:۳۰ وارد کنید.\" */\n  invalidMessage?: ReactNode;\n  /** @default (min, max) => «ساعت باید بین min و max باشد.» */\n  rangeMessage?: (min: string, max: string) => ReactNode;\n}\n\nconst toMin = (t: Time) => t.hours * 60 + t.minutes;\nconst fromMin = (m: number): Time => ({ hours: Math.floor(m / 60), minutes: m % 60 });\n\nconst Clock = () => (\n  <svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" strokeWidth={1.7} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><circle cx=\"10\" cy=\"10\" r=\"7\" /><path d=\"M10 6v4l2.5 2\" /></svg>\n);\n\n/** ساعت ۲۴ساعته با ارقام فارسی: «۹۳۰» یا «۹:۳۰» تایپ کنید، ↑↓ گام به گام */\nexport const TimeInput = forwardRef<HTMLInputElement, TimeInputProps>(function TimeInput(\n  { value, defaultValue = null, onChange, step = 15, min, max, digits, invalidMessage,\n    rangeMessage, error, name, placeholder, onBlur, onKeyDown, ...rest },\n  ref,\n) {\n  const { messages: M, digits: D, locale } = useKashi();\n  digits ??= D;\n  invalidMessage ??= M.invalid.time;\n  rangeMessage ??= M.invalid.timeRange;\n  const [inner, setInner] = useState<string | null>(defaultValue);\n  const current = value !== undefined ? value : inner;\n  const show = (s: string | null) => (s ? (digits === \"fa\" ? toFaDigits(s) : s) : \"\");\n  const [text, setText] = useState(show(current));\n  const [invalid, setInvalid] = useState<false | \"format\" | \"range\">(false);\n  useEffect(() => setText(show(current)), [current, digits]);\n\n  const lo = min ? toMin(parseTime(min)!) : 0;\n  const hi = max ? toMin(parseTime(max)!) : 24 * 60 - 1;\n  const commit = (t: Time | null) => {\n    const v = t ? formatTime(fromMin(Math.min(hi, Math.max(lo, toMin(t))))) : null;\n    if (value === undefined) setInner(v);\n    setText(show(v));\n    setInvalid(false);\n    if (v !== current) onChange?.(v);\n  };\n\n  return (\n    <>\n      <Input\n        ref={ref}\n        type=\"text\"\n        inputMode=\"numeric\"\n        dir=\"ltr\"\n        autoComplete=\"off\"\n        placeholder={placeholder ?? (digits === \"fa\" ? \"۰۹:۳۰\" : \"09:30\")}\n        value={text}\n        addonEnd={<Clock />}\n        error={error ?? (invalid === \"range\" ? rangeMessage(show(formatTime(fromMin(lo))), show(formatTime(fromMin(hi)))) : invalid ? invalidMessage : undefined)}\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 p = parseTime(t);\n            if (!p) setInvalid(\"format\");\n            else if (toMin(p) < lo || toMin(p) > hi) setInvalid(\"range\");\n            else commit(p);\n          }\n          onBlur?.(e);\n        }}\n        onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {\n          if (e.key === \"ArrowUp\" || e.key === \"ArrowDown\") {\n            e.preventDefault();\n            const base = parseTime(text) ?? parseTime(current ?? \"\") ?? fromMin(lo);\n            const m = toMin(base);\n            // به نزدیک‌ترین گام بعدی یا قبلی می‌پرد\n            const next = e.key === \"ArrowUp\" ? Math.floor(m / step) * step + step : Math.ceil(m / step) * step - step;\n            commit(fromMin(Math.min(hi, Math.max(lo, next))));\n          }\n          onKeyDown?.(e);\n        }}\n        {...rest}\n      />\n      {name && <input type=\"hidden\" name={name} value={current ?? \"\"} />}\n    </>\n  );\n});\n"
    }
  ]
}