{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-range-field",
  "title": "Date Range Field",
  "description": "Two segmented date fields for a start and an end, joined into one range value with reversed-range and length validation.",
  "registryDependencies": [
    "@tealmac/date-field",
    "@tealmac/date-time-core",
    "@tealmac/date-time-segments"
  ],
  "files": [
    {
      "path": "registry/tealmac/date-range-field.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { DateField } from \"@/registry/tealmac/date-field\"\nimport {\n  rangeLength,\n  type DateRange,\n  type ISODate,\n} from \"@/registry/tealmac/date-time-core\"\nimport { useControllableValue } from \"@/registry/tealmac/date-time-segments\"\n\nexport interface DateRangeFieldProps extends Omit<\n  React.ComponentProps<\"div\">,\n  \"defaultValue\" | \"onChange\" | \"role\"\n> {\n  /** Controlled range of ISO dates, or null while either end is empty. */\n  value?: DateRange | null\n  defaultValue?: DateRange | null\n  /** Called with a complete range whose start is not after its end, else null. */\n  onValueChange?: (value: DateRange | null) => void\n  min?: ISODate\n  max?: ISODate\n  /** Shortest and longest range, in days, both ends included. */\n  minDays?: number\n  maxDays?: number\n  isDateUnavailable?: (date: ISODate) => boolean\n  locale?: string\n  label?: string\n  startLabel?: string\n  endLabel?: string\n  name?: string\n  disabled?: boolean\n  readOnly?: boolean\n  invalid?: boolean\n}\n\nfunction DateRangeField({\n  value,\n  defaultValue = null,\n  onValueChange,\n  min,\n  max,\n  minDays,\n  maxDays,\n  isDateUnavailable,\n  locale,\n  label = \"Date range\",\n  startLabel = \"Start date\",\n  endLabel = \"End date\",\n  name,\n  disabled,\n  readOnly,\n  invalid,\n  className,\n  ...props\n}: DateRangeFieldProps) {\n  const [range, setRange] = useControllableValue<DateRange | null>(\n    value,\n    defaultValue,\n    onValueChange\n  )\n  const [draft, setDraft] = React.useState<{\n    from: ISODate | null\n    to: ISODate | null\n  }>({\n    from: range?.from ?? null,\n    to: range?.to ?? null,\n  })\n  const [seen, setSeen] = React.useState(range)\n  if (range !== seen) {\n    setSeen(range)\n    if (range && (range.from !== draft.from || range.to !== draft.to))\n      setDraft({ from: range.from, to: range.to })\n    if (range === null && draft.from && draft.to)\n      setDraft({ from: null, to: null })\n  }\n\n  function update(next: { from: ISODate | null; to: ISODate | null }) {\n    setDraft(next)\n    const complete = next.from && next.to && next.from <= next.to\n    const out = complete ? { from: next.from!, to: next.to! } : null\n    setSeen(out)\n    setRange(out)\n  }\n\n  const reversed = !!draft.from && !!draft.to && draft.from > draft.to\n  const length =\n    draft.from && draft.to && !reversed\n      ? rangeLength({ from: draft.from, to: draft.to })\n      : null\n  const tooShort = length !== null && minDays !== undefined && length < minDays\n  const tooLong = length !== null && maxDays !== undefined && length > maxDays\n  const bad = invalid || reversed || tooShort || tooLong\n  const message = reversed\n    ? \"The end date is before the start date.\"\n    : tooShort\n      ? `Choose at least ${minDays} days.`\n      : tooLong\n        ? `Choose at most ${maxDays} days.`\n        : null\n  const messageId = React.useId()\n\n  return (\n    <div\n      role=\"group\"\n      aria-label={label}\n      aria-describedby={message ? messageId : undefined}\n      data-slot=\"date-range-field\"\n      data-segment-scope=\"\"\n      data-invalid={bad ? \"\" : undefined}\n      className={cn(\"flex flex-col gap-1.5\", className)}\n      {...props}\n    >\n      <div className=\"flex flex-wrap items-center gap-2\">\n        <DateField\n          label={startLabel}\n          value={draft.from}\n          onValueChange={(from) => update({ ...draft, from })}\n          min={min}\n          max={max}\n          isDateUnavailable={isDateUnavailable}\n          locale={locale}\n          disabled={disabled}\n          readOnly={readOnly}\n          invalid={bad}\n        />\n        <span aria-hidden=\"true\" className=\"text-muted-foreground\">\n          –\n        </span>\n        <DateField\n          label={endLabel}\n          value={draft.to}\n          onValueChange={(to) => update({ ...draft, to })}\n          min={min}\n          max={max}\n          isDateUnavailable={isDateUnavailable}\n          locale={locale}\n          disabled={disabled}\n          readOnly={readOnly}\n          invalid={bad}\n        />\n      </div>\n      {message ? (\n        <p id={messageId} role=\"alert\" className=\"text-destructive text-xs\">\n          {message}\n        </p>\n      ) : null}\n      {name ? (\n        <>\n          <input\n            type=\"hidden\"\n            name={`${name}.from`}\n            value={range?.from ?? \"\"}\n          />\n          <input type=\"hidden\" name={`${name}.to`} value={range?.to ?? \"\"} />\n        </>\n      ) : null}\n    </div>\n  )\n}\n\nexport { DateRangeField }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}