{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-range-picker",
  "title": "Date Range Picker",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "Two native date controls with inclusive range correction and quick-select presets.",
  "registryDependencies": [
    "utils",
    "https://ui.ballmac.com/r/i18n.json"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/date-range-picker.tsx",
      "content": "// Ballmac UI: Date Range Picker. https://ui.ballmac.com/components/date-range-picker\n\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"@/lib/utils\"\nimport { useMessages } from \"@/lib/ballmac/i18n\"\n\ntype DateRange = {\n  /** Inclusive start date in YYYY-MM-DD form. */ from: string\n  /** Inclusive end date in YYYY-MM-DD form. */ to: string\n}\ntype DateRangePreset = DateRange & {\n  /** Button label for the preset. */ label: string\n}\ntype DateRangePickerProps = Omit<\n  React.ComponentProps<\"fieldset\">,\n  \"defaultValue\" | \"value\" | \"onChange\"\n> & {\n  /** Visible and accessible group label. */\n  label: string\n  /** Controlled date range. */\n  value?: DateRange\n  /** Initial date range. */\n  defaultValue?: DateRange\n  /** Called when either endpoint or a preset changes. */\n  onValueChange?: (range: DateRange) => void\n  /** Quick selections shown below the inputs. */\n  presets?: DateRangePreset[]\n  /** Earliest selectable date. */\n  min?: string\n  /** Latest selectable date. */\n  max?: string\n  /** Disable both inputs and presets. */\n  disabled?: boolean\n  /** Optional form field prefix for the start and end dates. */\n  name?: string\n}\nfunction DateRangePicker({\n  className,\n  label,\n  value,\n  defaultValue = { from: \"\", to: \"\" },\n  onValueChange,\n  presets = [],\n  min,\n  max,\n  disabled = false,\n  name,\n  ...props\n}: DateRangePickerProps) {\n  const msg = useMessages()\n  const [internal, setInternal] = React.useState(defaultValue)\n  const current = value ?? internal\n  function set(next: DateRange) {\n    if (value === undefined) setInternal(next)\n    onValueChange?.(next)\n  }\n  function clamp(date: string) {\n    return date && min && date < min\n      ? min\n      : date && max && date > max\n        ? max\n        : date\n  }\n  function change(side: \"from\" | \"to\", date: string) {\n    const next = { ...current, [side]: clamp(date) }\n    if (next.from && next.to && next.from > next.to) {\n      if (side === \"from\") next.to = next.from\n      else next.from = next.to\n    }\n    set(next)\n  }\n  return (\n    <fieldset\n      data-slot=\"date-range-picker\"\n      disabled={disabled}\n      className={cn(\n        \"bg-card flex min-w-0 flex-col gap-3 rounded-xl border border-border p-4\",\n        className,\n      )}\n      {...props}\n    >\n      <legend className=\"px-1 text-sm font-medium\">{label}</legend>\n      <div className=\"grid min-w-0 gap-3 sm:grid-cols-2\">\n        <label className=\"flex min-w-0 flex-col gap-1.5 text-xs font-medium text-muted-foreground\">\n          <span>{msg(\"date-range-picker.from\", \"From\")}</span>\n          <span>\n            <input\n              data-slot=\"date-range-from\"\n              type=\"date\"\n              name={name ? `${name}.from` : undefined}\n              value={current.from}\n              min={min}\n              max={max}\n              onChange={(event) => change(\"from\", event.target.value)}\n              disabled={disabled}\n              className=\"border-input bg-background text-foreground h-9 w-full min-w-0 rounded-md border px-3 text-sm outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-50\"\n            />\n          </span>\n        </label>\n        <label className=\"flex min-w-0 flex-col gap-1.5 text-xs font-medium text-muted-foreground\">\n          <span>{msg(\"date-range-picker.to\", \"To\")}</span>\n          <span>\n            <input\n              data-slot=\"date-range-to\"\n              type=\"date\"\n              name={name ? `${name}.to` : undefined}\n              value={current.to}\n              min={min}\n              max={max}\n              onChange={(event) => change(\"to\", event.target.value)}\n              disabled={disabled}\n              className=\"border-input bg-background text-foreground h-9 w-full min-w-0 rounded-md border px-3 text-sm outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-50\"\n            />\n          </span>\n        </label>\n      </div>\n      {presets.length > 0 && (\n        <div\n          data-slot=\"date-range-presets\"\n          className=\"flex flex-wrap gap-2 border-t border-border pt-3\"\n        >\n          {presets.map((preset) => (\n            <button\n              key={preset.label}\n              type=\"button\"\n              disabled={disabled}\n              aria-pressed={\n                current.from === clamp(preset.from) &&\n                current.to === clamp(preset.to)\n              }\n              onClick={() =>\n                set({ from: clamp(preset.from), to: clamp(preset.to) })\n              }\n              className=\"bg-secondary text-secondary-foreground hover:bg-accent h-8 rounded-full px-3 text-xs font-medium outline-none transition-colors duration-150 focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:opacity-50 motion-reduce:transition-none\"\n            >\n              {preset.label}\n            </button>\n          ))}\n        </div>\n      )}\n    </fieldset>\n  )\n}\nexport {\n  DateRangePicker,\n  type DateRangePickerProps,\n  type DateRange,\n  type DateRangePreset,\n}\n",
      "type": "registry:ui",
      "target": "@components/ballmac/date-range-picker.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "date",
      "range",
      "calendar"
    ],
    "version": "1.0.0",
    "updated": "2026-09-30",
    "ai": {
      "summary": "Two native date controls with inclusive range correction and quick-select presets.",
      "whenToUse": [
        "Pick report or booking date windows"
      ],
      "whenNotToUse": [
        "Use Date Picker for one date"
      ],
      "composesWith": [
        "time-picker"
      ],
      "a11y": [
        {
          "keys": "Tab / Arrow keys",
          "action": "Edits native date fields and activates presets"
        }
      ],
      "customization": [
        "Controlled and uncontrolled value",
        "Tokens and state styling"
      ]
    },
    "examples": [
      "date-range-picker-demo",
      "date-range-picker-states"
    ],
    "url": "https://ui.ballmac.com/components/date-range-picker"
  },
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}