Forms · TimeRangePicker

TimeRangePicker

A pair of segmented time inputs (start + end) plus a popover Clock with per-endpoint sliders + AM/PM toggles. Soma owns the range state machine (empty → pending → complete) and the open state; Eidos adds size, variant, intent color, the popover shell and the canonical Footer parts.

parts{compiled.parts.order.length} events{eventRows.length} variants3 sizes3
Working hours {#snippet children({ segments }: InputSnippetProps)} {#each segments as { part, value }, i (`${part}-${i}`)} {value} {/each} {/snippet} {#snippet children({ segments }: InputSnippetProps)} {#each segments as { part, value }, i (`${part}-${i}`)} {value} {/each} {/snippet}
Start {#if granularity === 'second'} {/if} {#if hourCycle === 12} {/if}
End {#if granularity === 'second'} {/if} {#if hourCycle === 12} {/if}
{#if showFooter && (showClear || showCancel || showClose)} {#if showClear}{/if} {#if showCancel}{/if} {#if showClose}{/if} {/if}
trace {#if trace.length === 0} edit, open the clock or play an event to see sema stamps {:else} {#each trace.slice(0, 3) as entry} {entry.event} · {entry.family}{entry.intent ? ' · ' + entry.intent : ''} {fmtTime(entry.at)} {/each} {/if} value {valueLabel}
{#if tab === 'live'}

Controls

soma owns the range state machine, segment editing, validation and the open state. eidos owns visual treatment.

soma props · value + behaviour
Footer composition

Each switch decides whether the part is included in the markup. The parts never read these flags — omitting the part hides it, including it always renders.

eidos props · visual treatment
soma headless · provider + sliders svelte
{somaSnippet}
eidos visual · root + attached parts svelte
{eidosSnippet}
{/if} {#if tab === 'api'}

API + reference comparison

FeatureReact AriaArkChakraUIXDecision
Paired segmented inputsyes (Date)nonoyesSoma owns start/end editors.
Per-endpoint slidersnononoyesTwo slider columns; share generic Slider.
Range state machineyes (Date)n/an/ayesEmpty → pending → complete coordinated in soma.
mode='inline' | 'modal'nononoyesSame contract as date / time / color pickers.
Footer = compositionnononoyesNo *Button root props — pure composition.
commit/cancel/clear helpersmanualmanualmanualyesWired by Footer parts via picker-shell context.
closeOnRangeCompletenononoyesAuto-dismiss when both endpoints set.
PropOwnerDefaultNotes
value / placeholdersoma—Bindable TimeRange.
opensomafalseBindable popover state.
modesomainlineinline closes on outside-click; modal requires footer action.
closeOnRangeCompletesomafalseAuto-close once both endpoints are set.
minValue / maxValuesoma—Validation bounds applied to both endpoints.
granularitysomaminuteHour / minute / second.
hourCyclesomalocale12h adds dayPeriod toggle per endpoint.
start / endReadonlySegmentssoma[]Lock segments per endpoint.
size / variant / coloreidosmd / surface / primaryVisual-only data attrs.
{/if} {#if tab === 'morfo'}

Morfo contract

{#each partsList as part} {/each}
PartMarkerElementRoleOptional
{part.kebab} [{part.marker}] <{part.defaultElement}> {part.role ?? '—'} {part.optional ? 'yes' : 'no'}
{/if} {#if tab === 'sema'}

sema · events + perceptual signature

TimeRangePicker declares open / dismiss / commit / clear events on its own surface. The underlying TimeRangeField and Popover contribute their own events too.

{#each eventRows as row} {/each}
OwnerNameFamilyVerbSequenceTargetPlay
{row.owner} {row.action.name} {row.action.semantic.family} {row.action.semantic.verb ?? '—'} {row.action.semantic.sequence ?? 'pre'} {row.action.target}
{/if} {#if tab === 'recipe'}

Eidos recipe

Selectors live in src/uix/eidos/components/time-range-picker/time-range-picker.css and reuse the time-field tokens for the field portion. The clock body lays out two endpoint columns (start / end) and inherits slider chrome from the slider recipe.

SelectorOwnerPurpose
[data-time-range-picker]morfoRoot visual scope + size/variant/color tokens.
[data-time-range-picker-trigger]morfoOpen-clock button. Has embedded variant inside the input.
[data-time-range-picker-clock][data-state]morfoPopover dialog surface. 2-column endpoint grid.
[data-time-range-picker-hour-slider][data-endpoint]morfoPer-endpoint slider wrapper. Composes generic Slider chrome.
[data-picker-footer][data-mode]eidosFooter action row shared via picker-shell.
{/if} {#if tab === 'a11y'}

Accessibility

ConcernContract
DialogClock exposes role="dialog" with an accessible name.
Triggeraria-haspopup="dialog" + aria-expanded + aria-controls.
SegmentsEditable segments expose role="spinbutton" with min/max/value per endpoint.
SlidersNative slider semantics — keyboard arrow/page/home/end per endpoint.
Modalmode='modal' blocks outside-click + Escape; consumer must wire Close/Cancel.
FormHidden inputs submit ISO strings for both endpoints when name is set.
{/if}