Forms - DateRangePicker

DateRangePicker

Segmented range input with an attached range calendar popover. Soma owns the two endpoints, min/max validation, popover state and range selection; Eidos only adds the visual size, variant and color recipe.

parts{compiled.parts.order.length} events{events.length} value{valueLabel} min/max{boundsLabel} months{numberOfMonths}
{labelText}
{#snippet children({ segments }: DateRangePickerInputSnippetProps)} {#each segments as { part, value }, index (`start-${part}-${index}`)} {value} {/each} {/snippet} {#snippet children({ segments }: DateRangePickerInputSnippetProps)} {#each segments as { part, value }, index (`end-${part}-${index}`)} {value} {/each} {/snippet}
{statusMessage}
{#snippet children(args: DateRangePickerCalendarSnippetProps)} {@render calendarBody(args)} {/snippet}
trace {#if trace.length === 0} open the picker and select a range {:else} {#each trace.slice(0, 3) as entry} {entry.event} - {entry.family}{entry.intent ? ' - ' + entry.intent : ''} {fmtTime(entry.at)} {/each} {/if} open {String(open)} range {valueLabel}
{#if tab === 'live'}

Controls

soma owns value, min/max dates, range length, disabled/unavailable dates and popover behavior. eidos owns visual treatment.

soma props - range value and constraints
eidos props - visual treatment
Demo content
soma headless - shared range, field, popover and calendar svelte
{somaSnippet}
eidos visual - root plus attached field, popover and range-calendar parts svelte
{eidosSnippet}
{/if} {#if tab === 'api'}

API reference

DateRangePicker is a composition over DateRangeField, Popover and RangeCalendar. Air has no local DateRangePicker; Terra in morfo-runtime is the historical baseline.

PropOwnerDefaultDescription
value bindablesomaundefinedRange {'{ start, end }'} shared by both inputs and calendar.
placeholder bindablesomatodayVisible month anchor.
open bindablesomafalsePopover state.
minValue / maxValuesoma-Selectable date bounds. Invalid pairs mark the range calendar invalid and log through UIX.
minDays / maxDays / allowSingleDaysoma- / - / trueInclusive range length constraints.
isDateDisabled / isDateUnavailable / isDateHolidaysoma-Per-date matchers for hard disabled, unavailable and decorative holiday dates.
startReadonlySegments / endReadonlySegmentssoma[]Independent segment locks for each endpoint.
numberOfMonths / fixedWeeks / pagedNavigationsoma2 / false / falseRange calendar layout and navigation behavior.
closeOnRangeSelectsomatrueClose the popover when both endpoints are selected.
size / variant / coloreidosmd / surface / primaryVisual-only data attrs for field, trigger and calendar.
Reference comparison
FeatureAirTerra morfo-runtimeReact AriaArkBitsUIX decision
DateRangePicker primitivenoyesyesrange modeyesAdd Eidos wrapper over current Soma composition.
Segmented start/end inputs-yesyesyesyesUse DateRangeField parts inside the picker namespace.
Min/max date controls-yesyesyesyesExpose as direct props and visible demo controls.
Range length constraints-min/max dayscustom validationrange constraintscustomFirst-class `minDays`, `maxDays`, `allowSingleDay`.
Multiple visible monthscalendar onlyyesyesyesyesTop heading shows full range; each visible month gets its own title only when there are 2 months.
{/if} {#if tab === 'morfo'}

morfo contract

Source: src/uix/morfo/components/date-range-picker.ts. The picker declares only the composition root, trigger and calendar bridge; DateRangeField, RangeCalendar and Popover keep their own contracts.

FieldValue
name"{dateRangePickerMorfo.name}"
kebab"{dateRangePickerMorfo.kebab}"
scope[{dateRangePickerMorfo.scope.map((s) => `"${s}"`).join(', ')}]
apgcomposed: DateRangeField + Popover + RangeCalendar
parts.length{dateRangePickerMorfo.parts.length}
own events{dateRangePickerEvents.length}
composed events{events.length}
Parts
{#each partsList as part} {/each}
PartMarkerElementRoleArchetypeStatesOptional
{part.kebab} [{part.marker}] <{part.defaultElement}> {part.role ?? '-'} {part.archetype ?? '-'} {part.states.length ? part.states.join(' | ') : '-'} {part.optional ? 'yes' : 'no'}
Composed contracts
ContractPartsEventsRole in picker
DateRangeField{dateRangeFieldCompiled.parts.order.length}{dateRangeFieldCompiled.actions.byName.size}Label plus start/end segmented inputs.
RangeCalendar{rangeCalendarCompiled.parts.order.length}{rangeCalendarCompiled.actions.byName.size}Range grid, min/max, preview and navigation.
Popover{popoverCompiled.parts.order.length}{popoverCompiled.actions.byName.size}Disclosure, portal and floating content.
Events declaration
{#each eventRows as { owner, action }} {@const intentDecl = 'intent' in action.semantic ? action.semantic.intent : undefined} {/each}
NameFamilyVerbSequenceIntentTarget
{owner}.{action.name} {action.semantic.family} {action.semantic.verb ?? '-'} {action.semantic.sequence ?? 'pre'} {typeof intentDecl === 'string' ? intentDecl : intentDecl ? 'fromProp' : '-'} {action.target}
{/if} {#if tab === 'sema'}

sema events

The public picker is a composition. Its event surface is the union of range calendar selection/navigation events, popover disclosure events and any field commit events.

OwnerEventsResponsibility
DateRangePicker{dateRangePickerEvents.length}Root state and composition.
DateRangeField{dateRangeFieldEvents.length}Endpoint field commits.
RangeCalendar{rangeCalendarEvents.length}Start selection, range commit, clear and calendar navigation.
Popover{popoverCompiled.actions.byName.size}Disclosure events.
Composed event declarations
{#each eventRows as { owner, action }} {@const intentDecl = 'intent' in action.semantic ? action.semantic.intent : undefined} {/each}
OwnerNameFamilyVerbSequenceIntent
{owner} {action.name} {action.semantic.family} {action.semantic.verb ?? '-'} {action.semantic.sequence ?? 'pre'} {typeof intentDecl === 'string' ? intentDecl : intentDecl ? 'fromProp' : '-'}
{/if} {#if tab === 'recipe'}

Eidos recipe

Selectors live in src/uix/eidos/components/date-range-picker/date-range-picker.css. The field and trigger use DateField tokens; the popup range grid mirrors Calendar without drawing a nested card.

SelectorSourcePurpose
[data-date-range-picker]morfoRoot provider, open and invalid states.
[data-date-range-picker][data-size][data-variant][data-color]eidosVisual scale, treatment and intent palette.
[data-date-range-field-input][data-endpoint]morfoStart and end segmented fields.
[data-date-range-picker-trigger]morfoCalendar opener button.
[data-date-range-picker-calendar][data-range-calendar]morfoRange calendar bridge with nested chrome removed.
[data-range-calendar-day][data-in-range]morfoRange track styling.
[data-range-calendar-day][data-range-start]morfoCommitted endpoint styling.
{/if} {#if tab === 'a11y'}

Accessibility

The picker combines DateRangeField's grouped segmented inputs, Popover disclosure and RangeCalendar's application-labelled grid.

ConcernContract
Triggeraria-haspopup="dialog", aria-expanded and calendar label from Morfo translations.
InputsEach endpoint is a DateField group with editable spinbutton segments.
CalendarRange grid exposes role="application", grid cells and day buttons.
KeyboardArrow keys move by day/week, PageUp/PageDown navigates months, Enter/Space selects, Escape cancels range selection.
ValidationInvalid min/max pairs are surfaced in the calendar snippet and logged through the UIX logger.
{/if}
{#snippet calendarBody({ months, weekdays, validationMessage }: DateRangePickerCalendarSnippetProps)} <
{#each months as month (`hdr-${month.value.toString()}`)}
{formatMonthYear(month.value)}
{/each}
>
{#if validationMessage || boundsMessage || spanMessage} {/if}
{#each months as month (month.value.toString())}
{#if showWeekNumbers} wk {/if} {#each weekdays as day} {day} {/each} {#each month.weeks as week} {#snippet children({ weekNumber }: DateRangePickerGridRowSnippetProps)} {#if showWeekNumbers} {weekNumber ?? ''} {/if} {#each week as date} {/each} {/snippet} {/each}
{/each}
{/snippet}