SvDateRangeInput

A compact start/end field that opens a two-month range calendar with one-click presets (Today, Last 7 days, This month, ...). Emits an inclusive [start, end] tuple or null.

SvDateRangeInput composes the existing headless range engine: the popover is just a <SvCalendar selectionMode="range">, so hover-preview, min/max, restricted / important dates and keyboard all come for free. It carries the shared editor contract (label, hint, error validation, size, RTL, localizable messages) via SvField, exactly like the other field editors, and the popover portals to <body> so it is never clipped by a scrolling grid.

Basic usage

<script lang="ts">
  import { SvDateRangeInput } from '@svgrid/grid'
  let range = $state<[Date, Date] | null>(null)
</script>

<SvDateRangeInput
  value={range}
  formatString="yyyy-MM-dd"
  months={2}
  onChange={(v) => (range = v)}
/>

Props

SvDateRangeInput extends SvEditorProps (disabled, readonly, required, invalid, error, label, hint, size, dir, name, id, ariaLabel). Its own props:

Prop Type Default Description
value DateRangeValue null Inclusive [start, end] range, or null.
onChange (value: DateRangeValue) => void - Fires with the new range (or null when cleared).
formatString string yyyy-MM-dd Display format for each end (token engine).
min / max DateLike | null null Selectable bounds.
months number 2 Number of month panels shown side by side.
firstDayOfWeek number 0 0 = Sunday .. 6 = Saturday.
weekNumbers boolean false Show the calendar's week-number column.
locale string navigator BCP-47 locale for names and formatting.
placeholder string Select date range Empty-field placeholder.
presets ReadonlyArray<CalendarPreset> - Quick shortcuts shown in the calendar's side rail.
animate boolean | slide | fade false Animate the calendar's month navigation.
autoOpen boolean false Open the popover as soon as the field is focused.

Helper types

Patterns

Preset shortcuts

Presets appear in a side rail on the calendar; function values resolve relative to today at click time, so "Last 7 days" is always correct:

<SvDateRangeInput
  presets={[
    { label: 'Last 7 days', value: () => [addDays(new Date(), -6), new Date()] },
    { label: 'This month', value: () => [startOfMonth(new Date()), new Date()] },
  ]}
  onChange={(v) => (range = v)}
/>

Bounded ranges

Pass min / max to keep the selection inside an allowed window - out-of-range days render disabled in the popover calendar:

<SvDateRangeInput min={new Date(2024, 0, 1)} max={new Date()} onChange={(v) => (range = v)} />

Labelled form field

Because it carries the editor contract, label, hint, and validation wire up like any other field editor:

<SvDateRangeInput
  label="Reporting period"
  hint="Both ends inclusive"
  required
  size="lg"
  onChange={(v) => (range = v)}
/>

Two-way bound booking range

Keep the tuple in your own $state and derive from it - here a night count for a booking. min={new Date()} blocks past check-in dates:

<script lang="ts">
  import { SvDateRangeInput, type DateRangeValue } from '@svgrid/grid'
  let stay = $state<DateRangeValue>(null)
  const nights = $derived(stay ? Math.round((+stay[1] - +stay[0]) / 86400000) : 0)
</script>

<SvDateRangeInput
  label="Check-in / check-out"
  value={stay}
  min={new Date()}
  months={2}
  onChange={(v) => (stay = v)}
/>
<p>{nights} night(s)</p>

Tip: the range is inclusive and both ends are normalized to start-of-day, so subtracting the two timestamps gives whole nights without time-of-day drift.

Accessibility

See also