DateRangePicker
Two date fields and a button that opens a RangeCalendar.
mmddyyyy
mmddyyyy
No range yet.
<script>
import { $bind, $signal } from 'brumate/signals';
import Button from '@brumate/ui/Button.brm';
import CalendarGrid from '@brumate/ui/CalendarGrid.brm';
import CalendarHeading from '@brumate/ui/CalendarHeading.brm';
import DateInput from '@brumate/ui/DateInput.brm';
import DateRangePicker from '@brumate/ui/DateRangePicker.brm';
import Dialog from '@brumate/ui/Dialog.brm';
import Group from '@brumate/ui/Group.brm';
import Label from '@brumate/ui/Label.brm';
import Popover from '@brumate/ui/Popover.brm';
import RangeCalendar from '@brumate/ui/RangeCalendar.brm';
let stay = $signal<{ start: string; end: string } | null>(null);
</script>
<DateRangePicker class="ui-field" value=$bind(stay)>
<Label>Stay</Label>
<Group class="ui-group">
<DateInput slot="start" class="ui-input ui-date-input" />
<span aria-hidden="true">–</span>
<DateInput slot="end" class="ui-input ui-date-input" />
<Button class="ui-button ui-icon-button" aria-label="Calendar">▾</Button>
</Group>
<Popover class="ui-popover">
<Dialog>
<RangeCalendar class="ui-calendar">
<header>
<Button slot="previous" class="ui-button ui-icon-button">‹</Button>
<CalendarHeading />
<Button slot="next" class="ui-button ui-icon-button">›</Button>
</header>
<CalendarGrid />
</RangeCalendar>
</Dialog>
</Popover>
</DateRangePicker>
<p>{stay ? `From ${stay.start} to ${stay.end}.` : 'No range yet.'}</p>
<DateRangePicker value=$bind(stay)>
<Label>Stay</Label>
<Group>
<DateInput slot="start" />
<span aria-hidden="true">–</span>
<DateInput slot="end" />
<Button>▾</Button>
</Group>
<Popover>
<Dialog>
<RangeCalendar>
<CalendarGrid />
</RangeCalendar>
</Dialog>
</Popover>
</DateRangePicker>
- A
DateRangePickerholds aLabel, aGroupwith aDateInputwithslot="start", one withslot="end"and aButton, and aPopoverwith aDialogand a RangeCalendar. ATextwithslot="description"and aFieldErrorwork as in a DateField. valueis the range as{ start, end }ofYYYY-MM-DDdates, or null until both dates are set. It starts its own state, or binds with$bind. See State.- A date that is only typed so far stays in its input while the value is null.
onChangegets each change, whether it is typed or picked.startNameandendNamepost the dates with a form, from hidden inputs.isRequiredasks the browser to check them on a submit, and the FieldError shows the message. A reset of the form gives back the first range.- It shows
data-open,data-disabled,data-readonly,data-invalidanddata-required.
The RangeCalendar
- The Button opens the Popover. The RangeCalendar shows the range, and its start takes focus, or
placeholderValuewhen there is no range. - A Dialog with no Heading is named by the Button and the Label. The Group is named by the Label and described as the field is.
- A range picked there sets both dates and closes the Popover, and focus goes back to the Button.
shouldCloseOnSelect={false}keeps it open. - The RangeCalendar follows
minValue,maxValueandisDateUnavailableof the picker, unless it has its own. isOpenis whether the Popover shows. It starts its own state, or binds with$bind, andonOpenChangegets each change.
Validation
- A start after the end makes the range invalid.
- So do
minValue,maxValueandisDateUnavailable, for either date, as in a DateField. - The errors of a Form for
startNameorendNameshow in the FieldError.