{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "date-time-range",
  "title": "Date-Time Range",
  "description": "A date range picker with start and end times.",
  "dependencies": [
    "lucide-react",
    "react-day-picker"
  ],
  "registryDependencies": [
    "utils",
    "button",
    "input",
    "label",
    "calendar",
    "popover"
  ],
  "files": [
    {
      "path": "registry/default/ui/date-time-range.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { CalendarIcon } from \"lucide-react\"\nimport type { DateRange } from \"react-day-picker\"\n\nimport { cn } from \"@/registry/default/lib/utils\"\nimport { Button, buttonVariants } from \"@/registry/default/ui/button\"\nimport { Calendar } from \"@/registry/default/ui/calendar\"\nimport { Input } from \"@/registry/default/ui/input\"\nimport { Label } from \"@/registry/default/ui/label\"\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/registry/default/ui/popover\"\n\ntype DateTimeRange = {\n  from?: Date\n  to?: Date\n}\n\nconst DEFAULT_START_TIME = \"09:00\"\nconst DEFAULT_END_TIME = \"17:00\"\n\nfunction timeOf(date: Date | undefined, fallback: string) {\n  if (!date) return fallback\n  return `${String(date.getHours()).padStart(2, \"0\")}:${String(date.getMinutes()).padStart(2, \"0\")}`\n}\n\nfunction withTime(date: Date | undefined, time: string) {\n  if (!date) return undefined\n  const [hours = 0, minutes = 0] = time.split(\":\").map(Number)\n  const next = new Date(date)\n  next.setHours(hours, minutes, 0, 0)\n  return next\n}\n\nfunction formatRange(range: DateTimeRange, locale?: string) {\n  const formatter = new Intl.DateTimeFormat(locale, {\n    dateStyle: \"medium\",\n    timeStyle: \"short\",\n  })\n  if (range.from && range.to) return formatter.formatRange(range.from, range.to)\n  if (range.from) return formatter.format(range.from)\n  return null\n}\n\nfunction DateTimeRangePicker({\n  value,\n  defaultValue = {},\n  onValueChange,\n  locale,\n  placeholder = \"Pick a date range\",\n  numberOfMonths = 2,\n  disabled,\n  className,\n  id,\n}: {\n  value?: DateTimeRange\n  defaultValue?: DateTimeRange\n  onValueChange?: (range: DateTimeRange) => void\n  locale?: string\n  placeholder?: string\n  numberOfMonths?: number\n  disabled?: boolean\n  className?: string\n  id?: string\n}) {\n  const [uncontrolledRange, setUncontrolledRange] = React.useState(defaultValue)\n  const [isOpen, setIsOpen] = React.useState(false)\n  const range = value ?? uncontrolledRange\n  const startTime = timeOf(range.from, DEFAULT_START_TIME)\n  const endTime = timeOf(range.to, DEFAULT_END_TIME)\n  const label = formatRange(range, locale)\n  const timeFieldId = React.useId()\n\n  function updateRange(next: DateTimeRange) {\n    if (value === undefined) setUncontrolledRange(next)\n    onValueChange?.(next)\n  }\n\n  function selectDays(days: DateRange | undefined) {\n    updateRange({\n      from: withTime(days?.from, startTime),\n      to: withTime(days?.to, endTime),\n    })\n  }\n\n  return (\n    <Popover open={isOpen} onOpenChange={setIsOpen}>\n      <PopoverTrigger\n        id={id}\n        disabled={disabled}\n        data-slot=\"date-time-range\"\n        data-empty={label ? undefined : \"\"}\n        className={cn(\n          buttonVariants({ variant: \"outline\" }),\n          \"w-full max-w-sm justify-start font-normal data-[empty]:text-muted-foreground\",\n          className\n        )}\n      >\n        <CalendarIcon />\n        <span className=\"truncate\">{label ?? placeholder}</span>\n      </PopoverTrigger>\n      <PopoverContent align=\"start\" className=\"w-auto p-0\">\n        <Calendar\n          mode=\"range\"\n          numberOfMonths={numberOfMonths}\n          defaultMonth={range.from}\n          selected={{ from: range.from, to: range.to }}\n          onSelect={selectDays}\n        />\n        <div className=\"grid grid-cols-2 gap-3 border-t p-3\">\n          <div className=\"grid gap-1.5\">\n            <Label htmlFor={`${timeFieldId}-start`}>Start time</Label>\n            <Input\n              id={`${timeFieldId}-start`}\n              type=\"time\"\n              value={startTime}\n              disabled={!range.from}\n              onChange={(event) =>\n                updateRange({\n                  ...range,\n                  from: withTime(range.from, event.target.value),\n                })\n              }\n            />\n          </div>\n          <div className=\"grid gap-1.5\">\n            <Label htmlFor={`${timeFieldId}-end`}>End time</Label>\n            <Input\n              id={`${timeFieldId}-end`}\n              type=\"time\"\n              value={endTime}\n              disabled={!range.to}\n              onChange={(event) =>\n                updateRange({\n                  ...range,\n                  to: withTime(range.to, event.target.value),\n                })\n              }\n            />\n          </div>\n        </div>\n        <div className=\"flex justify-between gap-2 border-t p-3\">\n          <Button variant=\"ghost\" size=\"sm\" onClick={() => updateRange({})}>\n            Clear\n          </Button>\n          <Button size=\"sm\" onClick={() => setIsOpen(false)}>\n            Done\n          </Button>\n        </div>\n      </PopoverContent>\n    </Popover>\n  )\n}\n\nexport { DateTimeRangePicker, type DateTimeRange }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}