diff --git a/src/uix/contracts.test.ts b/src/uix/contracts.test.ts index 47f6a66c1..4d9a5cb7d 100644 --- a/src/uix/contracts.test.ts +++ b/src/uix/contracts.test.ts @@ -306,6 +306,14 @@ describe('UIX layer contracts', () => { expect(missingExports).toEqual([]); }); + it('guards Soma public component modules with local README docs', () => { + const missingReadmes = collectPublicSomaComponentDirs().filter( + (dir) => !existsSync(join(HERE, 'soma', 'components', dir, 'README.md')) + ); + + expect(missingReadmes).toEqual([]); + }); + it('guards Soma from direct mutable DOM writes', () => { const violations = grepSources( join(HERE, 'soma'), diff --git a/src/uix/soma/components/time-picker/README.md b/src/uix/soma/components/time-picker/README.md new file mode 100644 index 000000000..ff640344f --- /dev/null +++ b/src/uix/soma/components/time-picker/README.md @@ -0,0 +1,104 @@ +# TimePicker + +`TimeField` + `Popover` with slider controls for hour / minute / second and an optional AM/PM toggle. The segmented input and the popover controls share the same provider state, so keyboard editing and pointer dragging stay in sync. + +## Anatomy + +```svelte + + Start time + + {#snippet children({ segments })} + {#each segments as { part, value } (part + value)} + {value} + {/each} + {/snippet} + + + Open + + + + + + + + + + + + + + + + + +``` + +## Parts + +| Part | Source | Description | +| ----------------- | ---------- | --------------------------------------------------------------- | +| `Provider` | picker | Shared value, placeholder, open state and composed providers. | +| `Trigger` | picker | Popover trigger button. | +| `HourSlider` | picker | Single-value slider for the hour segment. | +| `MinuteSlider` | picker | Single-value slider for the minute segment. | +| `SecondSlider` | picker | Single-value slider for the second segment. | +| `DayPeriodToggle` | picker | AM/PM radio group, rendered only when the resolved cycle is 12. | +| `Label` | TimeField | Re-export. Labels the segmented input. | +| `Input` | TimeField | Re-export. Emits the editable time segments. | +| `Segment` | TimeField | Re-export. Individual editable segment. | +| `HiddenInput` | TimeField | Re-export. Form value when `name` is set. | +| `Content` | Popover | Re-export. Floating surface for picker controls. | +| `Arrow` | Popover | Re-export. | +| `Close` | Popover | Re-export. | +| `Overlay` | Popover | Re-export. | +| `Anchor` | Popover | Re-export. | +| `Thumb` | Slider | Re-export. Used inside picker sliders. | +| `Range` | Slider | Re-export. Used inside picker sliders. | +| `Tick` | Slider | Re-export. Optional tick marks. | + +## ARIA + +- `Trigger` inherits Popover trigger semantics and adds the component-owned translated label when no `aria-label` is supplied. +- Sliders emit `role="slider"` through the composed Slider provider, with localized thumb labels for hour / minute / second. +- `DayPeriodToggle` emits `role="radiogroup"` and disables itself when the picker or the `dayPeriod` segment is readonly. +- Input and Segment inherit TimeField ARIA, validation, and hidden-description behavior. + +## Data Attributes + +| Part | Attribute | Values | +| ----------------- | --------------------------------- | ---------------------------- | +| Trigger | `data-time-picker-trigger` | Always present | +| Trigger | `data-state` | `open` \| `closed` | +| DayPeriodToggle | `data-time-picker-day-period-toggle` | Always present | +| DayPeriodItem | `data-time-picker-day-period-item` | Always present | +| DayPeriodItem | `data-checked` | Present for the active item | +| Content / Arrow / Close / Overlay / Anchor | + all `data-popover-*` | Inherited from Popover | +| Input / Segment / HiddenInput | + all `data-time-field-*` | Inherited from TimeField | +| HourSlider / MinuteSlider / SecondSlider | + all `data-slider-*` | Inherited from Slider | + +## Props + +See `types.ts` for the full typed surface. Summary: + +| Prop | Type | Notes | +| ---------------------------------------- | -------------------------------- | --------------------------------------------------- | +| `value` | `TimeValue \| undefined` | Bindable. | +| `placeholder` | `TimeValue` | Bindable; defaults to the current clock time. | +| `open` | `boolean` | Bindable popover state. | +| `onOpenChangeComplete` | `(open) => void` | Called after open-state completion. | +| `validate` / `onInvalid` | time validation callbacks | Same validation model as TimeField. | +| `minValue` / `maxValue` | `TimeValue` | Bounds for valid values. | +| `disabled` / `readonly` / `required` | `boolean` | Forwarded to the composed TimeField and sliders. | +| `readonlySegments` | `EditableTimeSegmentPart[]` | Locks individual segments, including `dayPeriod`. | +| `granularity` | `'hour' \| 'minute' \| 'second'` | Controls the field segment set. | +| `hideTimeZone` | `boolean` | For ZonedDateTime placeholders. | +| `hourCycle` | `12 \| 24` | Explicit cycle; otherwise resolved from Soma/locale.| +| `hourStep` / `minuteStep` / `secondStep` | `number` | Slider step sizes. Default `1`. | +| `locale` / `dir` | `string` / `'ltr' \| 'rtl'` | Override Soma defaults. | +| `errorMessageId` | `string` | External error id for `aria-describedby`. | + +## Notes + +`TimePicker` is a composed component. Popover, TimeField and Slider own their own runtime parts; the picker only owns the coordinating provider, trigger and time-specific controls.