import { untrack } from 'svelte' ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
import { watch } from 'runed' ;
import { context , type ProviderOpts , type WithRefOpts } from '../../provider' ;
refactor(soma): de-dup attrs in 11 providers via renderProps (batch 2)
Source role/aria/data from the morfo via renderProps() across the
date/time/calendar/picker families, removing the attrs the providers
re-declared ("morfo declares, soma executes"). Audit total 468 → 369.
Migrated: field, date-field, time-field, color-picker, date-picker,
date-range-picker, time-picker, time-range-picker, calendar, month-grid,
range-calendar.
date-field/time-field keep the `segment` part hardcoded on purpose: it is
multi-archetype (editable segments are spinbuttons, literal separators
must NOT be) so a uniform renderProps would inject role into the literals.
The residual hits the guard now pins are all legit value overrides:
composite aria-labelledby/describedby (input), the deferred segment part,
and computed aria-labels (color-picker/calendar include the value/month).
field test: required-indicator applies aria-hidden via syncAttrs, so the
attr lives on the DOM (not the props object) — assert it via getAttribute.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
import { boolToEmptyStrOrUndef , boolToStr } from '$adom' ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
import {
readableActive ,
state ,
type Active ,
type ActiveProps ,
type StateProps
remove backward-compat re-export shims
Per architectural feedback: when relocating code to its correct layer,
do the migration completely — update every consumer directly and
delete the original. No re-export barrels at the old paths.
Removed shims:
- `src/uix/soma/attrs/` — was re-exporting `createAttrs` /
`registerContract` / `assertContract` / `getContract` from
`$uix/morfo` and the bool/data-attr formatters from `$libs/dom`.
All 66 importing files updated to import from the canonical
locations directly.
- `src/uix/soma/reactive/` — `reactive.svelte.ts` was a thin
re-export of `$libs/reactive`. The 79 consumers (every soma
provider + layers + types) now import primitives directly from
`$libs/reactive`. The `opts.ts` Props↔Opts bridge moved to
`src/uix/soma/provider/opts.ts` (its natural home — provider
plumbing, not reactive primitives).
- `attachRef` / `RefAttachment` re-export from
`src/uix/soma/provider/provider.svelte.ts` and from
`src/uix/soma/index.ts`. Both removed. Consumers
(`floating.svelte.ts`, `date-field-provider.svelte.ts`) now
import directly from `$libs/reactive`.
Also:
- Moved `ActiveProps<T>` / `StateProps<T>` types to
`src/libs/reactive/types.ts` — they're trivial generic helpers
over `Active<T>` / `State<T>` and shouldn't have lived in soma.
- Stripped soma's root barrel (`src/uix/soma/index.ts`) of every
re-export of libs/morfo concerns. It now only re-exports
actually-soma surfaces (Provider base, mergeProps, css helpers,
id, types, Soma class).
Result:
- `grep -r '\$soma' src/uix/morfo` → 0 matches
- `grep -r '\$uix' src/libs` → 0 matches
- `src/uix/soma/attrs/` and `src/uix/soma/reactive/` no longer exist
- Every import statement now reflects the real architectural layer
Verified: vitest 19/20 files / 214 tests (1 pre-existing air dead-
branch fail using `bun:test`), smoke 68/68, morfo-check 66/66.
5 months ago
} from '$libs/reactive' ;
import type { SomaFocusEvent , SomaKeyboardEvent , OnChangeFn } from '../../types' ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
import { KEYS , getDirectionalKeys } from '../../keyboard' ;
import { isNumberString } from '../../types/guards' ;
refactor(uix): move useId counter from soma to $active-uix/id
The soma `id` module conflated two concerns: `createId` (wraps Svelte
`$props.id()` for SSR/ARIA element ids — ~200 soma importers, intrinsic to
the headless layer) and `useId` (a generic monotonic counter — 6 importers).
Only the counter is a cross-layer utility.
Move `useId` to a new `$active-uix/id` subpath (mirroring the existing
`$active-uix/prefs` that soma already imports), so any UIX layer — soma,
eidos, demos, apps — can mint client-only ids from a cheap, collision-free
counter without coupling to soma. `createId` stays in soma (its domain).
+ src/uix/active-uix/id/index.ts (useId)
~ soma/id now exports createId only
~ 6 useId importers repointed to $active-uix/id
(floating, toaster, date-field, time-field, accordion-item, internal/arrow)
Output format unchanged (`soma-{component}-{n}`) -> zero behavior change.
`npm run check` 0 errors; toast/accordion/popover verified in browser.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4 months ago
import { useId } from '$active-uix/id' ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
import { Soma } from '../../core/soma.svelte' ;
import {
DateFormatter ,
Time ,
convertTimeValueToDateValue ,
convertTimeValueToTime ,
isTimeBefore ,
isZonedDateTime ,
inferTimeGranularity ,
initializeTimeSegmentValues ,
createTimeSegmentContent ,
isEditableTimeSegmentPart ,
EDITABLE_TIME_SEGMENT_PARTS ,
type HourCycle ,
type TimeGranularity ,
type TimeValue ,
type TimeValidator ,
type TimeOnInvalid ,
type DateOnInvalid ,
type EditableTimeSegmentPart ,
type TimeSegmentObj ,
type TimeSegmentPart ,
type SegmentPart ,
type DayPeriod
} from '$libs/days' ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
import {
areAllTimeSegmentsFilledDOM ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
getAnnouncer ,
getFirstSegment ,
handleSegmentNavigation ,
initTimeSegmentStates ,
isAcceptableSegmentKey ,
isFirstSegment ,
isSegmentNavigationKey ,
moveToNextSegment ,
moveToPrevSegment ,
removeDescriptionElement ,
setDescription ,
type Announcer
} from '../../datetime' ;
refactor(soma): route FieldProvider through field/internals barrel + register DRP view sub-parts (audit Round 3 §3 #23 + P4 follow-up)
Two related housekeeping items in one commit:
**1. FieldProvider via barrel (audit Round 3 §3 #23)**
37 consumer files across soma imported `FieldProvider` from the deep
path `'../field/field-provider.svelte'`, mirroring the same pattern
that motivated `internals.ts` indirection on the 5 pickers earlier.
Adds `src/uix/soma/components/field/internals.ts` re-exporting
`FieldProvider` and exposes it from the barrel `exports.ts`. The 37
imports become the canonical `from '../field'`. Consistency only —
no behavioral change.
**2. DateRangePicker view sub-parts (P4 follow-up)**
The MonthView/YearView soma parts created in commit `e93c0c73` stamp
internal layout data-attrs (`-header`, `-prev`, `-heading`, `-next`,
`-grid`, `-cell`) that weren't declared in the morfo, surfacing as 12
hardcoded-data-attr violations once the contract suite caught up.
Declares the 12 sub-parts in the morfo with the canonical kebab
prefix (`month-view-*`, `year-view-*`). Each entry minimal (data: [],
aria: []) except `*-prev` / `*-next` which stamp `type='button'` and
`*-cell` which surfaces the `data-range-{start,end,in-range}`
classification states used by the eidos range tinting CSS.
Test result: 2393/2399 passing — back to the canonical 6 fails
(Words + cookie infra). The 13 transient regressions are closed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
import { FieldProvider } from '../field' ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
import { TIME_FIELD_LANGS } from './langs' ;
// ── Contract ─────────────────────────────────────────────────────────────────
morfo: port 65 components + sium.lazy + morfo-check passes 66/66
All 65 soma components now consume their morfo as the cross-layer contract.
createAttrs(morfo) and registerContract(morfo) overloads removed — the
Morfo form is now mandatory. Each component provider has a 3-line morfo
import + wire-up block instead of 20+ lines of inline declarations.
Additions:
sium/core/lazy.ts — lazy() combinator for recursive schemas (unblocks
morfo's eventual lazy part-tree validation; tests included).
sium/examples/ — booking, color, login, settings, signup, survey, tree
form demos.
Route demos at src/routes/test/sium/{booking,color,login,settings,
signup,survey,tree}.
Tooling:
morfo-check.ts — now tolerates sibling parts sharing a physical element
(Link variant of Item, etc.). Queries via allPartAttrs cross-scope.
morfo-vocabulary-check.ts — 28 WARN (legitimate per-component
vocabularies or subsets like switch missing "indeterminate"). All
reviewed, none are bugs.
Fixes caught by morfo-check:
table: data-sorted / data-pinned marked severity:'optional' (only
emitted when sorting/pinning active, not always).
tree-grid: data-expanded on Row severity:'optional' (branches only).
menubar: data-menubar-value declared on Trigger.
splitter: data-splitter-panel-id declared on Panel.
Verify:
npm run check — 0 morfo errors (4 pre-existing in sium/examples,
unrelated).
npx vitest run src/uix/morfo — 7/7 tests pass.
npm run smoke — 65/65 routes pass.
npm run morfo:check — 66/66 morfos validate against real DOM.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
import { timeFieldMorfo } from '../../../morfo/components/time-field' ;
import type { SomaRuntime , SomaRuntimePart } from '../../runtime.svelte' ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// ── Segment config ──────────────────────────────────────────────────────────
interface SegmentConfig {
min : number | ( ( provider : TimeFieldProvider ) = > number ) ;
max : number | ( ( provider : TimeFieldProvider ) = > number ) ;
cycle : number ;
padZero? : boolean ;
}
const SEGMENT_CONFIGS : Record < 'hour' | 'minute' | 'second' , SegmentConfig > = {
hour : {
min : ( p ) = > ( p . resolvedHourCycle === 12 ? 1 : 0 ) ,
max : ( p ) = > ( p . resolvedHourCycle === 24 ? 23 : 12 ) ,
cycle : 1 ,
padZero : true
} ,
minute : { min : 0 , max : 59 , cycle : 1 , padZero : true } ,
second : { min : 0 , max : 59 , cycle : 1 , padZero : true }
} ;
// ── Root provider ───────────────────────────────────────────────────────────
export interface TimeFieldOpts
extends
ProviderOpts ,
StateProps < {
value : TimeValue | undefined ;
placeholder : TimeValue ;
} > ,
ActiveProps < {
validate : TimeValidator < TimeValue > | undefined ;
onInvalid : TimeOnInvalid | undefined ;
minValue : TimeValue | undefined ;
maxValue : TimeValue | undefined ;
disabled : boolean ;
readonly : boolean ;
readonlySegments : EditableTimeSegmentPart [ ] ;
granularity : TimeGranularity | undefined ;
hideTimeZone : boolean ;
required : boolean ;
errorMessageId : string | undefined ;
locale : string ;
dir : 'ltr' | 'rtl' ;
hourCycle : HourCycle | undefined ;
} > { }
export class TimeFieldProvider {
readonly opts : TimeFieldOpts ;
readonly runtimePart : SomaRuntimePart ;
readonly runtime : SomaRuntime ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
static readonly ctx = context < TimeFieldProvider > ( 'TimeField' ) ;
static get ( ) : TimeFieldProvider | undefined {
return this . ctx . getOr ( undefined ) as TimeFieldProvider | undefined ;
}
static require ( ) : TimeFieldProvider {
return this . ctx . get ( ) ;
}
static create ( opts : TimeFieldOpts ) {
return new TimeFieldProvider ( opts ) ;
}
readonly soma = Soma . require ( ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
readonly field = FieldProvider . get ( ) ;
descriptionId = useId ( 'time-field-description' ) ;
segmentValues = $state ( ) as TimeSegmentObj ;
states = initTimeSegmentStates ( ) ;
announcer : Announcer = getAnnouncer ( null ) ;
fieldNode = $state < HTMLElement | null > ( null ) ;
labelNode = $state < HTMLElement | null > ( null ) ;
dayPeriodNode = $state < HTMLElement | null > ( null ) ;
name = $state ( '' ) ;
readonly formatter = {
getLocale : ( ) = > this . effectiveLocale ,
dayPeriod : ( date : Date , hourCycle? : HourCycle ) : 'AM' | 'PM' = > {
const parts = new DateFormatter ( this . effectiveLocale , {
hour : 'numeric' ,
minute : 'numeric' ,
hourCycle : hourCycle === 24 ? 'h23' : undefined
} ) . formatToParts ( date ) ;
return parts . find ( ( p ) = > p . type === 'dayPeriod' ) ? . value === 'PM' ? 'PM' : 'AM' ;
} ,
selectedTime : ( value : TimeValue ) : string = > {
const dv = convertTimeValueToDateValue ( value ) ;
const tz = isZonedDateTime ( dv ) ? ( dv as unknown as { timeZone : string } ) . timeZone : 'UTC' ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const options : Intl.DateTimeFormatOptions = { timeStyle : 'long' } ;
return new DateFormatter ( this . effectiveLocale , options ) . format ( dv . toDate ( tz ) ) ;
}
} ;
private constructor ( opts : TimeFieldOpts ) {
this . opts = opts ;
this . runtime = this . soma . runtime ( timeFieldMorfo , {
props : {
disabled : ( ) = > this . isDisabled ,
readonly : ( ) = > this . isReadonly ,
required : ( ) = > this . isRequired ,
invalid : ( ) = > this . isInvalid
} ,
parts : {
label : ( ) = > this . labelNode ? . id || undefined
}
} ) ;
this . runtimePart = this . runtime . part ( 'provider' , {
id : opts.id ,
ref : opts.ref ,
owner : this ,
context : TimeFieldProvider.ctx ,
syncAttrs : true
} ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
this . segmentValues = initializeTimeSegmentValues ( ) ;
$effect ( ( ) = > {
this . announcer = getAnnouncer ( this . soma . dom , this . fieldNode ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} ) ;
// Populate description on value change.
$effect ( ( ) = > {
if ( this . opts . value . current ) {
const descId = untrack ( ( ) = > this . descriptionId ) ;
setDescription ( {
id : descId ,
value : this.formatter.selectedTime ( this . opts . value . current ) ,
dom : this.soma.dom ,
target : this.fieldNode
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} ) ;
}
} ) ;
// Cleanup description on unmount.
$effect ( ( ) = > {
return ( ) = > {
const descId = untrack ( ( ) = > this . descriptionId ) ;
removeDescriptionElement ( descId , this . soma . dom , this . fieldNode ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} ;
} ) ;
// Sync segmentValues ⇆ value.
if ( this . opts . value . current ) this . syncSegmentValues ( this . opts . value . current ) ;
$effect ( ( ) = > {
this . opts . locale . current ;
if ( this . opts . value . current ) this . syncSegmentValues ( this . opts . value . current ) ;
this . clearUpdating ( ) ;
} ) ;
$effect ( ( ) = > {
if ( this . opts . value . current === undefined ) {
this . segmentValues = initializeTimeSegmentValues ( ) ;
}
} ) ;
watch (
( ) = > this . validationStatus ,
( ) = > {
if ( this . validationStatus !== false ) {
this . opts . onInvalid . current ? . (
this . validationStatus . reason as never ,
this . validationStatus . message as never
) ;
}
}
) ;
// Dev warning: readonlySegments without a value has no concrete anchor.
{
let lastWarnedKey : string | null = null ;
$effect ( ( ) = > {
const segs = this . opts . readonlySegments . current ;
const hasValue = this . opts . value . current !== undefined ;
const key = segs . length > 0 && ! hasValue ? [ . . . segs ] . sort ( ) . join ( ',' ) : null ;
if ( key && key !== lastWarnedKey ) {
lastWarnedKey = key ;
this . soma ? . logger . warn (
'TimeField' ,
'`readonlySegments` is set but `value` is undefined — lock has no concrete anchor; falling back to placeholder. Supply an initial `value` so the locked segment has a defined meaning.' ,
{ context : { readonlySegments : [ . . . segs ] } }
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
) ;
} else if ( ! key ) {
lastWarnedKey = null ;
}
} ) ;
}
}
// ── Merged flags ────────────────────────────────────────────────────────
readonly isDisabled = $derived . by (
( ) = > this . opts . disabled . current || ( this . field ? . isDisabled ? ? false )
) ;
readonly isReadonly = $derived . by (
( ) = > this . opts . readonly . current || ( this . field ? . isReadonly ? ? false )
) ;
readonly isRequired = $derived . by (
( ) = > this . opts . required . current || ( this . field ? . isRequired ? ? false )
) ;
// ── Derived ─────────────────────────────────────────────────────────────
readonly effectiveLocale = $derived . by (
( ) = > this . opts . locale . current || this . soma ? . langs . getLocale ( ) || 'en'
) ;
readonly inferredGranularity = $derived . by ( ( ) = >
inferTimeGranularity ( this . opts . granularity . current )
) ;
readonly resolvedHourCycle : HourCycle = $derived . by ( ( ) = > {
const explicit = this . opts . hourCycle . current ;
if ( explicit === 12 || explicit === 24 ) return explicit ;
const fromService = this . soma ? . dates ? . getHourCycle ? . ( ) ;
if ( fromService === 12 || fromService === 24 ) return fromService ;
// Infer from locale using a 12-hour check.
const fmt = new DateFormatter ( this . effectiveLocale , { hour : 'numeric' , minute : 'numeric' } ) ;
return fmt . resolvedOptions ( ) . hour12 ? 12 : 24 ;
} ) ;
readonly readonlySegmentsSet = $derived . by ( ( ) = > new Set ( this . opts . readonlySegments . current ) ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
readonly validationStatus = $derived . by ( ( ) = > {
const value = this . opts . value . current ;
if ( ! value ) return false as const ;
const msg = this . opts . validate . current ? . ( value as never ) ;
if ( msg ) return { reason : 'custom' , message : msg } as const ;
const vTime = convertTimeValueToTime ( value ) ;
const minValue = this . opts . minValue . current ;
if ( minValue && isTimeBefore ( vTime , convertTimeValueToTime ( minValue ) ) ) {
return { reason : 'min' } as const ;
}
const maxValue = this . opts . maxValue . current ;
if ( maxValue && isTimeBefore ( convertTimeValueToTime ( maxValue ) , vTime ) ) {
return { reason : 'max' } as const ;
}
return false ;
} ) ;
readonly isInvalid = $derived . by (
( ) = > this . validationStatus !== false || ( this . field ? . isInvalid ? ? false )
) ;
readonly timeRef = $derived . by ( ( ) = >
this . opts . value . current !== undefined
? convertTimeValueToTime ( this . opts . value . current )
: convertTimeValueToTime ( this . opts . placeholder . current )
) ;
readonly allSegmentContent = $derived . by ( ( ) = >
createTimeSegmentContent ( {
segmentValues : this.segmentValues ,
timeRef : this.timeRef ,
locale : this.effectiveLocale ,
granularity : this.inferredGranularity ,
hideTimeZone : this.opts.hideTimeZone.current ,
hourCycle : this.resolvedHourCycle
} )
) ;
readonly segmentContents = $derived . by ( ( ) = > this . allSegmentContent . arr ) ;
readonly sharedSegmentAttrs = {
role : 'spinbutton' as const ,
contenteditable : 'true' as const ,
tabindex : 0 ,
spellcheck : false ,
inputmode : 'numeric' as const ,
autocorrect : 'off' as const ,
enterkeyhint : 'next' as const ,
style : 'caret-color: transparent;' ,
onbeforeinput : ( e : Event ) = > e . preventDefault ( )
} ;
// ── Label / field helpers ───────────────────────────────────────────────
getLabelledBy ( segmentId : string ) : string {
return ` ${ segmentId } ${ this . labelNode ? . id ? ? '' } ` ;
}
getLabelNode ( ) : HTMLElement | null {
return this . labelNode ;
}
getEmptyLabel ( ) : string {
return this . langs ( TIME_FIELD_LANGS . EMPTY ) ;
}
getSegmentLabel ( part : string ) : string {
switch ( part ) {
case 'hour' :
return this . langs ( TIME_FIELD_LANGS . HOUR ) ;
case 'minute' :
return this . langs ( TIME_FIELD_LANGS . MINUTE ) ;
case 'second' :
return this . langs ( TIME_FIELD_LANGS . SECOND ) ;
default :
return '' ;
}
}
getDayPeriodLabel ( ) : string {
return this . langs ( TIME_FIELD_LANGS . DAY_PERIOD ) ;
}
getTimeZoneLabel ( ) : string {
return this . langs ( TIME_FIELD_LANGS . TIME_ZONE ) ;
}
langs ( idlangref : string ) : string {
return this . soma ? . langs . ts ( idlangref ) ? ? idlangref ;
}
getFieldNode ( ) : HTMLElement | null {
return this . fieldNode ;
}
setFieldNode ( el : HTMLElement | null ) {
this . fieldNode = el ;
}
setName ( name : string ) {
this . name = name ;
}
// ── Segment updates ─────────────────────────────────────────────────────
updateSegment < P extends EditableTimeSegmentPart > (
part : P ,
updater : ( prev : TimeSegmentObj [ P ] ) = > TimeSegmentObj [ P ]
) : void {
const previousValue = this . opts . value . current ? . toString ( ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
const prev = { . . . this . segmentValues } as TimeSegmentObj ;
const prevVal = prev [ part ] ;
const nextVal = updater ( prevVal as TimeSegmentObj [ P ] ) ;
const newSegmentValues : TimeSegmentObj = { . . . prev , [ part ] : nextVal } ;
this . segmentValues = newSegmentValues ;
if ( areAllTimeSegmentsFilledDOM ( newSegmentValues , this . fieldNode ) ) {
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
// Reconstruct the TimeValue.
let time : TimeValue = this . timeRef ;
const usedSegments = (
EDITABLE_TIME_SEGMENT_PARTS as readonly EditableTimeSegmentPart [ ]
) . filter ( ( p ) = > isEditableTimeSegmentPart ( p ) ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
for ( const p of usedSegments ) {
const v = newSegmentValues [ p ] ;
if ( v === null ) continue ;
if ( p === 'dayPeriod' ) {
// Handled via hour conversion below.
continue ;
}
time = time . set ( { [ p ] : Number . parseInt ( v as string ) } as never ) ;
}
// Apply dayPeriod semantics in 12-hour mode.
if ( this . resolvedHourCycle === 12 && 'dayPeriod' in newSegmentValues ) {
const dp = newSegmentValues . dayPeriod ;
const h = ( time as unknown as { hour : number } ) . hour ;
if ( dp === 'PM' && h < 12 ) time = time . set ( { hour : h + 12 } as never ) ;
if ( dp === 'AM' && h >= 12 ) time = time . set ( { hour : h - 12 } as never ) ;
}
this . setValue ( time ) ;
} else {
this . setValue ( undefined ) ;
}
// Fire the `commit-set` sema event on value change — mirrors date-field so
// every incrementable field emits the same commit event (morfo declares it;
// soma fires it). `partRef('input')` is registered by BOTH the visible
// segment group and the hidden form input; the kebab-keyed runtime registry
// keeps the last one (the hidden input, whose ref can be null) → target
// error. Pass the visible field box explicitly so the commit ring (eidos
// `commit-settle`) lands on it.
const nextValue = this . opts . value . current ? . toString ( ) ;
if ( previousValue !== nextValue ) {
void this . runtime . trigger ( 'commit-set' , { fallbackTarget : this.fieldNode ? ? undefined } ) ;
}
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
setValue ( v : TimeValue | undefined ) : void {
this . opts . value . current = v ;
}
syncSegmentValues ( value : TimeValue ) : void {
const next : TimeSegmentObj = {
hour : this.padZero ( 'hour' , ( value as unknown as { hour : number } ) . hour ) ,
minute : this.padZero ( 'minute' , ( value as unknown as { minute : number } ) . minute ) ,
second : this.padZero ( 'second' , ( value as unknown as { second : number } ) . second ) ,
dayPeriod : this.states.dayPeriod.updating
? ( this . states . dayPeriod . updating as DayPeriod )
: this . resolvedHourCycle === 12
? this . formatter . dayPeriod (
( ( ) = > {
const dv = convertTimeValueToDateValue ( value ) ;
const tz = isZonedDateTime ( dv )
? ( dv as unknown as { timeZone : string } ) . timeZone
: 'UTC' ;
return dv . toDate ( tz ) ;
} ) ( )
)
: 'AM'
} ;
this . segmentValues = next ;
this . clearUpdating ( ) ;
}
private padZero ( part : 'hour' | 'minute' | 'second' , n : number ) : string {
let display = n ;
if ( part === 'hour' && this . resolvedHourCycle === 12 ) {
if ( display === 0 ) display = 12 ;
else if ( display > 12 ) display = display - 12 ;
}
return display < 10 ? ` 0 ${ display } ` : ` ${ display } ` ;
}
clearUpdating ( ) : void {
( Object . keys ( this . states ) as EditableTimeSegmentPart [ ] ) . forEach ( ( k ) = > {
this . states [ k ] . updating = null ;
} ) ;
}
readonly props = $derived . by ( ( ) = >
this . runtimePart . assert ( {
. . . this . runtimePart . props ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
dir : this.opts.dir.current ,
'aria-invalid' : this . isInvalid || undefined
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} as const )
) ;
}
// ── Input ───────────────────────────────────────────────────────────────────
export interface TimeFieldInputOpts
extends
WithRefOpts ,
ActiveProps < {
name : string ;
ariaLabel : string | undefined ;
ariaLabelledBy : string | undefined ;
} > { }
export class TimeFieldInputProvider {
readonly opts : TimeFieldInputOpts ;
readonly runtimePart : SomaRuntimePart ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
static create ( opts : TimeFieldInputOpts ) {
return new TimeFieldInputProvider ( opts ) ;
}
readonly provider : TimeFieldProvider ;
private constructor ( opts : TimeFieldInputOpts ) {
this . opts = opts ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
this . provider = TimeFieldProvider . require ( ) ;
this . runtimePart = this . provider . runtime . part ( 'input' , {
id : opts.id ,
ref : opts.ref ,
owner : this ,
refactor(soma): de-dup attrs in 11 providers via renderProps (batch 2)
Source role/aria/data from the morfo via renderProps() across the
date/time/calendar/picker families, removing the attrs the providers
re-declared ("morfo declares, soma executes"). Audit total 468 → 369.
Migrated: field, date-field, time-field, color-picker, date-picker,
date-range-picker, time-picker, time-range-picker, calendar, month-grid,
range-calendar.
date-field/time-field keep the `segment` part hardcoded on purpose: it is
multi-archetype (editable segments are spinbuttons, literal separators
must NOT be) so a uniform renderProps would inject role into the literals.
The residual hits the guard now pins are all legit value overrides:
composite aria-labelledby/describedby (input), the deferred segment part,
and computed aria-labels (color-picker/calendar include the value/month).
field test: required-indicator applies aria-hidden via syncAttrs, so the
attr lives on the DOM (not the props object) — assert it via getAttribute.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
onRefChange : ( v ) = > this . provider . setFieldNode ( v ) ,
// Per-part source for the morfo's `aria-label` ← `ariaLabel` prop, so
// `renderProps()` resolves it instead of the provider re-declaring it.
// (`disabled`/`readonly`/`invalid` come from the component-level sources.)
props : { ariaLabel : ( ) = > this . opts . ariaLabel . current }
} ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
this . provider . setFieldNode ( opts . ref . current ) ;
watch (
( ) = > this . opts . name . current ,
( v ) = > this . provider . setName ( v )
) ;
$effect ( ( ) = > {
const field = this . provider . field ;
if ( ! field ) return ;
field . inputId . current = opts . id . current ;
} ) ;
}
readonly segments = $derived . by ( ( ) = > this . provider . segmentContents ) ;
readonly ariaDescribedBy = $derived . by ( ( ) = > {
try {
const doc = this . provider . soma . dom . getDocument ( this . opts . ref . current ) ;
const exists = doc . getElementById ( this . provider . descriptionId ) ;
return exists ? this . provider.descriptionId : undefined ;
} catch {
return undefined ;
}
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} ) ;
readonly props = $derived . by ( ( ) = > {
const labelledBy = this . opts . ariaLabelledBy . current ? ? this . provider . labelNode ? . id ;
return this . runtimePart . assert ( {
refactor(soma): de-dup attrs in 11 providers via renderProps (batch 2)
Source role/aria/data from the morfo via renderProps() across the
date/time/calendar/picker families, removing the attrs the providers
re-declared ("morfo declares, soma executes"). Audit total 468 → 369.
Migrated: field, date-field, time-field, color-picker, date-picker,
date-range-picker, time-picker, time-range-picker, calendar, month-grid,
range-calendar.
date-field/time-field keep the `segment` part hardcoded on purpose: it is
multi-archetype (editable segments are spinbuttons, literal separators
must NOT be) so a uniform renderProps would inject role into the literals.
The residual hits the guard now pins are all legit value overrides:
composite aria-labelledby/describedby (input), the deferred segment part,
and computed aria-labels (color-picker/calendar include the value/month).
field test: required-indicator applies aria-hidden via syncAttrs, so the
attr lives on the DOM (not the props object) — assert it via getAttribute.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
// Morfo-declared attrs (role=group, aria-label, aria-disabled/readonly,
// aria-invalid, aria-labelledby from the Label part), resolved against
// this part's registered sources — no longer re-declared here.
. . . this . runtimePart . renderProps ( ) ,
// Soma-only: aria-labelledby honours an explicit `ariaLabelledBy` prop
// (the morfo only knows the Label part); aria-describedby is the computed
// hidden-description element id (not the morfo's prop); data-invalid/
// data-disabled are morfo-declared WITHOUT a value.
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
'aria-labelledby' : labelledBy ,
'aria-describedby' : this . ariaDescribedBy ,
'data-invalid' : boolToEmptyStrOrUndef ( this . provider . isInvalid ) ,
'data-disabled' : boolToEmptyStrOrUndef ( this . provider . isDisabled )
} as const ) ;
} ) ;
}
// ── Label ───────────────────────────────────────────────────────────────────
export interface TimeFieldLabelOpts extends WithRefOpts { }
export class TimeFieldLabelProvider {
readonly opts : TimeFieldLabelOpts ;
readonly runtimePart : SomaRuntimePart ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
static create ( opts : TimeFieldLabelOpts ) {
return new TimeFieldLabelProvider ( opts ) ;
}
readonly provider : TimeFieldProvider ;
private constructor ( opts : TimeFieldLabelOpts ) {
this . opts = opts ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
this . provider = TimeFieldProvider . require ( ) ;
this . runtimePart = this . provider . runtime . part ( 'label' , {
id : opts.id ,
ref : opts.ref ,
owner : this ,
onRefChange : ( v ) = > {
this . provider . labelNode = v ;
} ,
syncAttrs : true
} ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
readonly onclick = ( ) = > {
const first = getFirstSegment ( this . provider . getFieldNode ( ) ) ;
this . provider . soma . dom . focus ( first ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
} ;
readonly props = $derived . by ( ( ) = >
this . runtimePart . assert ( {
. . . this . runtimePart . props ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
onclick : this.onclick
} as const )
) ;
}
// ── Hidden input ────────────────────────────────────────────────────────────
export interface TimeFieldHiddenInputOpts extends ProviderOpts { }
export class TimeFieldHiddenInputProvider {
readonly opts : TimeFieldHiddenInputOpts ;
readonly runtimePart : SomaRuntimePart ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
static create ( opts : TimeFieldHiddenInputOpts ) {
return new TimeFieldHiddenInputProvider ( opts ) ;
}
readonly provider : TimeFieldProvider ;
private constructor ( opts : TimeFieldHiddenInputOpts ) {
this . opts = opts ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
this . provider = TimeFieldProvider . require ( ) ;
this . runtimePart = this . provider . runtime . part ( 'input' , {
id : opts.id ,
ref : opts.ref ,
owner : this
} ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
readonly props = $derived . by ( ( ) = > {
const value = this . provider . opts . value . current ;
// ISO time string (HH:MM:SS[.sss][+TZ]).
const isoValue = value ? value . toString ( ) : '' ;
return {
type : 'hidden' as const ,
name : this.provider.name ,
value : isoValue ,
required : this.provider.isRequired || undefined ,
'aria-hidden' : 'true' as const ,
tabindex : - 1
} ;
} ) ;
}
// ── Base segment (hour / minute / second) ───────────────────────────────────
export abstract class BaseTimeNumericSegmentProvider {
readonly opts : WithRefOpts ;
readonly runtimePart : SomaRuntimePart ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
readonly provider : TimeFieldProvider ;
protected readonly part : 'hour' | 'minute' | 'second' ;
protected readonly config : SegmentConfig ;
protected constructor (
opts : WithRefOpts ,
part : 'hour' | 'minute' | 'second' ,
config : SegmentConfig
) {
this . opts = opts ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
this . part = part ;
this . config = config ;
this . provider = TimeFieldProvider . require ( ) ;
this . runtimePart = this . provider . runtime . part ( 'segment' , {
id : opts.id ,
ref : opts.ref ,
owner : this
} ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
protected get announcer ( ) : Announcer {
return this . provider . announcer ;
}
protected getMin ( ) : number {
return typeof this . config . min === 'function' ? this . config . min ( this . provider ) : this . config . min ;
}
protected getMax ( ) : number {
return typeof this . config . max === 'function' ? this . config . max ( this . provider ) : this . config . max ;
}
protected formatValue ( n : number ) : string {
return this . config . padZero && n < 10 ? ` 0 ${ n } ` : ` ${ n } ` ;
}
protected get directionalKeys() {
return getDirectionalKeys ( this . provider . opts . dir . current , 'horizontal' ) ;
}
handleArrowUp ( ) : void {
const stateKey = this . part as EditableTimeSegmentPart ;
this . provider . states [ stateKey ] . hasLeftFocus = false ;
this . provider . updateSegment ( this . part , ( prev ) = > {
if ( prev === null ) {
const min = this . getMin ( ) ;
this . announcer . announce ( min ) ;
return this . formatValue ( min ) ;
}
const n = Number . parseInt ( prev as string ) ;
const next = n >= this . getMax ( ) ? this . getMin ( ) : n + this . config . cycle ;
this . announcer . announce ( next ) ;
return this . formatValue ( next ) ;
} ) ;
}
handleArrowDown ( ) : void {
const stateKey = this . part as EditableTimeSegmentPart ;
this . provider . states [ stateKey ] . hasLeftFocus = false ;
this . provider . updateSegment ( this . part , ( prev ) = > {
if ( prev === null ) {
const max = this . getMax ( ) ;
this . announcer . announce ( max ) ;
return this . formatValue ( max ) ;
}
const n = Number . parseInt ( prev as string ) ;
const next = n <= this . getMin ( ) ? this . getMax ( ) : n - this . config . cycle ;
this . announcer . announce ( next ) ;
return this . formatValue ( next ) ;
} ) ;
}
handleNumberKey ( e : SomaKeyboardEvent ) : void {
const stateKey = this . part as EditableTimeSegmentPart ;
const num = Number . parseInt ( e . key ) ;
const max = this . getMax ( ) ;
const maxStart = Math . floor ( max / 10 ) ;
this . provider . updateSegment ( this . part , ( prev ) = > {
if ( this . provider . states [ stateKey ] . hasLeftFocus ) {
prev = null ;
this . provider . states [ stateKey ] . hasLeftFocus = false ;
}
if ( prev === null ) {
if ( num === 0 && ! this . config . padZero ) {
return null ;
}
// First digit — if it would exceed max, treat as complete.
if ( num > maxStart ) {
this . announcer . announce ( num ) ;
moveToNextSegment (
this . provider . soma . dom ,
e as unknown as KeyboardEvent ,
this . provider . getFieldNode ( )
) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
return this . formatValue ( num ) ;
}
return ` ${ num } ` ;
}
const prevN = Number . parseInt ( prev as string ) ;
const candidate = prevN * 10 + num ;
if ( candidate <= max ) {
this . announcer . announce ( candidate ) ;
moveToNextSegment (
this . provider . soma . dom ,
e as unknown as KeyboardEvent ,
this . provider . getFieldNode ( )
) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
return this . formatValue ( candidate ) ;
}
// Replace instead of append.
this . announcer . announce ( num ) ;
if ( num > maxStart ) {
moveToNextSegment (
this . provider . soma . dom ,
e as unknown as KeyboardEvent ,
this . provider . getFieldNode ( )
) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
return ` ${ num } ` ;
} ) ;
}
handleBackspace ( e : SomaKeyboardEvent ) : void {
const stateKey = this . part as EditableTimeSegmentPart ;
this . provider . states [ stateKey ] . hasLeftFocus = false ;
let moveToPrev = false ;
this . provider . updateSegment ( this . part , ( prev ) = > {
if ( prev === null ) {
moveToPrev = true ;
this . announcer . announce ( null ) ;
return null ;
}
const str = ( prev as string ) . toString ( ) ;
if ( str . length === 1 ) {
this . announcer . announce ( null ) ;
return null ;
}
const next = Number . parseInt ( str . slice ( 0 , - 1 ) ) ;
this . announcer . announce ( next ) ;
return ` ${ next } ` ;
} ) ;
if ( moveToPrev )
moveToPrevSegment (
this . provider . soma . dom ,
e as unknown as KeyboardEvent ,
this . provider . getFieldNode ( )
) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
protected handleKeydown ( e : SomaKeyboardEvent ) : void {
if ( e . ctrlKey || e . metaKey || this . provider . isDisabled ) return ;
if ( e . key !== KEYS . TAB ) e . preventDefault ( ) ;
if ( ! isAcceptableSegmentKey ( e . key ) ) return ;
if ( this . provider . readonlySegmentsSet . has ( this . part ) ) return ;
if ( e . key === KEYS . ARROW_UP ) {
this . handleArrowUp ( ) ;
return ;
}
if ( e . key === KEYS . ARROW_DOWN ) {
this . handleArrowDown ( ) ;
return ;
}
if ( isNumberString ( e . key ) ) {
this . handleNumberKey ( e ) ;
return ;
}
if ( e . key === KEYS . BACKSPACE ) {
this . handleBackspace ( e ) ;
return ;
}
if ( isSegmentNavigationKey ( e . key ) ) {
const { nextKey , prevKey } = this . directionalKeys ;
handleSegmentNavigation (
this . provider . soma . dom ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
e as unknown as KeyboardEvent ,
this . provider . getFieldNode ( ) ,
nextKey ,
prevKey
) ;
}
}
onkeydown = ( e : SomaKeyboardEvent ) = > this . handleKeydown ( e ) ;
onfocusout = ( _ : SomaFocusEvent ) = > {
const stateKey = this . part as EditableTimeSegmentPart ;
this . provider . states [ stateKey ] . hasLeftFocus = true ;
if ( this . config . padZero ) {
this . provider . updateSegment ( this . part , ( prev ) = > {
if ( prev && ( prev as string ) . length === 1 ) return ` 0 ${ prev } ` ;
return prev ;
} ) ;
}
} ;
getSegmentProps() {
const value = this . provider . segmentValues [ this . part ] ;
const isEmpty = value === null || value === undefined ;
const valueNow = isEmpty ? undefined : Number . parseInt ( value as string ) ;
const valueMin = this . getMin ( ) ;
const valueMax = this . getMax ( ) ;
let valueText = isEmpty ? this . provider . getEmptyLabel ( ) : ` ${ valueNow } ` ;
if ( this . part === 'hour' && this . provider . resolvedHourCycle === 12 ) {
const dp = ( this . provider . segmentValues as TimeSegmentObj ) . dayPeriod ;
valueText = isEmpty ? this . provider . getEmptyLabel ( ) : ` ${ valueNow } ${ dp ? ? 'AM' } ` ;
}
return {
'aria-label' : ` ${ this . provider . getSegmentLabel ( this . part ) } , ` ,
'aria-valuemin' : valueMin ,
'aria-valuemax' : valueMax ,
'aria-valuenow' : valueNow ,
'aria-valuetext' : valueText
} ;
}
readonly props = $derived . by ( ( ) = > {
const p = this . getSegmentProps ( ) ;
const isReadonly = this . provider . isReadonly || this . provider . readonlySegmentsSet . has ( this . part ) ;
return this . runtimePart . assert ( {
refactor(soma): de-dup attrs in 11 providers via renderProps (batch 2)
Source role/aria/data from the morfo via renderProps() across the
date/time/calendar/picker families, removing the attrs the providers
re-declared ("morfo declares, soma executes"). Audit total 468 → 369.
Migrated: field, date-field, time-field, color-picker, date-picker,
date-range-picker, time-picker, time-range-picker, calendar, month-grid,
range-calendar.
date-field/time-field keep the `segment` part hardcoded on purpose: it is
multi-archetype (editable segments are spinbuttons, literal separators
must NOT be) so a uniform renderProps would inject role into the literals.
The residual hits the guard now pins are all legit value overrides:
composite aria-labelledby/describedby (input), the deferred segment part,
and computed aria-labels (color-picker/calendar include the value/month).
field test: required-indicator applies aria-hidden via syncAttrs, so the
attr lives on the DOM (not the props object) — assert it via getAttribute.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
// Morfo-declared attrs (role=spinbutton, aria-disabled, aria-invalid),
// resolved against the component-level sources — no longer re-declared.
. . . this . runtimePart . renderProps ( ) ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
. . . this . provider . sharedSegmentAttrs ,
refactor(soma): de-dup attrs in 11 providers via renderProps (batch 2)
Source role/aria/data from the morfo via renderProps() across the
date/time/calendar/picker families, removing the attrs the providers
re-declared ("morfo declares, soma executes"). Audit total 468 → 369.
Migrated: field, date-field, time-field, color-picker, date-picker,
date-range-picker, time-picker, time-range-picker, calendar, month-grid,
range-calendar.
date-field/time-field keep the `segment` part hardcoded on purpose: it is
multi-archetype (editable segments are spinbuttons, literal separators
must NOT be) so a uniform renderProps would inject role into the literals.
The residual hits the guard now pins are all legit value overrides:
composite aria-labelledby/describedby (input), the deferred segment part,
and computed aria-labels (color-picker/calendar include the value/month).
field test: required-indicator applies aria-hidden via syncAttrs, so the
attr lives on the DOM (not the props object) — assert it via getAttribute.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4 months ago
// Soma-only: data-* are morfo-declared WITHOUT a value; the value-aria
// (`...p`), per-segment aria-readonly and aria-labelledby are computed
// here and override the morfo's raw declaration.
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
'data-segment' : this . part ,
'data-placeholder' : boolToEmptyStrOrUndef ( this . provider . segmentValues [ this . part ] === null ) ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
'data-invalid' : boolToEmptyStrOrUndef ( this . provider . isInvalid ) ,
'data-disabled' : boolToEmptyStrOrUndef ( this . provider . isDisabled ) ,
'data-readonly' : boolToEmptyStrOrUndef ( isReadonly ) ,
'aria-readonly' : boolToStr ( isReadonly ) ,
. . . p ,
'aria-labelledby' : this . provider . getLabelledBy ( this . opts . id . current ) ,
onkeydown : this.onkeydown ,
onfocusout : this.onfocusout
} as const ) ;
} ) ;
}
class TimeFieldHourSegmentProvider extends BaseTimeNumericSegmentProvider {
constructor ( opts : WithRefOpts ) {
super ( opts , 'hour' , SEGMENT_CONFIGS . hour ) ;
}
}
class TimeFieldMinuteSegmentProvider extends BaseTimeNumericSegmentProvider {
constructor ( opts : WithRefOpts ) {
super ( opts , 'minute' , SEGMENT_CONFIGS . minute ) ;
}
}
class TimeFieldSecondSegmentProvider extends BaseTimeNumericSegmentProvider {
constructor ( opts : WithRefOpts ) {
super ( opts , 'second' , SEGMENT_CONFIGS . second ) ;
}
}
// ── DayPeriod segment ───────────────────────────────────────────────────────
export class TimeFieldDayPeriodSegmentProvider {
readonly opts : WithRefOpts ;
readonly runtimePart : SomaRuntimePart ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
static create ( opts : WithRefOpts ) {
return new TimeFieldDayPeriodSegmentProvider ( opts ) ;
}
readonly provider : TimeFieldProvider ;
private constructor ( opts : WithRefOpts ) {
this . opts = opts ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
this . provider = TimeFieldProvider . require ( ) ;
this . runtimePart = this . provider . runtime . part ( 'segment' , {
id : opts.id ,
ref : opts.ref ,
owner : this ,
onRefChange : ( v ) = > {
this . provider . dayPeriodNode = v ;
}
} ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
onkeydown = ( e : SomaKeyboardEvent ) = > {
if ( e . ctrlKey || e . metaKey || this . provider . isDisabled ) return ;
if ( this . provider . readonlySegmentsSet . has ( 'dayPeriod' ) ) return ;
if ( e . key !== KEYS . TAB ) e . preventDefault ( ) ;
const flip = ( ) = > {
this . provider . updateSegment ( 'dayPeriod' , ( prev ) = > {
const next : DayPeriod = prev === 'PM' ? 'AM' : 'PM' ;
this . provider . announcer . announce ( next ) ;
return next ;
} ) ;
} ;
if (
e . key === KEYS . ARROW_UP ||
e . key === KEYS . ARROW_DOWN ||
e . key === KEYS . BACKSPACE ||
e . key === KEYS . SPACE
) {
flip ( ) ;
return ;
}
if ( e . key === 'a' || e . key === 'A' ) {
this . provider . updateSegment ( 'dayPeriod' , ( ) = > {
this . provider . announcer . announce ( 'AM' ) ;
return 'AM' ;
} ) ;
return ;
}
if ( e . key === 'p' || e . key === 'P' ) {
this . provider . updateSegment ( 'dayPeriod' , ( ) = > {
this . provider . announcer . announce ( 'PM' ) ;
return 'PM' ;
} ) ;
return ;
}
if ( isSegmentNavigationKey ( e . key ) ) {
const { nextKey , prevKey } = getDirectionalKeys ( this . provider . opts . dir . current , 'horizontal' ) ;
handleSegmentNavigation (
this . provider . soma . dom ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
e as unknown as KeyboardEvent ,
this . provider . getFieldNode ( ) ,
nextKey ,
prevKey
) ;
}
} ;
readonly props = $derived . by ( ( ) = > {
const value = ( this . provider . segmentValues as TimeSegmentObj ) . dayPeriod ;
const isReadonly =
this . provider . isReadonly || this . provider . readonlySegmentsSet . has ( 'dayPeriod' ) ;
return this . runtimePart . assert ( {
. . . this . runtimePart . props ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
. . . this . provider . sharedSegmentAttrs ,
'data-segment' : 'dayPeriod' as const ,
'data-invalid' : boolToEmptyStrOrUndef ( this . provider . isInvalid ) ,
'data-disabled' : boolToEmptyStrOrUndef ( this . provider . isDisabled ) ,
'data-readonly' : boolToEmptyStrOrUndef ( isReadonly ) ,
'aria-label' : this . provider . getDayPeriodLabel ( ) ,
'aria-valuemin' : 0 ,
'aria-valuemax' : 12 ,
'aria-valuenow' : value === 'AM' ? 0 : 12 ,
'aria-valuetext' : value ? ? 'AM' ,
'aria-labelledby' : this . provider . getLabelledBy ( this . opts . id . current ) ,
onkeydown : this.onkeydown
} as const ) ;
} ) ;
}
// ── Literal segment ─────────────────────────────────────────────────────────
export class TimeFieldLiteralSegmentProvider {
readonly opts : WithRefOpts ;
readonly runtimePart : SomaRuntimePart ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
static create ( opts : WithRefOpts ) {
return new TimeFieldLiteralSegmentProvider ( opts ) ;
}
private constructor ( opts : WithRefOpts ) {
this . opts = opts ;
const provider = TimeFieldProvider . require ( ) ;
this . runtimePart = provider . runtime . part ( 'segment' , {
id : opts.id ,
ref : opts.ref ,
owner : this
} ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
readonly props = $derived . by ( ( ) = >
this . runtimePart . assert ( {
. . . this . runtimePart . props ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
'aria-hidden' : 'true' as const ,
'data-segment' : 'literal' as const
} as const )
) ;
}
// ── TimeZone segment (read-only) ────────────────────────────────────────────
export class TimeFieldTimeZoneSegmentProvider {
readonly opts : WithRefOpts ;
readonly runtimePart : SomaRuntimePart ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
static create ( opts : WithRefOpts ) {
return new TimeFieldTimeZoneSegmentProvider ( opts ) ;
}
readonly provider : TimeFieldProvider ;
private constructor ( opts : WithRefOpts ) {
this . opts = opts ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
this . provider = TimeFieldProvider . require ( ) ;
this . runtimePart = this . provider . runtime . part ( 'segment' , {
id : opts.id ,
ref : opts.ref ,
owner : this
} ) ;
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
}
readonly props = $derived . by ( ( ) = >
this . runtimePart . assert ( {
. . . this . runtimePart . props ,
soma: tier 3 dates + tier 4 time (partial), dias/segments, docs
Tier 3 (complete):
DateField, DatePicker, DateRangeField, DateRangePicker
Tier 4 (partial):
TimeField, TimePicker (TimeRangeField pending)
Core:
$lib/util/dias/segments.ts — pure domain primitives for segmented
date/time inputs (constants, guards, initializers, reconciliation,
content generation, Intl opts)
soma/external/dates re-exports everything (no façades inside soma)
soma/datetime/ slimmed to UI-only helpers (announcer, DOM segment nav,
SegmentState, KEYS predicates, description writers)
Conventions established (A23–A29):
A23 — never re-implement date helpers; extend dias
A24 — readonlySegments without value anchor logs warning
A25 — range components split readonly per-endpoint
A26 — contenteditable segments use onbeforeinput to block IME/paste
A27 — picker composition pattern with shared writableActive refs
A28 — time placeholders render as hh/mm/ss via dias/segments
A29 — demo pages are interactive testbeds, not static galleries
Demo pages: Material Design 3 styled, every public prop wired to a live
control, Field integration + state readout. Day-period uses real radio
group in 12h mode. Auto-anchor on range pickers keeps the end month
visible. 12h sliders map 0↔12 / 13-23↔1-11 correctly.
Docs: COMPONENT_GUIDE.md + SOMA_ARCHITECTURE.md updated with A23–A29,
checklist items 28–31, picker composition section, dias boundary,
directory structure note for soma/datetime/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 months ago
'aria-label' : this . provider . getTimeZoneLabel ( ) ,
'data-segment' : 'timeZoneName' as const ,
'data-invalid' : boolToEmptyStrOrUndef ( this . provider . isInvalid ) ,
'data-readonly' : '' as const ,
'aria-readonly' : 'true' as const ,
tabindex : - 1
} as const )
) ;
}
// ── Dispatcher ──────────────────────────────────────────────────────────────
export function createTimeSegmentProvider ( part : SegmentPart , opts : WithRefOpts ) {
switch ( part ) {
case 'hour' :
return new TimeFieldHourSegmentProvider ( opts ) ;
case 'minute' :
return new TimeFieldMinuteSegmentProvider ( opts ) ;
case 'second' :
return new TimeFieldSecondSegmentProvider ( opts ) ;
case 'dayPeriod' :
return TimeFieldDayPeriodSegmentProvider . create ( opts ) ;
case 'timeZoneName' :
return TimeFieldTimeZoneSegmentProvider . create ( opts ) ;
case 'literal' :
default :
return TimeFieldLiteralSegmentProvider . create ( opts ) ;
}
}