diff --git a/src/uix/eidos/components/time-range-picker/README.md b/src/uix/eidos/components/time-range-picker/README.md
new file mode 100644
index 000000000..71b7a4ade
--- /dev/null
+++ b/src/uix/eidos/components/time-range-picker/README.md
@@ -0,0 +1,98 @@
+# Eidos TimeRangePicker
+
+Composite popover combining a segmented `TimeRangeField` input pair (start + end)
+and a `Clock` view with per-endpoint sliders (Hour / Minute / Second + AM/PM
+toggle). Soma owns range state coordination (`{ start, end }` + open +
+helpers `commit / cancel / clear`); Eidos adds visual treatment, the popover
+content shell, the side-by-side endpoint layout and the canonical Footer
+parts.
+
+## Baseline
+
+Air no tenía un `TimeRangePicker` propio en la rama anterior. La referencia
+real es la composición Soma `time-range-picker` + `time-range-field` +
+`popover` + `slider` que ya implementa la edición segmentada de ambos
+extremos y la selección por sliders independientes (`type="start" |
+"end"`). La receta Eidos sólo envuelve esa superficie con tamaños, variante
+visual y paleta de intents. Referencias externas: React Aria
+DateRangePicker (pares), Ark UI Time Picker (single), Chakra Time Picker.
+
+## Superficie
+
+```svelte
+
+
+ {#snippet children({ segments })}
+ {#each segments as { part, value }, i (`${part}-${i}`)}
+ {value}
+ {/each}
+ {/snippet}
+
+
+ {#snippet children({ segments })}
+ {#each segments as { part, value }, i (`${part}-${i}`)}
+ {value}
+ {/each}
+
+ {/snippet}
+
+
+
+
+
+
+ Start
+
+
+
+
+
+ End
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Partes públicas: `Trigger`, `Content`, `Clock`, `Label`, `Input`,
+`Segment`, `HourSlider`, `MinuteSlider`, `SecondSlider`,
+`DayPeriodToggle`, `Footer`, `Clear`, `Cancel`, `Close`. Cada slider /
+toggle acepta `type: 'start' | 'end'`.
+
+## Comparativa
+
+| Característica | React Aria | Ark | Chakra | UIX |
+| ----------------------------- | ---------- | --- | ------ | --- |
+| Inputs segmentados (par) | sí | no | no | sí |
+| Sliders por endpoint | no | no | no | sí |
+| Modo inline / modal | no | no | no | sí |
+| Footer (composición) | no | no | no | sí |
+| commit / cancel / clear shell | manual | no | no | sí |
+| Range state-machine swap | sí | n/a | n/a | sí |
+
+## Decisiones
+
+- **State machine empty → pending → complete** vive en el provider Soma —
+ el eidos no decide cuándo se cierra. `closeOnRangeComplete` es opcional.
+- **`mode='inline'|'modal'`** propaga al popover (`modal: mode === 'modal'`).
+- **Footer = composición** — sin `*Button` props, igual que TimePicker.
+- **N-8** — los sliders comparten `SliderProvider` (no se reimplementa
+ drag / teclado).
+- **Layout 2 columnas** — Start y End ocupan columnas independientes en
+ el popover. Por debajo de 40rem caen apiladas.
+
+## Gaps
+
+- No hay vista "preset list" (Hoy, Última hora…). Se puede componer fuera.
+- No hay swap automático visible si el usuario fija `end < start` — la
+ validación queda en manos del `validate` opt.
diff --git a/src/uix/eidos/components/time-range-picker/context.svelte.ts b/src/uix/eidos/components/time-range-picker/context.svelte.ts
new file mode 100644
index 000000000..5f420b1de
--- /dev/null
+++ b/src/uix/eidos/components/time-range-picker/context.svelte.ts
@@ -0,0 +1,32 @@
+import { getContext, setContext } from 'svelte';
+import type {
+ TimeRangePickerColor,
+ TimeRangePickerSize,
+ TimeRangePickerVariant
+} from './types';
+
+const TIME_RANGE_PICKER_VISUAL_CONTEXT = Symbol('uix.eidos.time-range-picker.visual');
+
+export interface TimeRangePickerVisualContext {
+ getSize(): TimeRangePickerSize;
+ getVariant(): TimeRangePickerVariant;
+ getColor(): TimeRangePickerColor;
+ /**
+ * Whether to render the labelled ruler scale next to each slider.
+ * `true` by default; pass `ruler={false}` on `` to hide
+ * the rulers entirely, or `ruler={false}` on an individual slider eidos
+ * wrapper to opt that one out.
+ */
+ getRuler(): boolean;
+}
+
+export function setTimeRangePickerVisualContext(
+ ctx: TimeRangePickerVisualContext
+): TimeRangePickerVisualContext {
+ setContext(TIME_RANGE_PICKER_VISUAL_CONTEXT, ctx);
+ return ctx;
+}
+
+export function getTimeRangePickerVisualContext(): TimeRangePickerVisualContext | undefined {
+ return getContext(TIME_RANGE_PICKER_VISUAL_CONTEXT);
+}
diff --git a/src/uix/eidos/components/time-range-picker/index.ts b/src/uix/eidos/components/time-range-picker/index.ts
new file mode 100644
index 000000000..0d3fbba4e
--- /dev/null
+++ b/src/uix/eidos/components/time-range-picker/index.ts
@@ -0,0 +1,93 @@
+import TimeRangePickerComponent from './time-range-picker.svelte';
+import Trigger from './time-range-picker-trigger.svelte';
+import Content from './time-range-picker-content.svelte';
+import Clock from './time-range-picker-clock.svelte';
+// PickerShell parts — shared by every composite picker.
+import Footer from '../picker-shell/picker-shell-footer.svelte';
+import Clear from '../picker-shell/picker-shell-clear.svelte';
+import Cancel from '../picker-shell/picker-shell-cancel.svelte';
+import Close from '../picker-shell/picker-shell-close.svelte';
+import HourSlider from './time-range-picker-hour-slider.svelte';
+import MinuteSlider from './time-range-picker-minute-slider.svelte';
+import SecondSlider from './time-range-picker-second-slider.svelte';
+import DayPeriodToggle from './time-range-picker-day-period-toggle.svelte';
+// Field surface is re-exported direct from soma — eidos shares the
+// time-range-field styling layer via [data-time-range-field] selectors.
+import * as TimeRangePickerSoma from '$soma/components/time-range-picker';
+import Anchor from '../popover/popover-anchor.svelte';
+import Overlay from '../popover/popover-overlay.svelte';
+import Arrow from '../popover/popover-arrow.svelte';
+import { Portal } from '$soma/components/internal';
+
+const Label = TimeRangePickerSoma.Label;
+const Input = TimeRangePickerSoma.Input;
+const Segment = TimeRangePickerSoma.Segment;
+
+type TimeRangePickerNamespace = typeof TimeRangePickerComponent & {
+ Trigger: typeof Trigger;
+ Portal: typeof Portal;
+ Anchor: typeof Anchor;
+ Overlay: typeof Overlay;
+ Content: typeof Content;
+ Arrow: typeof Arrow;
+ Clock: typeof Clock;
+ Footer: typeof Footer;
+ Clear: typeof Clear;
+ Cancel: typeof Cancel;
+ Close: typeof Close;
+ HourSlider: typeof HourSlider;
+ MinuteSlider: typeof MinuteSlider;
+ SecondSlider: typeof SecondSlider;
+ DayPeriodToggle: typeof DayPeriodToggle;
+ Label: typeof Label;
+ Input: typeof Input;
+ Segment: typeof Segment;
+};
+
+const TimeRangePicker = TimeRangePickerComponent as TimeRangePickerNamespace;
+TimeRangePicker.Trigger = Trigger;
+TimeRangePicker.Portal = Portal;
+TimeRangePicker.Anchor = Anchor;
+TimeRangePicker.Overlay = Overlay;
+TimeRangePicker.Content = Content;
+TimeRangePicker.Arrow = Arrow;
+TimeRangePicker.Clock = Clock;
+TimeRangePicker.Footer = Footer;
+TimeRangePicker.Clear = Clear;
+TimeRangePicker.Cancel = Cancel;
+TimeRangePicker.Close = Close;
+TimeRangePicker.HourSlider = HourSlider;
+TimeRangePicker.MinuteSlider = MinuteSlider;
+TimeRangePicker.SecondSlider = SecondSlider;
+TimeRangePicker.DayPeriodToggle = DayPeriodToggle;
+TimeRangePicker.Label = Label;
+TimeRangePicker.Input = Input;
+TimeRangePicker.Segment = Segment;
+
+export { TimeRangePicker };
+
+export default TimeRangePicker;
+
+export type {
+ TimeRangePickerProps,
+ TimeRangePickerTriggerProps as TriggerProps,
+ TimeRangePickerContentProps as ContentProps,
+ TimeRangePickerClockProps as ClockProps,
+ TimeRangePickerFooterProps as FooterProps,
+ TimeRangePickerClearProps as ClearProps,
+ TimeRangePickerCancelProps as CancelProps,
+ TimeRangePickerCloseProps as CloseProps,
+ TimeRangePickerLabelProps as LabelProps,
+ TimeRangePickerInputProps as InputProps,
+ TimeRangePickerSegmentProps as SegmentProps,
+ TimeRangePickerHiddenInputProps as HiddenInputProps,
+ TimeRangePickerAnchorProps as AnchorProps,
+ TimeRangePickerOverlayProps as OverlayProps,
+ TimeRangePickerArrowProps as ArrowProps,
+ TimeRangePickerSliderProps as SliderProps,
+ TimeRangePickerDayPeriodToggleProps as DayPeriodToggleProps,
+ InputSnippetProps,
+ TimeRangePickerSize,
+ TimeRangePickerVariant,
+ TimeRangePickerColor
+} from './types';
diff --git a/src/uix/eidos/components/time-range-picker/time-range-picker-clock.svelte b/src/uix/eidos/components/time-range-picker/time-range-picker-clock.svelte
new file mode 100644
index 000000000..2d7b18b17
--- /dev/null
+++ b/src/uix/eidos/components/time-range-picker/time-range-picker-clock.svelte
@@ -0,0 +1,23 @@
+
+
+
+ {@render children?.()}
+
diff --git a/src/uix/eidos/components/time-range-picker/time-range-picker-content.svelte b/src/uix/eidos/components/time-range-picker/time-range-picker-content.svelte
new file mode 100644
index 000000000..2fe3ea991
--- /dev/null
+++ b/src/uix/eidos/components/time-range-picker/time-range-picker-content.svelte
@@ -0,0 +1,71 @@
+
+
+
+ {#snippet children(snippetProps)}
+ {@render bodyContent?.(snippetProps)}
+ {/snippet}
+
diff --git a/src/uix/eidos/components/time-range-picker/time-range-picker-day-period-toggle.svelte b/src/uix/eidos/components/time-range-picker/time-range-picker-day-period-toggle.svelte
new file mode 100644
index 000000000..11ea3a516
--- /dev/null
+++ b/src/uix/eidos/components/time-range-picker/time-range-picker-day-period-toggle.svelte
@@ -0,0 +1,10 @@
+
+
+
+ {@render children?.()}
+
diff --git a/src/uix/eidos/components/time-range-picker/time-range-picker-hour-slider.svelte b/src/uix/eidos/components/time-range-picker/time-range-picker-hour-slider.svelte
new file mode 100644
index 000000000..941730be0
--- /dev/null
+++ b/src/uix/eidos/components/time-range-picker/time-range-picker-hour-slider.svelte
@@ -0,0 +1,61 @@
+
+
+
+ {#if showRuler}
+
+ {#each tickValues as v}
+
+
+ {cycle === 12 ? v : String(v).padStart(2, '0')}
+
+ {/each}
+
+{/if}
diff --git a/src/uix/soma/components/time-range-picker/exports.ts b/src/uix/soma/components/time-range-picker/exports.ts
index 3096bd862..73862e49e 100644
--- a/src/uix/soma/components/time-range-picker/exports.ts
+++ b/src/uix/soma/components/time-range-picker/exports.ts
@@ -1,6 +1,7 @@
// Root + unique wrappers
export { default as Provider } from './components/time-range-picker.svelte';
export { default as Trigger } from './components/time-range-picker-trigger.svelte';
+export { default as Clock } from './components/time-range-picker-clock.svelte';
export { default as HourSlider } from './components/time-range-picker-hour-slider.svelte';
export { default as MinuteSlider } from './components/time-range-picker-minute-slider.svelte';
export { default as SecondSlider } from './components/time-range-picker-second-slider.svelte';
@@ -15,6 +16,12 @@ export { Label, Input, Segment } from '../time-range-field/exports';
// Slider sub-parts (thumb / range / tick)
export { Thumb, Range, Tick } from '../slider/exports';
+// Provider class + attrs map (consumed by per-endpoint slider wrappers).
+export {
+ TimeRangePickerProvider,
+ timeRangePickerAttrs
+} from './time-range-picker-provider.svelte';
+
export type {
TimeRangePickerProviderProps as ProviderProps,
TimeRangePickerTriggerProps as TriggerProps,
diff --git a/src/uix/soma/components/time-range-picker/time-range-picker-provider.svelte.test.ts b/src/uix/soma/components/time-range-picker/time-range-picker-provider.svelte.test.ts
index 69d6e7596..b70b996a2 100644
--- a/src/uix/soma/components/time-range-picker/time-range-picker-provider.svelte.test.ts
+++ b/src/uix/soma/components/time-range-picker/time-range-picker-provider.svelte.test.ts
@@ -73,6 +73,7 @@ function createTimeRangePickerOpts() {
endReadonlySegments: state(['dayPeriod']),
required: state(false),
closeOnRangeComplete: state(true),
+ mode: state<'inline' | 'modal'>('inline'),
granularity: state(undefined),
hideTimeZone: state(false),
hourCycle: state(undefined),
diff --git a/src/uix/soma/components/time-range-picker/time-range-picker-provider.svelte.ts b/src/uix/soma/components/time-range-picker/time-range-picker-provider.svelte.ts
index 89c81f84d..344a732e9 100644
--- a/src/uix/soma/components/time-range-picker/time-range-picker-provider.svelte.ts
+++ b/src/uix/soma/components/time-range-picker/time-range-picker-provider.svelte.ts
@@ -1,5 +1,8 @@
+import { untrack } from 'svelte';
+import { watch } from 'runed';
import { context, type ProviderOpts } from '../../provider';
import { createAttrs } from '$uix/morfo';
+import { boolToEmptyStrOrUndef, boolToStr, getDataOpenClosed } from '$adom';
import { type ActiveProps, type StateProps } from '$libs/reactive';
import type { OnChangeFn, Direction } from '../../types';
import { Soma } from '../../core/soma.svelte';
@@ -13,6 +16,11 @@ import type {
HourCycle
} from '$libs/days';
import type { TimeRangeEndpoint } from './types';
+import {
+ pickerShellContext,
+ type PickerShellHandle,
+ type PickerShellMode
+} from '../picker-shell';
import { timeRangePickerMorfo } from '../../../morfo/components/time-range-picker';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
@@ -41,6 +49,7 @@ export interface TimeRangePickerOpts
endReadonlySegments: EditableTimeSegmentPart[];
required: boolean;
closeOnRangeComplete: boolean;
+ mode: PickerShellMode;
granularity: TimeGranularity | undefined;
hideTimeZone: boolean;
hourCycle: HourCycle | undefined;
@@ -58,6 +67,10 @@ export interface TimeRangePickerOpts
* a `PopoverProvider` and a `TimeRangeFieldProvider` tied to the same state;
* per-endpoint slider wrappers read from this provider to drive start/end
* independently.
+ *
+ * Picker-shell contract (mode + commit/cancel/clear + snapshot) follows the
+ * same shape as date-picker / time-picker / color-picker so the shared
+ * `` parts work out of the box.
*/
export class TimeRangePickerProvider {
readonly opts: TimeRangePickerOpts;
@@ -75,18 +88,53 @@ export class TimeRangePickerProvider {
return new TimeRangePickerProvider(opts);
}
+ /** Snapshot of both endpoints captured on the OPEN edge for `cancel()`. */
+ private valueOnOpen: { start: TimeValue | undefined; end: TimeValue | undefined } | undefined =
+ undefined;
+
private constructor(opts: TimeRangePickerOpts) {
this.opts = opts;
this.soma = Soma.require();
- this.runtime = this.soma.runtime(timeRangePickerMorfo, {});
+ this.runtime = this.soma.runtime(timeRangePickerMorfo, {
+ states: { open: () => this.opts.open.current },
+ props: {
+ disabled: () => this.opts.disabled.current,
+ readonly: () => this.opts.readonly.current,
+ required: () => this.opts.required.current
+ }
+ });
this.runtimePart = this.runtime.part('provider', {
id: opts.id,
ref: opts.ref,
owner: this,
context: TimeRangePickerProvider.ctx
});
+
+ // Capture both endpoints on the opening edge for `cancel()` revert.
+ watch(
+ () => this.opts.open.current,
+ (now, prev) => {
+ if (now && !prev) {
+ this.valueOnOpen = untrack(() => ({
+ start: this.opts.startValue.current,
+ end: this.opts.endValue.current
+ }));
+ }
+ }
+ );
+
+ // Expose our commit/cancel/clear/mode handle to descendant Eidos
+ // PickerShell parts.
+ pickerShellContext.set(this.pickerShellHandle);
}
+ readonly pickerShellHandle: PickerShellHandle = {
+ getMode: () => this.opts.mode.current,
+ commit: () => this.commit(),
+ cancel: () => this.cancel(),
+ clear: () => this.clear()
+ };
+
/** Resolved hour cycle — same rule as TimeField/TimePicker. */
readonly resolvedHourCycle: HourCycle = $derived.by(() => {
const explicit = this.opts.hourCycle.current;
@@ -188,6 +236,44 @@ export class TimeRangePickerProvider {
this.opts.open.current = false;
}
}
+
+ // ── PickerShell handle (mode + commit/cancel/clear) ────────────────────
+
+ /** Save current range and close the popover. */
+ commit(): void {
+ this.opts.open.current = false;
+ }
+
+ /** Discard mid-session edits (revert to OPEN-edge snapshot) and close. */
+ cancel(): void {
+ const snapshot = this.valueOnOpen;
+ if (snapshot) {
+ this.opts.startValue.current = snapshot.start;
+ this.opts.endValue.current = snapshot.end;
+ }
+ this.opts.open.current = false;
+ }
+
+ /** Reset both endpoints to undefined without closing. */
+ clear(): void {
+ this.opts.startValue.current = undefined;
+ this.opts.endValue.current = undefined;
+ }
+
+ // ── Root props (emitted onto `[data-time-range-picker]`) ───────────────
+
+ readonly props = $derived.by(() =>
+ this.runtimePart.assert({
+ ...this.runtimePart.props,
+ 'data-state': getDataOpenClosed(this.opts.open.current),
+ 'data-disabled': boolToEmptyStrOrUndef(this.opts.disabled.current),
+ 'data-readonly': boolToEmptyStrOrUndef(this.opts.readonly.current),
+ 'data-required': boolToEmptyStrOrUndef(this.opts.required.current),
+ 'aria-disabled': boolToStr(this.opts.disabled.current),
+ 'aria-readonly': boolToStr(this.opts.readonly.current),
+ 'aria-required': boolToStr(this.opts.required.current)
+ } as const)
+ );
}
export { attrs as timeRangePickerAttrs };
diff --git a/src/uix/soma/components/time-range-picker/types.ts b/src/uix/soma/components/time-range-picker/types.ts
index 787679b8e..9ae6a3084 100644
--- a/src/uix/soma/components/time-range-picker/types.ts
+++ b/src/uix/soma/components/time-range-picker/types.ts
@@ -1,4 +1,3 @@
-import type { Snippet } from 'svelte';
import type { WithChild, Without, OnChangeFn, Direction } from '../../types';
import type { PrimitiveDivAttributes, PrimitiveButtonAttributes } from '../../types';
import type {
@@ -10,12 +9,12 @@ import type {
TimeGranularity,
HourCycle
} from '$libs/days';
+import type { PickerShellMode } from '../picker-shell';
/** Which endpoint a per-endpoint sub-part drives. */
export type TimeRangeEndpoint = 'start' | 'end';
-/** Props for the root `TimeRangePicker.Provider`. */
-export type TimeRangePickerProviderProps = {
+type TimeRangePickerBaseProps = {
id?: string;
// ── Value ──
@@ -45,6 +44,13 @@ export type TimeRangePickerProviderProps = {
* @default false
*/
closeOnRangeComplete?: boolean;
+ /**
+ * Picker mode. `'inline'` closes on outside-click; `'modal'` blocks
+ * outside-click and requires a footer action (`Close` / `Cancel`) to
+ * dismiss. Same contract as date-picker / time-picker / color-picker.
+ * @default 'inline'
+ */
+ mode?: PickerShellMode;
// ── Validation ──
/** Custom range validator. */
@@ -82,9 +88,12 @@ export type TimeRangePickerProviderProps = {
// ── ARIA ──
errorMessageId?: string;
- children?: Snippet;
};
+/** Props for the root `TimeRangePicker.Provider`. */
+export type TimeRangePickerProviderProps = WithChild &
+ Without;
+
/** Props for `TimeRangePicker.Trigger`. */
export type TimeRangePickerTriggerProps = WithChild<{
id?: string;
diff --git a/web/routes/uix/+layout@.svelte b/web/routes/uix/+layout@.svelte
index 556b6b7b4..3034f0959 100644
--- a/web/routes/uix/+layout@.svelte
+++ b/web/routes/uix/+layout@.svelte
@@ -151,6 +151,7 @@
{ slug: '/uix/components/date-range-picker', label: 'Date range picker' },
{ slug: '/uix/components/time-field', label: 'Time field' },
{ slug: '/uix/components/time-picker', label: 'Time picker' },
+ { slug: '/uix/components/time-range-picker', label: 'Time range picker' },
{ slug: '/uix/components/color-field', label: 'Color field' },
{ slug: '/uix/components/color-picker', label: 'Color picker' },
{ slug: '/uix/components/stepper', label: 'Stepper' },
diff --git a/web/routes/uix/components/time-range-picker/+page.svelte b/web/routes/uix/components/time-range-picker/+page.svelte
new file mode 100644
index 000000000..80e268418
--- /dev/null
+++ b/web/routes/uix/components/time-range-picker/+page.svelte
@@ -0,0 +1,743 @@
+
+
+
+
+
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.
+
+ 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.
+
inline closes on outside-click; modal requires footer action.
+
closeOnRangeComplete
soma
false
Auto-close once both endpoints are set.
+
minValue / maxValue
soma
—
Validation bounds applied to both endpoints.
+
granularity
soma
minute
Hour / minute / second.
+
hourCycle
soma
locale
12h adds dayPeriod toggle per endpoint.
+
start / endReadonlySegments
soma
[]
Lock segments per endpoint.
+
size / variant / color
eidos
md / surface / primary
Visual-only data attrs.
+
+
+
+
+ {/if}
+
+ {#if tab === 'morfo'}
+
+
Morfo contract
+
+
+
Part
Marker
Element
Role
Optional
+
+ {#each partsList as part}
+
+
{part.kebab}
+
[{part.marker}]
+
<{part.defaultElement}>
+
{part.role ?? '—'}
+
{part.optional ? 'yes' : 'no'}
+
+ {/each}
+
+
+
+
+ {/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.
+
+
+
+
Owner
Name
Family
Verb
Sequence
Target
Play
+
+ {#each eventRows as row}
+
+
{row.owner}
+
{row.action.name}
+
{row.action.semantic.family}
+
{row.action.semantic.verb ?? '—'}
+
{row.action.semantic.sequence ?? 'pre'}
+
{row.action.target}
+
+
+
+
+ {/each}
+
+
+
+
+ {/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.
+
+
+
+
Selector
Owner
Purpose
+
+
[data-time-range-picker]
morfo
Root visual scope + size/variant/color tokens.
+
[data-time-range-picker-trigger]
morfo
Open-clock button. Has embedded variant inside the input.