Usage
AdvancedDateRangePicker from @prepared911/ui-calendar lets operators express date constraints as inequalities rather than contiguous calendar spans. A SegmentedControl at the top switches between three modes—Before, After, Between—and the body renders the corresponding DatePicker configuration. The component is built for filter and search surfaces where "on or before Jan 1", "after 6pm yesterday", and "between Oct 1 and Nov 30" all need to coexist in one control.
Values use the discriminated union AdvancedDateRangeValue keyed by mode:
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
DatePicker in Range mode: the SegmentedControl is overhead without new capability.rangeOnly to hide the mode strip and keep the grid; users see one cleaner control.AdvancedDateTimeRangePicker.AdvancedTimeRangePicker (shared pattern, no calendar).SegmentedControl with three SegmentedControlItems: Before, After, Between. Hidden when rangeOnly is true.DatePicker rendered in the mode's configuration:
Single mode; the selected date is the bound.Range mode; both endpoints are selectable.onApply is provided, the component defers onChange until Apply fires and shows a Reset button.The three modes are carried in AdvancedDateRangeValue's discriminated mode field:
| Mode | Selection | Semantics |
|---|---|---|
Before | one date | the constraint is "≤ selected date" |
After | one date | the constraint is "≥ selected date" |
Between | two dates | inclusive range [startDate, endDate] |
Switching modes preserves compatible fields (a Between end becomes the Before date; an After start becomes the Between start) so operators don't lose context when changing their mind mid-selection.
onApply is provided, the picker tracks in-progress selection internally and only commits on Apply. onReset (or the built-in Reset when hideReset is false) returns the control to an empty selection.disableFuture, disablePastDates, and disableFutureDates all route to the underlying DatePicker's MonthCaption, so the month and year Selects stay in sync with what's selectable.AdvancedDateRangePicker is usually rendered inside a Popover (see dispatch's CustomTimeRangePopoverContent); the component itself has no trigger, so the consuming surface handles open state.rangeOnly. Hides the mode strip and always uses Between. Pick this when the product has no inequality semantics: it's a lighter UI.hideMonthYearSelect. Drops the MonthCaption selects for narrow popovers.disableFuture / disablePastDates / disableFutureDates. Constraint knobs; pick the narrowest that matches the query (disabling future dates for past-event filters prevents "show me incidents from next week").hideReset. Hide the Reset button when the surrounding surface already has clear-all affordances (a filter chip's X, a form reset).Do
onApply in filter popovers so dispatching queries stays explicit.disableFutureDates for historical-only filters; the MonthCaption will stop at today automatically.Don't
AdvancedDateRangePicker when the product only ever needs Between: rangeOnly or a plain DatePicker Range is less noise.The mode strip is a SegmentedControl; the grid is a DatePicker. Both handle their own keyboard semantics, but the component does not announce mode changes or the committed value—your surrounding UI has to surface them. See the Accessibility page for the full contract, including focus handoff between the mode strip and grid.