From 243122393d41a4331555f0a0b1372b1df681b3e0 Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 26 Aug 2026 04:51:32 +0200 Subject: [PATCH] =?UTF-8?q?refactor(soma)!:=20fase=20C2b=20=E2=80=94=20ren?= =?UTF-8?q?derProps()=20muere;=20.props=20es=20la=20=C3=BAnica=20bolsa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tras C2a las dos computaban lo mismo. Sin shim, por doctrina: los 262 call sites en 62 ficheros migran a `.props`, el método y su tipo desaparecen del runtime, y las ~200 menciones en comentarios (la plantilla «lets renderProps() emit…» de los morfos incluida) pasan a nombrar la bolsa de render, para no dejar deriva doc↔código en el propio código. Verificación: svelte-check con src/ a 0 (71 errores = exactamente el ledger de web/) · suites de soma tocadas en verde. Co-Authored-By: Claude Fable 5 --- .../eidos/components/media-player/README.md | 2 +- src/uix/morfo/components/calendar.ts | 8 +- src/uix/morfo/components/date-field.ts | 2 +- src/uix/morfo/components/date-range-picker.ts | 2 +- src/uix/morfo/components/file-upload.ts | 4 +- src/uix/morfo/components/form.ts | 2 +- src/uix/morfo/components/knob.ts | 2 +- src/uix/morfo/components/month-grid.ts | 10 +-- src/uix/morfo/components/password-field.ts | 2 +- src/uix/morfo/components/time-field.ts | 4 +- src/uix/morfo/components/year-grid.ts | 10 +-- src/uix/morfo/field-role.ts | 2 +- src/uix/morfo/types.ts | 2 +- .../calendar/calendar-provider.svelte.ts | 32 ++++---- .../carousel/carousel-provider.svelte.ts | 28 +++---- .../chronos/chronos-provider.svelte.ts | 22 +++--- .../color-field-provider.svelte.ts | 14 ++-- .../color-picker-provider.svelte.ts | 38 +++++----- .../command/command-provider.svelte.ts | 22 +++--- .../context-menu-provider.svelte.ts | 30 ++++---- .../css-field/css-field-provider.svelte.ts | 12 +-- .../date-field/date-field-provider.svelte.ts | 4 +- .../date-picker-provider.svelte.ts | 2 +- .../date-range-picker-provider.svelte.ts | 2 +- .../drag-drop-provider.svelte.test.ts | 4 +- .../drag-drop/drag-drop-provider.svelte.ts | 12 +-- .../dropdown-menu-provider.svelte.ts | 42 +++++------ .../editable/editable-provider.svelte.test.ts | 2 +- .../editable/editable-provider.svelte.ts | 18 ++--- .../emoji-picker-provider.svelte.ts | 8 +- .../components/field/field-provider.svelte.ts | 12 +-- .../file-upload-provider.svelte.ts | 14 ++-- .../components/form/form-provider.svelte.ts | 6 +- .../gradient-picker-provider.svelte.ts | 6 +- .../grid-list-provider.svelte.test.ts | 2 +- .../grid-list/grid-list-provider.svelte.ts | 18 ++--- .../components/knob/knob-provider.svelte.ts | 4 +- .../link-preview-provider.svelte.test.ts | 2 +- .../link-preview-provider.svelte.ts | 6 +- .../listbox/listbox-provider.svelte.ts | 20 ++--- .../mask-field/mask-field-provider.svelte.ts | 2 +- .../media-player-provider.svelte.test.ts | 2 +- .../media-player-provider.svelte.ts | 74 +++++++++---------- .../menubar/menubar-provider.svelte.test.ts | 6 +- .../menubar/menubar-provider.svelte.ts | 14 ++-- .../month-grid/month-grid-provider.svelte.ts | 20 ++--- .../natural-time-picker-provider.svelte.ts | 2 +- .../nav-tree/nav-tree-provider.svelte.ts | 12 +-- .../navigation-menu-provider.svelte.ts | 18 ++--- .../number-field-provider.svelte.ts | 14 ++-- .../pagination-provider.svelte.test.ts | 4 +- .../pagination/pagination-provider.svelte.ts | 20 ++--- .../password-field-provider.svelte.ts | 18 ++--- .../path-trace/path-trace-provider.svelte.ts | 6 +- .../picker/picker-provider.svelte.ts | 2 +- .../pin-input/pin-input-provider.svelte.ts | 10 +-- .../proof-of-human-provider.svelte.ts | 2 +- .../range-calendar-provider.svelte.ts | 14 ++-- .../rotate-align-provider.svelte.ts | 6 +- .../scroll-area-provider.svelte.ts | 4 +- .../search-field-provider.svelte.test.ts | 4 +- .../search-field-provider.svelte.ts | 4 +- .../select/select-provider.svelte.ts | 12 +-- .../sidebar/sidebar-provider.svelte.ts | 26 +++---- .../slider/slider-provider.svelte.ts | 6 +- .../stepper/stepper-provider.svelte.ts | 8 +- .../components/table/table-provider.svelte.ts | 24 +++--- .../tag-group/tag-group-provider.svelte.ts | 10 +-- .../tags-input/tags-input-provider.svelte.ts | 24 +++--- .../textarea/textarea-provider.svelte.ts | 2 +- .../time-field/time-field-provider.svelte.ts | 6 +- .../components/time-picker-clock.svelte | 4 +- .../time-picker-provider.svelte.ts | 2 +- .../components/time-range-picker-clock.svelte | 4 +- .../time-range-picker-provider.svelte.ts | 2 +- .../timeline/timeline-provider.svelte.test.ts | 2 +- .../timeline/timeline-provider.svelte.ts | 8 +- .../tree-grid/tree-grid-provider.svelte.ts | 16 ++-- .../tree-view/tree-view-provider.svelte.ts | 16 ++-- .../waveform/waveform-provider.svelte.ts | 4 +- .../year-grid/year-grid-provider.svelte.ts | 20 ++--- src/uix/soma/runtime.svelte.ts | 27 +++---- src/uix/soma/soma-attr-audit.test.ts | 8 +- 83 files changed, 457 insertions(+), 466 deletions(-) diff --git a/src/uix/eidos/components/media-player/README.md b/src/uix/eidos/components/media-player/README.md index 64120dbc0..71789a47c 100644 --- a/src/uix/eidos/components/media-player/README.md +++ b/src/uix/eidos/components/media-player/README.md @@ -163,7 +163,7 @@ en las cuatro variantes, 0 diffs** contra el commit anterior. | Doble-tap-seek | **diferir** | Necesita debounce click/dblclick sobre el media; el tap-to-play cubre el gesto principal. | | Thumbnail preview en el scrubber | **diferir** | **Gap del Slider**: no hay pista de preview por hover; requiere extender Slider o una capa eidos-only. | | Captions surface custom (`Captions` part) | **implementar → hecho** | El player PINTA las cues (ver §Captions abajo). El render nativo se retiró: caía en la franja de la barra de controles y llegaba ilegible. | -| `renderProps` en los botones/partes | **diferir** | Los providers emiten attrs directamente en `props` (pragmático, consistente con el resto del componente); migrar a `renderProps` + `propRef` en el barrido de remediación global. | +| La bolsa de render en los botones/partes | **diferir** | Los providers emiten attrs directamente en `props` (pragmático, consistente con el resto del componente); migrar a la bolsa de render (`.props`) + `propRef` en el barrido de remediación global. | ## Captions — el player pinta las cues (2026-08-04) diff --git a/src/uix/morfo/components/calendar.ts b/src/uix/morfo/components/calendar.ts index 3092e282c..9225cdded 100644 --- a/src/uix/morfo/components/calendar.ts +++ b/src/uix/morfo/components/calendar.ts @@ -93,7 +93,7 @@ export const calendarMorfo = { aria: [ { attr: 'aria-label', value: v.propRef('ariaLabel'), severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"), so - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-disabled', value: v.propRef('disabled'), @@ -270,7 +270,7 @@ export const calendarMorfo = { severity: 'recommended' }, // Provider emitted `boolToStr(...)` (always "true"/"false") for both; - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-readonly', value: v.propRef('readonly'), @@ -350,7 +350,7 @@ export const calendarMorfo = { ], aria: [ // Provider emitted `boolToStr(...)` (always "true"/"false") for both; - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-selected', value: v.propRef('selected'), @@ -391,7 +391,7 @@ export const calendarMorfo = { aria: [ { attr: 'aria-label', value: v.propRef('dateLabel'), severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"); - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. // No aria-selected here: it is not supported on `role='button'` // (checkpoint C4 fix, 2026-07-07) — the selection state lives on the // Cell (`role='gridcell'`), which legitimately carries it. diff --git a/src/uix/morfo/components/date-field.ts b/src/uix/morfo/components/date-field.ts index 2963a711d..2e2f595a3 100644 --- a/src/uix/morfo/components/date-field.ts +++ b/src/uix/morfo/components/date-field.ts @@ -84,7 +84,7 @@ export const dateFieldMorfo = { { // Provider stringified this as `boolToStr(disabled)` ("true"/ // "false" ALWAYS), so it models a boolean state, not presence. - // `propRef` + `ariaBoolean` lets renderProps() emit the same + // `propRef` + `ariaBoolean` lets the render bag emit the same // "true"/"false" the provider used to. attr: 'aria-disabled', value: v.propRef('disabled'), diff --git a/src/uix/morfo/components/date-range-picker.ts b/src/uix/morfo/components/date-range-picker.ts index a776638fd..d41aff412 100644 --- a/src/uix/morfo/components/date-range-picker.ts +++ b/src/uix/morfo/components/date-range-picker.ts @@ -59,7 +59,7 @@ export const dateRangePickerMorfo = { aria: [ // Provider stringified these as `boolToStr(...)` ("true"/"false" // ALWAYS), so they model a boolean state, not presence. `propRef` + - // `ariaBoolean` lets renderProps() emit the same "true"/"false". + // `ariaBoolean` lets the render bag emit the same "true"/"false". { attr: 'aria-disabled', value: v.propRef('disabled'), diff --git a/src/uix/morfo/components/file-upload.ts b/src/uix/morfo/components/file-upload.ts index 88beee625..4090ec144 100644 --- a/src/uix/morfo/components/file-upload.ts +++ b/src/uix/morfo/components/file-upload.ts @@ -127,7 +127,7 @@ export const fileUploadMorfo = { severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"), so - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-disabled', value: v.propRef('disabled'), @@ -157,7 +157,7 @@ export const fileUploadMorfo = { severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"), so - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-disabled', value: v.propRef('disabled'), diff --git a/src/uix/morfo/components/form.ts b/src/uix/morfo/components/form.ts index 001efc7ab..26e5dff68 100644 --- a/src/uix/morfo/components/form.ts +++ b/src/uix/morfo/components/form.ts @@ -106,7 +106,7 @@ export const formMorfo = { { // Provider stringified this as `boolToStr(isPending)` ("true"/ // "false" ALWAYS), so it models a boolean state, not presence. - // `propRef` + `ariaBoolean` lets renderProps() emit the same + // `propRef` + `ariaBoolean` lets the render bag emit the same // "true"/"false" the provider used to. attr: 'aria-busy', value: v.propRef('isPending'), diff --git a/src/uix/morfo/components/knob.ts b/src/uix/morfo/components/knob.ts index 170b48fb1..12f0e7563 100644 --- a/src/uix/morfo/components/knob.ts +++ b/src/uix/morfo/components/knob.ts @@ -68,7 +68,7 @@ export const knobMorfo = { { attr: 'aria-valuemax', value: v.propRef('max') }, { attr: 'aria-valuenow', value: v.propRef('value') }, // The provider overrides this with a locale-formatted string via - // renderProps() — soma genuinely owns the *value* (formatting). + // the render bag — soma genuinely owns the *value* (formatting). { attr: 'aria-valuetext', value: v.propRef('value'), severity: 'recommended' }, { attr: 'aria-labelledby', diff --git a/src/uix/morfo/components/month-grid.ts b/src/uix/morfo/components/month-grid.ts index 1f992e618..175d4b103 100644 --- a/src/uix/morfo/components/month-grid.ts +++ b/src/uix/morfo/components/month-grid.ts @@ -62,7 +62,7 @@ export const monthGridMorfo = { aria: [ { attr: 'aria-label', value: v.propRef('ariaLabel'), severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"), so - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-disabled', value: v.propRef('disabled'), @@ -120,7 +120,7 @@ export const monthGridMorfo = { severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"), so - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-disabled', value: v.propRef('disabled'), @@ -146,7 +146,7 @@ export const monthGridMorfo = { severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"), so - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-disabled', value: v.propRef('disabled'), @@ -171,7 +171,7 @@ export const monthGridMorfo = { severity: 'recommended' }, // Provider emitted `boolToStr(...)` (always "true"/"false") for both; - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-readonly', value: v.propRef('readonly'), @@ -204,7 +204,7 @@ export const monthGridMorfo = { ], aria: [ // Provider emitted `boolToStr(...)` (always "true"/"false") for both; - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-selected', value: v.propRef('selected'), diff --git a/src/uix/morfo/components/password-field.ts b/src/uix/morfo/components/password-field.ts index 25a0f788a..ed1f20353 100644 --- a/src/uix/morfo/components/password-field.ts +++ b/src/uix/morfo/components/password-field.ts @@ -177,7 +177,7 @@ export const passwordFieldMorfo = { { // Provider stringified this as `boolToStr(isInvalid)` ("true"/ // "false" ALWAYS), so it models a boolean state, not presence. - // `propRef` + `ariaBoolean` lets renderProps() emit the same + // `propRef` + `ariaBoolean` lets the render bag emit the same // "true"/"false" the provider used to. attr: 'aria-invalid', value: v.propRef('invalid'), diff --git a/src/uix/morfo/components/time-field.ts b/src/uix/morfo/components/time-field.ts index a38d26dc3..039456371 100644 --- a/src/uix/morfo/components/time-field.ts +++ b/src/uix/morfo/components/time-field.ts @@ -74,7 +74,7 @@ export const timeFieldMorfo = { { // Provider stringified these as `boolToStr(...)` ("true"/"false" // ALWAYS), so they model a boolean state, not presence. `propRef` - // + `ariaBoolean` lets renderProps() emit the same "true"/"false". + // + `ariaBoolean` lets the render bag emit the same "true"/"false". attr: 'aria-disabled', value: v.propRef('disabled'), severity: 'optional', @@ -89,7 +89,7 @@ export const timeFieldMorfo = { { // Provider emitted `boolToTrueOrUndef(invalid)` (present-when-true, // absent otherwise), so literal + `prop-truthy` reproduces that - // exactly — renderProps() emits "true" only when invalid. + // exactly — the render bag emits "true" only when invalid. attr: 'aria-invalid', value: v.literal('true'), severity: 'optional', diff --git a/src/uix/morfo/components/year-grid.ts b/src/uix/morfo/components/year-grid.ts index dd519ccf1..442ab2ee8 100644 --- a/src/uix/morfo/components/year-grid.ts +++ b/src/uix/morfo/components/year-grid.ts @@ -61,7 +61,7 @@ export const yearGridMorfo = { aria: [ { attr: 'aria-label', value: v.propRef('ariaLabel'), severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"), so - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-disabled', value: v.propRef('disabled'), @@ -119,7 +119,7 @@ export const yearGridMorfo = { severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"), so - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-disabled', value: v.propRef('disabled'), @@ -145,7 +145,7 @@ export const yearGridMorfo = { severity: 'recommended' }, // Provider emitted `boolToStr(disabled)` (always "true"/"false"), so - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-disabled', value: v.propRef('disabled'), @@ -170,7 +170,7 @@ export const yearGridMorfo = { severity: 'recommended' }, // Provider emitted `boolToStr(...)` (always "true"/"false") for both; - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-readonly', value: v.propRef('readonly'), @@ -203,7 +203,7 @@ export const yearGridMorfo = { ], aria: [ // Provider emitted `boolToStr(...)` (always "true"/"false") for both; - // `propRef` + `ariaBoolean` lets renderProps() emit the same string. + // `propRef` + `ariaBoolean` lets the render bag emit the same string. { attr: 'aria-selected', value: v.propRef('selected'), diff --git a/src/uix/morfo/field-role.ts b/src/uix/morfo/field-role.ts index d128d91b2..773d5f41c 100644 --- a/src/uix/morfo/field-role.ts +++ b/src/uix/morfo/field-role.ts @@ -26,7 +26,7 @@ export type FieldRole = keyof typeof FIELD_ROLE_ATTR; * own state attrs and adds its specifics. * * Mirrors the existing css-field ↔ spin-field adoption (`data-spin-field*` via - * `v.literal('')`): a static presence attr auto-emitted through `renderProps()`, + * `v.literal('')`): a static presence attr auto-emitted through the render bag, * so no per-provider soma change is needed. Used as a plain `data[]` element (not * spread) so the morfo stays an `as const satisfies Morfo` literal — the kebab (and * thus `AttrsOf`) is untouched. diff --git a/src/uix/morfo/types.ts b/src/uix/morfo/types.ts index 7e20af829..9303d8048 100644 --- a/src/uix/morfo/types.ts +++ b/src/uix/morfo/types.ts @@ -283,7 +283,7 @@ export interface MorfoAriaEntry { * Render the value as an ARIA boolean (`"true"`/`"false"` string) instead of * passing it through raw. A `stateRef` value already compiles to aria-boolean; * set this for a `propRef` boolean (e.g. `aria-required` ← `required` prop) so - * `renderProps()` stringifies it and the provider need not re-declare it. + * the render bag stringifies it and the provider need not re-declare it. */ ariaBoolean?: boolean; } diff --git a/src/uix/soma/components/calendar/calendar-provider.svelte.ts b/src/uix/soma/components/calendar/calendar-provider.svelte.ts index c69b86680..05c9984ff 100644 --- a/src/uix/soma/components/calendar/calendar-provider.svelte.ts +++ b/src/uix/soma/components/calendar/calendar-provider.svelte.ts @@ -166,7 +166,7 @@ export class CalendarProvider implements CalendarNav { owner: this, context: CalendarProvider.ctx, // Source for the morfo's `aria-disabled` (propRef('disabled')) so - // renderProps() resolves it instead of the provider re-declaring it. + // the render bag resolves it instead of the provider re-declaring it. props: { disabled: () => opts.disabled.current } }); @@ -583,8 +583,8 @@ export class CalendarProvider implements CalendarNav { readonly props = $derived.by(() => this.runtimePart.assert({ - // role + aria-disabled are morfo-declared, resolved via renderProps. - ...this.runtimePart.renderProps(), + // role + aria-disabled are morfo-declared, resolved via the render bag. + ...this.runtimePart.props, // Computed accessible name (calendarLabel + heading) — overrides the // morfo's raw `ariaLabel` propRef (which the provider doesn't source). 'aria-label': this.fullCalendarLabel, @@ -751,8 +751,8 @@ abstract class CalendarPaginationButtonProvider { const disabled = this.disabled(); return this.runtimePart.assert({ // type, aria-label (commonRef calendar.prev/next-month) and - // aria-disabled are morfo-declared, resolved via renderProps. - ...this.runtimePart.renderProps(), + // aria-disabled are morfo-declared, resolved via the render bag. + ...this.runtimePart.props, 'data-disabled': boolToEmptyStrOrUndef(disabled), disabled, onclick: this.onclick @@ -860,8 +860,8 @@ export class CalendarMonthSelectProvider { const disabled = this.isDisabled; return this.runtimePart.assert({ // aria-label (commonRef calendar.month-select) and aria-disabled are - // morfo-declared, resolved via renderProps. - ...this.runtimePart.renderProps(), + // morfo-declared, resolved via the render bag. + ...this.runtimePart.props, 'data-disabled': boolToEmptyStrOrUndef(disabled), disabled, value: String(this.selectedMonthItem.value), @@ -940,8 +940,8 @@ export class CalendarYearSelectProvider { const disabled = this.isDisabled; return this.runtimePart.assert({ // aria-label (commonRef calendar.year-select) and aria-disabled are - // morfo-declared, resolved via renderProps. - ...this.runtimePart.renderProps(), + // morfo-declared, resolved via the render bag. + ...this.runtimePart.props, 'data-disabled': boolToEmptyStrOrUndef(disabled), disabled, value: String(this.selectedYearItem.value), @@ -976,7 +976,7 @@ export class CalendarGridProvider { ref: opts.ref, owner: this, // Sources for the morfo's `aria-readonly`/`aria-disabled` - // (propRef('readonly')/propRef('disabled')) so renderProps resolves them. + // (propRef('readonly')/propRef('disabled')) so the render bag resolves them. props: { readonly: () => this.provider.opts.readonly.current, disabled: () => this.provider.opts.disabled.current @@ -988,8 +988,8 @@ export class CalendarGridProvider { const headingId = this.opts.labelledBy.current ?? this.provider.headingId.current; return this.runtimePart.assert({ // role, aria-readonly and aria-disabled are morfo-declared, resolved - // via renderProps. - ...this.runtimePart.renderProps(), + // via the render bag. + ...this.runtimePart.props, // Computed: honours the `labelledBy` prop override + the registered // heading id (overrides the morfo's raw partRef('heading')). 'aria-labelledby': headingId || undefined @@ -1144,8 +1144,8 @@ export class CalendarCellProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // role, aria-selected and aria-disabled are morfo-declared, resolved - // via renderProps. - ...this.runtimePart.renderProps(), + // via the render bag. + ...this.runtimePart.props, // Soma-owned data values (the morfo declares these attrs without a value). 'data-selected': boolToEmptyStrOrUndef(this.selected), 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), @@ -1212,8 +1212,8 @@ export class CalendarDayProvider { const s = this.cell.snippetProps; const tabindex = this.focused && !s.disabled ? 0 : -1; return this.runtimePart.assert({ - // role and aria-disabled are morfo-declared, resolved via renderProps. - ...this.runtimePart.renderProps(), + // role and aria-disabled are morfo-declared, resolved via the render bag. + ...this.runtimePart.props, tabindex, // Soma-owned data values (the morfo declares these attrs without a value). 'data-selected': boolToEmptyStrOrUndef(s.selected), diff --git a/src/uix/soma/components/carousel/carousel-provider.svelte.ts b/src/uix/soma/components/carousel/carousel-provider.svelte.ts index d1129d6bc..d4d742f39 100644 --- a/src/uix/soma/components/carousel/carousel-provider.svelte.ts +++ b/src/uix/soma/components/carousel/carousel-provider.svelte.ts @@ -443,16 +443,16 @@ export class CarouselProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // role + aria-roledescription are morfo-declared, resolved by renderProps. + // role + aria-roledescription are morfo-declared, resolved by the render bag. // aria-label rides the render bag as the naming DEFAULT (consumer-first, // A-85): the consumer's own `aria-label` stays in restProps and wins by // merge policy — the old resolver chain routed it through here only to // hand it back unchanged. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, tabindex: this.isDisabled ? -1 : 0, lang: this.opts.locale.current || undefined, // data-* below are contract-only in the morfo (declared with `values`, - // no resolvable `value`), so renderProps doesn't emit them — soma owns them. + // no resolvable `value`), so the render bag doesn't emit them — soma owns them. 'data-orientation': this.opts.orientation.current, 'data-dir': this.resolvedDir, 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), @@ -607,8 +607,8 @@ export class CarouselItemProvider { // gap is the default `0px`. const basis = spv === 1 ? '100%' : `calc((100% - ${spv - 1} * ${gap}) / ${spv})`; return this.runtimePart.assert({ - // role + aria-roledescription are morfo-declared, resolved by renderProps. - ...this.runtimePart.renderProps(), + // role + aria-roledescription are morfo-declared, resolved by the render bag. + ...this.runtimePart.props, // aria-label: soma override — the morfo declares propRef('index') (the raw // slide number); soma supplies the localizable "Slide N of M" phrasing. 'aria-label': `Slide ${this.opts.index.current + 1} of ${this.provider.slideCount}`, @@ -657,10 +657,10 @@ export class CarouselPrevTriggerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // type + role are morfo-declared, resolved by renderProps. aria-label is the + // type + role are morfo-declared, resolved by the render bag. aria-label is the // naming DEFAULT declared in the morfo (consumer-first, A-85) and rides the // render bag; the consumer's own attr wins in `mergeProps`. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, disabled: this.isDisabled || undefined, 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), onclick: this.onclick @@ -700,10 +700,10 @@ export class CarouselNextTriggerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // type + role are morfo-declared, resolved by renderProps. aria-label is the + // type + role are morfo-declared, resolved by the render bag. aria-label is the // naming DEFAULT declared in the morfo (consumer-first, A-85) and rides the // render bag; the consumer's own attr wins in `mergeProps`. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, disabled: this.isDisabled || undefined, 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), onclick: this.onclick @@ -731,7 +731,7 @@ export class CarouselIndicatorGroupProvider { id: opts.id, ref: opts.ref, owner: this, - // `orientation` source so renderProps resolves the morfo's + // `orientation` source so the render bag resolves the morfo's // aria-orientation (propRef('orientation')). props: { orientation: () => this.provider.opts.orientation.current } }); @@ -739,8 +739,8 @@ export class CarouselIndicatorGroupProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // role + aria-orientation are morfo-declared, resolved by renderProps. - ...this.runtimePart.renderProps(), + // role + aria-orientation are morfo-declared, resolved by the render bag. + ...this.runtimePart.props, // data-orientation is contract-only in the morfo (no resolvable `value`) — soma owns it. 'data-orientation': this.provider.opts.orientation.current } as const) @@ -787,8 +787,8 @@ export class CarouselIndicatorProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // type + role are morfo-declared, resolved by renderProps. - ...this.runtimePart.renderProps(), + // type + role are morfo-declared, resolved by the render bag. + ...this.runtimePart.props, // aria-selected: soma override — the morfo declares stateRef('active') which // stringifies to 'true'/'false'; soma supplies the real boolean (the active // flag is computed here, not registered as a runtime state source). diff --git a/src/uix/soma/components/chronos/chronos-provider.svelte.ts b/src/uix/soma/components/chronos/chronos-provider.svelte.ts index ae621e6e1..7cae67ffd 100644 --- a/src/uix/soma/components/chronos/chronos-provider.svelte.ts +++ b/src/uix/soma/components/chronos/chronos-provider.svelte.ts @@ -1433,8 +1433,8 @@ export class ChronosProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // role + aria-label + aria-disabled are morfo-declared (renderProps). - ...this.runtimePart.renderProps(), + // role + aria-label + aria-disabled are morfo-declared (the render bag). + ...this.runtimePart.props, 'data-view': this.view, 'data-readonly': boolToEmptyStrOrUndef(this.opts.readonly.current), 'data-disabled': boolToEmptyStrOrUndef(this.opts.disabled.current) @@ -1530,7 +1530,7 @@ export class ChronosRegionProvider { if (part === 'heading') this.provider.headingId = opts.id.current; } - readonly props = $derived.by(() => this.runtimePart.renderProps()); + readonly props = $derived.by(() => this.runtimePart.props); } /** @@ -1572,7 +1572,7 @@ export class ChronosAgendaItemProvider { }); } - readonly props = $derived.by(() => this.runtimePart.renderProps()); + readonly props = $derived.by(() => this.runtimePart.props); } // ── Event chip ─────────────────────────────────────────────────────────────── @@ -1588,7 +1588,7 @@ export class ChronosAgendaItemProvider { * `role` / aria never reached the DOM (the template re-typed them by hand), and * nothing could resolve a chip element back to its instance — which is why the * four events it carries had to smuggle an element through `targetOverride`. - * Registering per instance closes both: `renderProps()` applies the declared + * Registering per instance closes both: `the render bag` applies the declared * contract, and {@link SomaRuntimePart.trigger} anchors the stamp on the chip * the user actually touched. */ @@ -1659,7 +1659,7 @@ export class ChronosEventChipProvider { this.runtimePart.assert({ // Morfo-declared: role=button, aria-label, data-event-id, data-color — // resolved against THIS chip's sources. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, tabindex: 0, onkeydown: this.onkeydown, // Soma-owned: the morfo declares these attrs without a value source. @@ -1703,7 +1703,7 @@ export class ChronosResizeHandleProvider { readonly props = $derived.by(() => // Morfo-declared: data-edge (propRef) + aria-hidden. - this.runtimePart.assert({ ...this.runtimePart.renderProps() } as const) + this.runtimePart.assert({ ...this.runtimePart.props } as const) ); } @@ -1752,7 +1752,7 @@ export class ChronosDayCellProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: role=gridcell, data-date, aria-label, aria-selected. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: the roving tab stop and the value-less state markers. tabindex: this.opts.focused.current ? 0 : -1, 'data-today': boolToEmptyStrOrUndef(this.opts.today.current), @@ -1801,7 +1801,7 @@ export class ChronosActionProvider { } // Morfo-declared: role, type, the translated aria-label. - readonly props = $derived.by(() => this.runtimePart.renderProps()); + readonly props = $derived.by(() => this.runtimePart.props); } // ── "+N more" overflow link ────────────────────────────────────────────────── @@ -1838,7 +1838,7 @@ export class ChronosMoreLinkProvider { } // Morfo-declared: role, type, the translated aria-label, data-count. - readonly props = $derived.by(() => this.runtimePart.renderProps()); + readonly props = $derived.by(() => this.runtimePart.props); } // ── Navigation buttons ─────────────────────────────────────────────────────── @@ -1881,7 +1881,7 @@ export class ChronosNavButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: role, type, the translated aria-label, aria-disabled. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-disabled': boolToEmptyStrOrUndef(this.opts.disabled.current) } as const) ); diff --git a/src/uix/soma/components/color-field/color-field-provider.svelte.ts b/src/uix/soma/components/color-field/color-field-provider.svelte.ts index 0e57a99a0..b461ff739 100644 --- a/src/uix/soma/components/color-field/color-field-provider.svelte.ts +++ b/src/uix/soma/components/color-field/color-field-provider.svelte.ts @@ -162,7 +162,7 @@ export class ColorFieldProvider { context: ColorFieldProvider.ctx, // Per-part source for the morfo's `aria-invalid` (literal 'true' gated // on `prop-truthy: invalid`). `isInvalid` merges validation + Field — - // richer than `opts.invalid`, so renderProps resolves the gate against it. + // richer than `opts.invalid`, so the render bag resolves the gate against it. props: { invalid: () => this.isInvalid } }); @@ -419,7 +419,7 @@ export class ColorFieldProvider { } readonly sharedSegmentAttrs = { - // `role: 'spinbutton'` comes from the morfo via renderProps() on each + // `role: 'spinbutton'` comes from the morfo via the render bag() on each // segment — not re-declared here. contenteditable: 'true' as const, tabindex: 0, @@ -435,7 +435,7 @@ export class ColorFieldProvider { this.runtimePart.assert({ // Morfo supplies `aria-invalid` (literal 'true' when `invalid` truthy), // resolved against the per-part `invalid` source registered above. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned data-* — the morfo declares these without a value source. 'data-format': this.opts.format.current, 'data-invalid': boolToEmptyStrOrUndef(this.isInvalid), @@ -515,7 +515,7 @@ export class ColorFieldInputProvider { ref: opts.ref, owner: this, onRefChange: (v) => this.provider.setFieldNode(v), - // Per-part sources for the morfo's value-bound aria. renderProps then + // Per-part sources for the morfo's value-bound aria. the render bag then // resolves aria-disabled/readonly (ariaBoolean) + aria-invalid against // the merged disabled/readonly/invalid flags instead of the provider // re-declaring them. (aria-label/aria-labelledby stay soma overrides — @@ -549,7 +549,7 @@ export class ColorFieldInputProvider { // Morfo supplies role=group + aria-disabled/readonly/invalid (resolved // against the registered flags). aria-label/aria-labelledby below // override the morfo defaults (explicit prop + labelNode fallback). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'aria-label': this.opts.ariaLabel.current, 'aria-labelledby': labelledBy, // Soma-owned data-* — the morfo declares these without a value source. @@ -780,7 +780,7 @@ abstract class BaseColorSegmentProvider { // aria-invalid (resolved against the registered flags). The computed // aria below (value/min/max/label/labelledby) overrides the morfo's raw // propRef defaults; aria-readonly is a per-segment soma override. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, ...this.provider.sharedSegmentAttrs, // Soma-owned data-* — the morfo declares these without a value source. 'data-segment': this.part, @@ -941,7 +941,7 @@ export class ColorFieldFormatSelectProvider { // Morfo supplies the default aria-label (translationRef → format-select). // The soma aria-label below overrides it to honour an explicit ariaLabel // prop (falling back to the same default label). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, disabled: this.provider.isDisabled || undefined, 'aria-label': this.opts.ariaLabel.current ?? this.provider.getFormatSelectLabel(), // Soma-owned data-* — the morfo declares these without a value source. diff --git a/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts b/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts index 60a7fa8d5..3ed27329c 100644 --- a/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts +++ b/src/uix/soma/components/color-picker/color-picker-provider.svelte.ts @@ -481,8 +481,8 @@ export class ColorPickerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared `data-state` (← stateRef `open`) is resolved by - // renderProps against the component-level `open` state source. - ...this.runtimePart.renderProps(), + // the render bag against the component-level `open` state source. + ...this.runtimePart.props, 'aria-invalid': this.isInvalid || undefined, // The morfo declares these data-* WITHOUT a value (presence / // soma-owned), so soma still supplies them. @@ -615,7 +615,7 @@ export class ColorPickerTriggerProvider { // Morfo supplies `type`, `aria-haspopup`, `aria-expanded` (← state // `open`) and the default translated `aria-label`. aria-controls is // dropped (Content registers on the Popover runtime, not ours). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma override: honour an explicit `ariaLabel` prop over the morfo // default. 'aria-label': this.resolvedLabel, @@ -669,8 +669,8 @@ export class ColorPickerValueSwatchProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo declares aria-hidden; renderProps supplies it. - ...this.runtimePart.renderProps(), + // Morfo declares aria-hidden; the render bag supplies it. + ...this.runtimePart.props, style: this.backgroundStyle } as const) ); @@ -949,8 +949,8 @@ export class ColorPickerAreaBackgroundProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo declares aria-hidden; renderProps supplies it. - ...this.runtimePart.renderProps(), + // Morfo declares aria-hidden; the render bag supplies it. + ...this.runtimePart.props, style: this.backgroundStyle } as const) ); @@ -1046,9 +1046,9 @@ export class ColorPickerAreaThumbProvider { const ariaText = this.ariaValueText; return this.runtimePart.assert({ // Morfo supplies role=slider. aria-label / aria-valuenow are - // propRefs over unregistered sources, so renderProps drops them and + // propRefs over unregistered sources, so the render bag drops them and // soma supplies the computed 2D aria-label / aria-valuetext below. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, tabindex: this.provider.isDisabled ? -1 : 0, 'aria-label': `${this.area.xChannel}, ${this.area.yChannel}`, 'aria-valuetext': ariaText, @@ -1173,8 +1173,8 @@ export class ColorPickerTransparencyGridProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo declares aria-hidden; renderProps supplies it. - ...this.runtimePart.renderProps(), + // Morfo declares aria-hidden; the render bag supplies it. + ...this.runtimePart.props, style: this.style } as const) ); @@ -1215,7 +1215,7 @@ export class ColorPickerSwatchGroupProvider { this.runtimePart.assert({ // Morfo supplies role=radiogroup. aria-label is soma-owned (not // morfo-declared) — honours an explicit prop / falls back to langs. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'aria-label': this.resolvedLabel, 'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled) } as const) @@ -1240,7 +1240,7 @@ export class ColorPickerSwatchTriggerProvider { id: opts.id, ref: opts.ref, owner: this, - // Per-part source so renderProps resolves the morfo aria-checked + // Per-part source so the render bag resolves the morfo aria-checked // (ariaBoolean over propRef `checked`) — always emits true/false. props: { checked: () => this.isChecked } }); @@ -1273,7 +1273,7 @@ export class ColorPickerSwatchTriggerProvider { // Morfo supplies type=button, role=radio and aria-checked (← propRef // `checked`, registered above). aria-label is propRef over an // unregistered source, so soma supplies the colour string below. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'aria-label': this.opts.color.current, 'data-checked': boolToEmptyStrOrUndef(this.isChecked), 'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled), @@ -1309,8 +1309,8 @@ export class ColorPickerSwatchProvider { readonly props = $derived.by(() => { const c = this.effectiveColor; return this.runtimePart.assert({ - // Morfo declares aria-hidden; renderProps supplies it. - ...this.runtimePart.renderProps(), + // Morfo declares aria-hidden; the render bag supplies it. + ...this.runtimePart.props, style: c ? `background: ${c};` : undefined } as const); }); @@ -1337,8 +1337,8 @@ export class ColorPickerSwatchIndicatorProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo declares aria-hidden; renderProps supplies it. - ...this.runtimePart.renderProps() + // Morfo declares aria-hidden; the render bag supplies it. + ...this.runtimePart.props } as const) ); } @@ -1412,7 +1412,7 @@ export class ColorPickerEyeDropperProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo supplies type=button + the default translated aria-label. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma override: honour an explicit `ariaLabel` prop over the default. 'aria-label': this.resolvedLabel, 'data-unsupported': boolToEmptyStrOrUndef(!this.isSupported), diff --git a/src/uix/soma/components/command/command-provider.svelte.ts b/src/uix/soma/components/command/command-provider.svelte.ts index fd7b412cc..6354e2ca9 100644 --- a/src/uix/soma/components/command/command-provider.svelte.ts +++ b/src/uix/soma/components/command/command-provider.svelte.ts @@ -523,7 +523,7 @@ export class CommandInputProvider { const listId = this.provider.listId.current; return this.runtimePart.assert({ // Morfo-declared: role, aria-autocomplete, aria-expanded. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, type: 'text' as const, autocomplete: 'off' as const, autocorrect: 'off' as const, @@ -574,7 +574,7 @@ export class CommandListProvider { // Morfo-declared: role. The morfo also declares aria-label via // translationRef (the default); soma overrides to honour an explicit // `label` prop, falling back to the same translated string. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'aria-label': this.provider.opts.label.current ?? this.provider.soma.langs.ts(COMMAND_LANGS.LIST) } as const) @@ -654,7 +654,7 @@ export class CommandItemProvider { ref: opts.ref, owner: this, // Per-part sources for the morfo's aria-selected/aria-disabled — the - // component-level runtime carries no sources. With these, renderProps() + // component-level runtime carries no sources. With these, the render bag // resolves those aria attrs from the morfo instead of re-declaring them. props: { selected: () => this.isSelected, @@ -748,7 +748,7 @@ export class CommandItemProvider { this.runtimePart.assert({ // Morfo-declared: role, aria-selected, aria-disabled (resolved from this // part's registered selected/disabled sources). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: composed aria id (overrides the registration id) + the // data-* attrs the morfo declares WITHOUT a value (it owns the value). id: this.idForAria, @@ -872,7 +872,7 @@ export class CommandLinkItemProvider { this.runtimePart.assert({ // Morfo-declared: role, aria-selected, aria-disabled (resolved from this // part's registered selected/disabled sources). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: composed aria id, the native href the morfo doesn't model, // and the data-* attrs the morfo declares WITHOUT a value. id: this.idForAria, @@ -942,7 +942,7 @@ export class CommandGroupProvider { // partRef('group-heading'), but that can't resolve without a registered // parts source — soma supplies the registered heading id. data-empty is // declared without a value (soma owns it). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'aria-labelledby': this.headingId.current || undefined, 'data-empty': boolToEmptyStrOrUndef(!this.hasVisibleItems) } as const) @@ -975,7 +975,7 @@ export class CommandGroupHeadingProvider { readonly props = $derived.by(() => // Morfo-declared: aria-hidden. No soma-specific extras. - this.runtimePart.assert({ ...this.runtimePart.renderProps() } as const) + this.runtimePart.assert({ ...this.runtimePart.props } as const) ); } @@ -1002,7 +1002,7 @@ export class CommandGroupItemsProvider { readonly props = $derived.by(() => // Morfo-declared: role. No soma-specific extras. - this.runtimePart.assert({ ...this.runtimePart.renderProps() } as const) + this.runtimePart.assert({ ...this.runtimePart.props } as const) ); } @@ -1033,7 +1033,7 @@ export class CommandEmptyProvider { readonly props = $derived.by(() => // Morfo-declared: role, aria-live. No soma-specific extras. - this.runtimePart.assert({ ...this.runtimePart.renderProps() } as const) + this.runtimePart.assert({ ...this.runtimePart.props } as const) ); } @@ -1071,7 +1071,7 @@ export class CommandLoadingProvider { // Morfo-declared: role, aria-valuemin/max, aria-valuenow (from the // registered percent source). data-value is declared without a value — // soma owns it. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-value': this.percent.current } as const) ); @@ -1105,6 +1105,6 @@ export class CommandSeparatorProvider { readonly props = $derived.by(() => // Morfo-declared: role, aria-orientation. No soma-specific extras. - this.runtimePart.assert({ ...this.runtimePart.renderProps() } as const) + this.runtimePart.assert({ ...this.runtimePart.props } as const) ); } diff --git a/src/uix/soma/components/context-menu/context-menu-provider.svelte.ts b/src/uix/soma/components/context-menu/context-menu-provider.svelte.ts index b88fc5645..be66eddb0 100644 --- a/src/uix/soma/components/context-menu/context-menu-provider.svelte.ts +++ b/src/uix/soma/components/context-menu/context-menu-provider.svelte.ts @@ -180,7 +180,7 @@ export class ContextMenuTriggerProvider { id: opts.id, ref: opts.ref, owner: this, - // Per-part sources so renderProps resolves the morfo's state-/part- + // Per-part sources so the render bag resolves the morfo's state-/part- // bound aria: aria-expanded ← open, aria-controls ← content id. states: { open: () => this.provider.opts.open.current }, parts: { content: () => this.provider.contentId.current } @@ -199,7 +199,7 @@ export class ContextMenuTriggerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: aria-haspopup, aria-expanded, aria-controls. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: data-state (morfo declares it without a value) + the // native disabled marker + the handler. 'data-state': getDataOpenClosed(this.provider.opts.open.current), @@ -262,7 +262,7 @@ export class ContextMenuContentProvider { id: opts.id, ref: opts.ref, owner: this, - // aria-labelledby ← trigger id (renderProps resolves the partRef). + // aria-labelledby ← trigger id (the render bag resolves the partRef). parts: { trigger: () => this.provider.triggerId.current }, onRefChange: (el) => { this.provider.setContentRef(el); @@ -382,7 +382,7 @@ export class ContextMenuContentProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: role=menu, aria-labelledby, aria-orientation. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned data-state (morfo declares it without a value) + layers. 'data-state': getDataOpenClosed(this.provider.opts.open.current), ...this.floating.props, @@ -478,7 +478,7 @@ export class ContextMenuItemProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: role=menuitem, aria-disabled. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: tabindex + the no-value data-* markers + handlers. tabindex: this.opts.disabled.current ? undefined : -1, 'data-disabled': boolToEmptyStrOrUndef(this.opts.disabled.current), @@ -518,14 +518,14 @@ export class ContextMenuGroupProvider { ref: opts.ref, owner: this, context: ContextMenuGroupProvider.ctx, - // aria-labelledby ← group-heading id (renderProps resolves the partRef). + // aria-labelledby ← group-heading id (the render bag resolves the partRef). parts: { 'group-heading': () => this.headingId.current } }); } readonly props = $derived.by(() => // Morfo-declared: role=group, aria-labelledby. - this.runtimePart.assert({ ...this.runtimePart.renderProps() } as const) + this.runtimePart.assert({ ...this.runtimePart.props } as const) ); } @@ -582,7 +582,7 @@ export class ContextMenuSeparatorProvider { readonly props = $derived.by(() => // Morfo-declared: role=separator, aria-orientation=horizontal. - this.runtimePart.assert({ ...this.runtimePart.renderProps() } as const) + this.runtimePart.assert({ ...this.runtimePart.props } as const) ); } @@ -643,7 +643,7 @@ export class ContextMenuCheckboxItemProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: role=menuitemcheckbox, aria-checked, aria-disabled. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: tabindex + the no-value data-* markers + handlers. tabindex: this.opts.disabled.current ? undefined : -1, 'data-state': this.opts.checked.current ? 'checked' : 'unchecked', @@ -687,7 +687,7 @@ export class ContextMenuRadioGroupProvider { readonly props = $derived.by(() => // Morfo-declared: role=group. - this.runtimePart.assert({ ...this.runtimePart.renderProps() } as const) + this.runtimePart.assert({ ...this.runtimePart.props } as const) ); } @@ -753,7 +753,7 @@ export class ContextMenuRadioItemProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: role=menuitemradio, aria-checked, aria-disabled. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: tabindex + the no-value data-* markers + handlers. tabindex: this.opts.disabled.current ? undefined : -1, 'data-state': this.isChecked ? 'checked' : 'unchecked', @@ -878,7 +878,7 @@ export class ContextMenuSubTriggerProvider { id: opts.id, ref: opts.ref, owner: this, - // renderProps resolves the morfo's state-/part-/prop-bound aria: + // the render bag resolves the morfo's state-/part-/prop-bound aria: // aria-expanded ← open, aria-controls ← sub-content id, // aria-disabled ← disabled prop. states: { open: () => this.sub.opts.open.current }, @@ -952,7 +952,7 @@ export class ContextMenuSubTriggerProvider { this.runtimePart.assert({ // Morfo-declared: role=menuitem, aria-haspopup, aria-expanded, // aria-controls, aria-disabled. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: tabindex + the no-value data-* markers + handlers. tabindex: this.opts.disabled.current ? undefined : -1, 'data-state': getDataOpenClosed(this.sub.opts.open.current), @@ -1001,7 +1001,7 @@ export class ContextMenuSubContentProvider { id: opts.id, ref: opts.ref, owner: this, - // aria-labelledby ← sub-trigger id (renderProps resolves the partRef). + // aria-labelledby ← sub-trigger id (the render bag resolves the partRef). parts: { 'sub-trigger': () => this.sub.subTriggerId.current }, onRefChange: (el) => { this.sub.setContentRef(el); @@ -1083,7 +1083,7 @@ export class ContextMenuSubContentProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: role=menu, aria-labelledby, aria-orientation. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned data-state (morfo declares it without a value) + layers. 'data-state': getDataOpenClosed(this.sub.opts.open.current), ...this.floating.props, diff --git a/src/uix/soma/components/css-field/css-field-provider.svelte.ts b/src/uix/soma/components/css-field/css-field-provider.svelte.ts index 518aa9419..9da94df1c 100644 --- a/src/uix/soma/components/css-field/css-field-provider.svelte.ts +++ b/src/uix/soma/components/css-field/css-field-provider.svelte.ts @@ -546,7 +546,7 @@ export class CssFieldInputProvider { // Morfo-declared: role, data-spin-field-input, aria-required/disabled/ // readonly/invalid (the last four via ariaBoolean). Soma adds only the // CSS-value-specific aria below + handlers/native. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, inputmode: 'text' as const, autocomplete: 'off' as const, autocorrect: 'off' as const, @@ -604,7 +604,7 @@ export class CssFieldIncrementTriggerProvider { id: opts.id, ref: opts.ref, owner: this, - // trigger-local disabled (includes atMax) → renderProps resolves aria-disabled + // trigger-local disabled (includes atMax) → the render bag resolves aria-disabled props: { disabled: () => this.isDisabled } }); } @@ -629,7 +629,7 @@ export class CssFieldIncrementTriggerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // type, aria-label, data-spin-field, aria-disabled are morfo-declared. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, tabindex: -1, disabled: this.isDisabled || undefined, 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), @@ -670,7 +670,7 @@ export class CssFieldDecrementTriggerProvider { id: opts.id, ref: opts.ref, owner: this, - // trigger-local disabled (includes atMin) → renderProps resolves aria-disabled + // trigger-local disabled (includes atMin) → the render bag resolves aria-disabled props: { disabled: () => this.isDisabled } }); } @@ -695,7 +695,7 @@ export class CssFieldDecrementTriggerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // type, aria-label, data-spin-field, aria-disabled are morfo-declared. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, tabindex: -1, disabled: this.isDisabled || undefined, 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), @@ -818,7 +818,7 @@ export class CssFieldScrubberProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // role, aria-hidden, data-spin-field-scrubber are morfo-declared. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-scrubbing': boolToEmptyStrOrUndef(this.provider.scrubbing), 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), style: { diff --git a/src/uix/soma/components/date-field/date-field-provider.svelte.ts b/src/uix/soma/components/date-field/date-field-provider.svelte.ts index fe07e8bc3..c29ddbe62 100644 --- a/src/uix/soma/components/date-field/date-field-provider.svelte.ts +++ b/src/uix/soma/components/date-field/date-field-provider.svelte.ts @@ -933,7 +933,7 @@ export class DateFieldInputProvider { owner: this, 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. + // `the render bag` resolves it instead of the provider re-declaring it. // (`disabled`/`invalid` come from the component-level sources.) props: { ariaLabel: () => this.opts.ariaLabel.current } }); @@ -971,7 +971,7 @@ export class DateFieldInputProvider { // Morfo-declared attrs (role=group, aria-label, aria-disabled, and // aria-labelledby from the Label part), resolved against this part's // registered sources — no longer re-declared in the provider. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // 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); diff --git a/src/uix/soma/components/date-picker/date-picker-provider.svelte.ts b/src/uix/soma/components/date-picker/date-picker-provider.svelte.ts index 4811c905b..ef636bf3c 100644 --- a/src/uix/soma/components/date-picker/date-picker-provider.svelte.ts +++ b/src/uix/soma/components/date-picker/date-picker-provider.svelte.ts @@ -291,7 +291,7 @@ export class DatePickerProvider { // invalid, aria-disabled/readonly/required), resolved against the // component-level `open` state + disabled/readonly/required/invalid // prop sources — no longer re-declared in the provider. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Raw, so no assertion leaves the root inheriting. The in-place half reads // it: the root IS the field, and its segment row (`[data-field-control]`, // `flex-direction: row`) reverses in RTL. The portalled calendar reads the diff --git a/src/uix/soma/components/date-range-picker/date-range-picker-provider.svelte.ts b/src/uix/soma/components/date-range-picker/date-range-picker-provider.svelte.ts index 7333ed936..c17dc9be6 100644 --- a/src/uix/soma/components/date-range-picker/date-range-picker-provider.svelte.ts +++ b/src/uix/soma/components/date-range-picker/date-range-picker-provider.svelte.ts @@ -296,7 +296,7 @@ export class DateRangePickerProvider { // invalid, aria-disabled/readonly/required), resolved against the // component-level sources (`states.open` + `props.disabled/readonly/ // required/invalid`) — no longer re-declared in the provider. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Raw, so no assertion leaves the root inheriting. The root itself is the // in-place paint: `[data-date-range-picker]` is a wrapping flex ROW, so the // endpoint + trigger order reverses in RTL (each endpoint's own composed diff --git a/src/uix/soma/components/drag-drop/drag-drop-provider.svelte.test.ts b/src/uix/soma/components/drag-drop/drag-drop-provider.svelte.test.ts index 3fb8d6396..3773d6d26 100644 --- a/src/uix/soma/components/drag-drop/drag-drop-provider.svelte.test.ts +++ b/src/uix/soma/components/drag-drop/drag-drop-provider.svelte.test.ts @@ -164,7 +164,7 @@ describe('DragDropProvider', () => { expect(result.draggable.props).toMatchObject({ role: 'button', tabindex: 0, - // renderProps() emits the morfo's literal ARIA string, not a JS boolean. + // the render bag emits the morfo's literal ARIA string, not a JS boolean. 'aria-grabbed': 'true', 'data-dragging': '', 'data-value': 'card-a' @@ -177,7 +177,7 @@ describe('DragDropProvider', () => { 'data-accepting': '' }); expect(result.preview.props).toMatchObject({ - // renderProps() emits the morfo's literal ARIA string, not a JS boolean. + // the render bag emits the morfo's literal ARIA string, not a JS boolean. 'aria-hidden': 'true', 'data-active': '' }); diff --git a/src/uix/soma/components/drag-drop/drag-drop-provider.svelte.ts b/src/uix/soma/components/drag-drop/drag-drop-provider.svelte.ts index dfc10f614..17dbb45f9 100644 --- a/src/uix/soma/components/drag-drop/drag-drop-provider.svelte.ts +++ b/src/uix/soma/components/drag-drop/drag-drop-provider.svelte.ts @@ -357,7 +357,7 @@ export class DragDropProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo declares data-dragging without a value (soma-owned); supply it. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-dragging': boolToEmptyStrOrUndef(this.active !== null) } as const) ); @@ -401,7 +401,7 @@ export class DraggableProvider { owner: this, // Per-part sources for the morfo's conditional aria (aria-disabled ← // prop-truthy `disabled`, aria-grabbed ← prop-truthy `active`). With - // these, `renderProps()` resolves them instead of the provider + // these, `the render bag` resolves them instead of the provider // re-declaring role/aria. props: { disabled: () => this.opts.disabled.current, @@ -501,7 +501,7 @@ export class DraggableProvider { // role, aria-disabled (literal+condition), aria-grabbed (literal+ // condition) are morfo-declared and resolved against this part's // registered `disabled`/`active` sources. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, tabindex: this.opts.disabled.current ? -1 : 0, // aria-roledescription: the morfo declares the `draggable` label key // ("Draggable"); soma overrides with the dedicated role-description key @@ -548,7 +548,7 @@ export class DroppableProvider { ref: opts.ref, owner: this, // Per-part source for the morfo's conditional aria-disabled - // (literal 'true' ← prop-truthy `disabled`). `renderProps()` resolves + // (literal 'true' ← prop-truthy `disabled`). `the render bag` resolves // it instead of the provider re-declaring role/aria. props: { disabled: () => this.opts.disabled.current @@ -603,7 +603,7 @@ export class DroppableProvider { this.runtimePart.assert({ // role, aria-disabled (literal+condition) are morfo-declared and // resolved against this part's registered `disabled` source. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, tabindex: this.provider.active && this.isAccepting ? 0 : -1, // aria-roledescription: morfo declares the `droppable` label key // ("Drop zone"); soma overrides with the dedicated role-description key @@ -676,7 +676,7 @@ export class DragPreviewProvider { this.runtimePart.assert({ // aria-hidden is morfo-declared (literal 'true'). data-active is // declared without a value (soma-owned); supply it. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-active': boolToEmptyStrOrUndef(this.provider.active !== null), style: this.style } as const) diff --git a/src/uix/soma/components/dropdown-menu/dropdown-menu-provider.svelte.ts b/src/uix/soma/components/dropdown-menu/dropdown-menu-provider.svelte.ts index 0bc4e0938..e6ca15884 100644 --- a/src/uix/soma/components/dropdown-menu/dropdown-menu-provider.svelte.ts +++ b/src/uix/soma/components/dropdown-menu/dropdown-menu-provider.svelte.ts @@ -225,7 +225,7 @@ export class MenuTriggerProvider { owner: this, // Per-part sources for the morfo's value-bound attrs: aria-expanded // (stateRef 'open') and aria-controls (partRef 'content', emitted only - // when content is present). With these, `renderProps()` resolves them + // when content is present). With these, `the render bag` resolves them // from the morfo instead of the provider re-declaring them. states: { open: () => this.provider.opts.open.current }, parts: { content: () => this.provider.contentId.current || undefined } @@ -257,8 +257,8 @@ export class MenuTriggerProvider { this.runtimePart.assert({ // Morfo-declared (type, aria-haspopup, aria-controls) resolved against // this part's registered sources — no longer re-declared here. - ...this.runtimePart.renderProps(), - // aria-expanded resolves from renderProps (morfo stateRef → string); + ...this.runtimePart.props, + // aria-expanded resolves from the render bag (morfo stateRef → string); // data-state has no morfo value source so soma derives it. 'data-state': getDataOpenClosed(this.provider.opts.open.current), // The root renders no element, so the trigger IS the in-place half and @@ -329,7 +329,7 @@ export class MenuContentProvider { this.provider.setContentRef(el); }, // aria-labelledby (partRef 'trigger', emitted only when the trigger is - // present) resolves from this source via renderProps. + // present) resolves from this source via the render bag. parts: { trigger: () => this.provider.triggerId.current || undefined } }); this.provider.contentId.current = opts.id.current; @@ -497,7 +497,7 @@ export class MenuContentProvider { this.runtimePart.assert({ // Morfo-declared (role, aria-labelledby, aria-orientation) resolved // against this part's registered sources. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // data-state has no morfo value source — soma derives it. 'data-state': getDataOpenClosed(this.provider.opts.open.current), ...this.floating.props, @@ -567,7 +567,7 @@ export class MenuItemProvider { ref: opts.ref, owner: this, // aria-disabled (literal 'true', emitted only when `disabled` is - // truthy) resolves from this source via renderProps. + // truthy) resolves from this source via the render bag. props: { disabled: () => this.opts.disabled.current } }); } @@ -597,7 +597,7 @@ export class MenuItemProvider { this.runtimePart.assert({ // Morfo-declared (role, aria-disabled) resolved against this part's // registered sources. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma owns: tabindex (native, computed from disabled + // accessibleWhenDisabled), data-disabled/data-text-value (morfo // declares them without a value source), handlers. @@ -643,7 +643,7 @@ export class MenuGroupProvider { owner: this, context: MenuGroupProvider.ctx, // aria-labelledby (partRef 'group-heading', emitted only when the - // heading is present) resolves from this source via renderProps. + // heading is present) resolves from this source via the render bag. parts: { 'group-heading': () => this.headingId.current || undefined } }); } @@ -652,7 +652,7 @@ export class MenuGroupProvider { this.runtimePart.assert({ // Morfo-declared (role, aria-labelledby) resolved against this part's // registered sources. - ...this.runtimePart.renderProps() + ...this.runtimePart.props } as const) ); } @@ -712,7 +712,7 @@ export class MenuSeparatorProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared (role, aria-orientation) — both literals. - ...this.runtimePart.renderProps() + ...this.runtimePart.props } as const) ); } @@ -785,7 +785,7 @@ export class MenuCheckboxItemProvider { owner: this, // Per-part sources: aria-checked (stateRef 'checked') resolves from the // EFFECTIVE checked state (group membership when grouped), aria-disabled - // (literal 'true' when `disabled` truthy) — both via renderProps. + // (literal 'true' when `disabled` truthy) — both via the render bag. states: { checked: () => this.effectiveChecked }, props: { disabled: () => this.opts.disabled.current } }); @@ -827,8 +827,8 @@ export class MenuCheckboxItemProvider { this.runtimePart.assert({ // Morfo-declared (role, aria-disabled) resolved against this part's // registered sources. - ...this.runtimePart.renderProps(), - // aria-checked resolves from renderProps (morfo stateRef → string). + ...this.runtimePart.props, + // aria-checked resolves from the render bag (morfo stateRef → string). // Soma owns: data-state (no morfo value source), tabindex/ // data-disabled/data-text-value (native or no-value), handlers. tabindex: @@ -877,7 +877,7 @@ export class MenuRadioGroupProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared (role) — literal. - ...this.runtimePart.renderProps() + ...this.runtimePart.props } as const) ); } @@ -914,7 +914,7 @@ export class MenuRadioItemProvider { owner: this, // Per-part sources: aria-checked (stateRef 'checked') resolves from the // computed isChecked (value === group value), aria-disabled (literal - // 'true' when `disabled` truthy) — both via renderProps. + // 'true' when `disabled` truthy) — both via the render bag. states: { checked: () => this.isChecked }, props: { disabled: () => this.opts.disabled.current } }); @@ -947,8 +947,8 @@ export class MenuRadioItemProvider { this.runtimePart.assert({ // Morfo-declared (role, aria-disabled) resolved against this part's // registered sources. - ...this.runtimePart.renderProps(), - // aria-checked resolves from renderProps (morfo stateRef → string). + ...this.runtimePart.props, + // aria-checked resolves from the render bag (morfo stateRef → string). // Soma owns: data-state (no morfo value source), tabindex/ // data-disabled/data-text-value (native or no-value), handlers. tabindex: @@ -1184,8 +1184,8 @@ export class MenuSubTriggerProvider { this.runtimePart.assert({ // Morfo-declared (role, aria-haspopup, aria-controls, aria-disabled) // resolved against this part's registered sources. - ...this.runtimePart.renderProps(), - // aria-expanded resolves from renderProps (morfo stateRef → string). + ...this.runtimePart.props, + // aria-expanded resolves from the render bag (morfo stateRef → string). // Soma owns: data-state (no morfo value source), tabindex/ // data-disabled/data-text-value (native or no-value), handlers. tabindex: @@ -1242,7 +1242,7 @@ export class MenuSubContentProvider { this.sub.setContentRef(el); }, // aria-labelledby (partRef 'sub-trigger', emitted only when present) - // resolves from this source via renderProps. + // resolves from this source via the render bag. parts: { 'sub-trigger': () => this.sub.subTriggerId.current || undefined } }); this.sub.contentId.current = opts.id.current; @@ -1341,7 +1341,7 @@ export class MenuSubContentProvider { this.runtimePart.assert({ // Morfo-declared (role, aria-labelledby, aria-orientation) resolved // against this part's registered sources. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // data-state has no morfo value source — soma derives it. 'data-state': getDataOpenClosed(this.sub.opts.open.current), // Programmatically focusable: keyboard open lands focus on the panel diff --git a/src/uix/soma/components/editable/editable-provider.svelte.test.ts b/src/uix/soma/components/editable/editable-provider.svelte.test.ts index d529421c3..ba232440c 100644 --- a/src/uix/soma/components/editable/editable-provider.svelte.test.ts +++ b/src/uix/soma/components/editable/editable-provider.svelte.test.ts @@ -281,7 +281,7 @@ describe('EditableProvider', () => { expect(opts.editing.current).toBe(true); expect(result.edit.props.disabled).toBe(true); // `data-editing` is morfo-declared and reaches props through the runtime's - // renderProps() spread — present at runtime, erased from the static type, + // render-bag spread — present at runtime, erased from the static type, // hence the indexed read through a cast. expect((result.edit.props as Record)['data-editing']).toBe(''); expect(result.submit.props).toMatchObject({ diff --git a/src/uix/soma/components/editable/editable-provider.svelte.ts b/src/uix/soma/components/editable/editable-provider.svelte.ts index fc3abffef..83da5b0d3 100644 --- a/src/uix/soma/components/editable/editable-provider.svelte.ts +++ b/src/uix/soma/components/editable/editable-provider.svelte.ts @@ -275,7 +275,7 @@ export class EditablePreviewProvider { this.runtimePart.assert({ // Morfo-declared: role=textbox, aria-readonly=true. data-placeholder- // shown/disabled/readonly are declared without a value (soma-owned). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma override: aria-label is the placeholder, removed when empty // (the morfo's raw propRef would emit '' instead of absent). 'aria-label': this.provider.opts.placeholder.current || undefined, @@ -360,7 +360,7 @@ export class EditableInputProvider { // Morfo-declared: aria-disabled/invalid (literal 'true' present-when- // prop-truthy). data-disabled/readonly/invalid are declared without a // value (soma-owned). Resolved against the component-level sources. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, hidden: !this.provider.isEditing || undefined, value: this.provider.draft, placeholder: this.provider.opts.placeholder.current || undefined, @@ -420,8 +420,8 @@ export class EditableEditTriggerProvider { this.runtimePart.assert({ // Morfo-declared: type=button, data-editing ← editing. data-disabled/ // readonly are declared without a value (soma-owned). - ...this.runtimePart.renderProps(), - // Naming default ships in `renderProps()` (consumerWins, A-85) — the + ...this.runtimePart.props, + // Naming default ships in `the render bag` (consumerWins, A-85) — the // morfo's commonRef resolves the same `#?common.buttons.*` id. 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), 'data-readonly': boolToEmptyStrOrUndef(this.provider.opts.readonly.current), @@ -468,8 +468,8 @@ export class EditableSubmitTriggerProvider { this.runtimePart.assert({ // Morfo-declared: type=button, data-editing ← editing. data-disabled/ // readonly are declared without a value (soma-owned). - ...this.runtimePart.renderProps(), - // Naming default ships in `renderProps()` (consumerWins, A-85) — the + ...this.runtimePart.props, + // Naming default ships in `the render bag` (consumerWins, A-85) — the // morfo's commonRef resolves the same `#?common.buttons.*` id. 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), 'data-readonly': boolToEmptyStrOrUndef(this.provider.opts.readonly.current), @@ -506,7 +506,7 @@ export class EditableHiddenInputProvider { this.runtimePart.assert({ // Morfo-declared: aria-hidden=true. Native form attrs the morfo does // not model stay soma-owned. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, type: 'hidden' as const, name: this.provider.opts.name.current || undefined, value: this.provider.opts.value.current, @@ -554,8 +554,8 @@ export class EditableCancelTriggerProvider { this.runtimePart.assert({ // Morfo-declared: type=button, data-editing ← editing. data-disabled/ // readonly are declared without a value (soma-owned). - ...this.runtimePart.renderProps(), - // Naming default ships in `renderProps()` (consumerWins, A-85) — the + ...this.runtimePart.props, + // Naming default ships in `the render bag` (consumerWins, A-85) — the // morfo's commonRef resolves the same `#?common.buttons.*` id. 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), 'data-readonly': boolToEmptyStrOrUndef(this.provider.opts.readonly.current), diff --git a/src/uix/soma/components/emoji-picker/emoji-picker-provider.svelte.ts b/src/uix/soma/components/emoji-picker/emoji-picker-provider.svelte.ts index a21dad945..e75f33f0a 100644 --- a/src/uix/soma/components/emoji-picker/emoji-picker-provider.svelte.ts +++ b/src/uix/soma/components/emoji-picker/emoji-picker-provider.svelte.ts @@ -184,7 +184,7 @@ export class EmojiPickerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-disabled': boolToEmptyStrOrUndef(this.opts.disabled.current) } as const) ); @@ -223,7 +223,7 @@ export class EmojiPickerTriggerProvider { // Morfo supplies type=button, aria-haspopup=dialog, aria-expanded (←open) // and the default translated aria-label. `data-state` comes from the // composed PopoverTrigger via mergeProps in the wrapper. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'aria-label': this.resolvedLabel, disabled: this.provider.opts.disabled.current || undefined, 'data-disabled': boolToEmptyStrOrUndef(this.provider.opts.disabled.current) @@ -256,7 +256,7 @@ export class EmojiPickerContentProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // The morfo declares data-state without a stateRef — soma supplies it. 'data-state': this.provider.opts.open.current ? 'open' : 'closed' } as const) @@ -374,6 +374,6 @@ export class EmojiPickerPreviewProvider { } readonly props = $derived.by(() => - this.runtimePart.assert({ ...this.runtimePart.renderProps() } as const) + this.runtimePart.assert({ ...this.runtimePart.props } as const) ); } diff --git a/src/uix/soma/components/field/field-provider.svelte.ts b/src/uix/soma/components/field/field-provider.svelte.ts index 3fd6b447c..409ae42f7 100644 --- a/src/uix/soma/components/field/field-provider.svelte.ts +++ b/src/uix/soma/components/field/field-provider.svelte.ts @@ -333,7 +333,7 @@ export class FieldInputProvider { ref: opts.ref, owner: this, // Per-part source for the morfo's `type` aria entry (the component-level - // sources don't carry it). With this, `renderProps()` resolves `type` + // sources don't carry it). With this, `the render bag` resolves `type` // from the morfo instead of the provider re-declaring it. props: { type: () => this.opts.type.current } }); @@ -382,7 +382,7 @@ export class FieldInputProvider { // Morfo-declared attrs (type, data-state, aria-labelledby, aria-invalid, // aria-busy, aria-required), resolved against this part's registered // sources — no longer re-declared in the provider. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-only: native form attrs + the value the component owns. value: this.opts.value.current, name: this.opts.name.current, @@ -500,8 +500,8 @@ export class FieldPrefixProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo declares aria-hidden — resolved by renderProps, not re-declared. - ...this.runtimePart.renderProps() + // Morfo declares aria-hidden — resolved by the render bag, not re-declared. + ...this.runtimePart.props } as const) ); } @@ -530,8 +530,8 @@ export class FieldSuffixProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo declares aria-hidden — resolved by renderProps, not re-declared. - ...this.runtimePart.renderProps() + // Morfo declares aria-hidden — resolved by the render bag, not re-declared. + ...this.runtimePart.props } as const) ); } diff --git a/src/uix/soma/components/file-upload/file-upload-provider.svelte.ts b/src/uix/soma/components/file-upload/file-upload-provider.svelte.ts index a17c0010c..44d6c3e98 100644 --- a/src/uix/soma/components/file-upload/file-upload-provider.svelte.ts +++ b/src/uix/soma/components/file-upload/file-upload-provider.svelte.ts @@ -394,7 +394,7 @@ export class FileUploadDropzoneProvider { this.runtimePart.assert({ // role, aria-label, aria-disabled are morfo-declared (resolved against // the part's `disabled` source) — no longer re-declared here. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, tabindex: this.provider.isDisabled ? -1 : 0, 'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled), 'data-dragging': boolToEmptyStrOrUndef(this.dragging), @@ -445,7 +445,7 @@ export class FileUploadTriggerProvider { this.runtimePart.assert({ // type, aria-label, aria-disabled are morfo-declared (resolved against // the part's `disabled` source) — no longer re-declared here. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, disabled: this.provider.isDisabled || undefined, 'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled), onclick: this.onclick @@ -536,7 +536,7 @@ export class FileUploadFileListProvider { this.runtimePart.assert({ // role + aria-label (morfo's `list` translationRef resolves to the same // idlangref FILE_UPLOAD_LANGS.LIST) are morfo-declared. - ...this.runtimePart.renderProps() + ...this.runtimePart.props } as const) ); } @@ -582,7 +582,7 @@ export class FileUploadItemProvider { this.runtimePart.assert({ // role is morfo-declared. data-file-name/type are morfo-declared WITHOUT // a value (soma owns the values). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-file-name': this.opts.file.current.name, 'data-file-type': this.opts.file.current.type || undefined } as const) @@ -738,7 +738,7 @@ export class FileUploadItemRemoveProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // type is morfo-declared. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, disabled: this.item.provider.isDisabled || undefined, // Soma override: the morfo's aria-label is the bare "Remove file" text; // the provider appends the file name for a per-item accessible label. @@ -790,7 +790,7 @@ export class FileUploadItemProgressProvider { this.runtimePart.assert({ // role, aria-valuenow (← percent source), aria-valuemin/max are // morfo-declared. data-value is morfo-declared WITHOUT a value. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma override: the morfo's aria-label is the bare file name; the // provider annotates it with the current percentage. 'aria-label': `${this.item.opts.file.current.name}: ${this.percent}%`, @@ -833,7 +833,7 @@ export class FileUploadClearTriggerProvider { this.runtimePart.assert({ // type + aria-label (morfo's `buttons.clear` commonRef resolves to the // same idlangref FILE_UPLOAD_LANGS.CLEAR) are morfo-declared. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, disabled: this.isDisabled || undefined, 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), onclick: this.onclick diff --git a/src/uix/soma/components/form/form-provider.svelte.ts b/src/uix/soma/components/form/form-provider.svelte.ts index f9f7ea525..54bbb32f6 100644 --- a/src/uix/soma/components/form/form-provider.svelte.ts +++ b/src/uix/soma/components/form/form-provider.svelte.ts @@ -200,7 +200,7 @@ export class FormSubmitProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // type, aria-busy (← isPending, ariaBoolean) are morfo-declared. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'aria-label': this.resolvedLabel, disabled: this.provider.isPending || undefined, // Morfo declares data-pending as presence-only (no value); soma owns @@ -258,7 +258,7 @@ export class FormResetProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // type is morfo-declared. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // aria-label honours the explicit `ariaLabel` prop (falls back to the // same `buttons.reset` translation the morfo declares). 'aria-label': this.resolvedLabel, @@ -325,7 +325,7 @@ export class FormErrorSummaryProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // role=alert, aria-live=polite are morfo-declared. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // hidden is a native attr the morfo does not model. hidden: this.isHidden || undefined, // Morfo declares data-hidden as presence-only (no value); soma owns diff --git a/src/uix/soma/components/gradient-picker/gradient-picker-provider.svelte.ts b/src/uix/soma/components/gradient-picker/gradient-picker-provider.svelte.ts index a829abff7..ce698a60c 100644 --- a/src/uix/soma/components/gradient-picker/gradient-picker-provider.svelte.ts +++ b/src/uix/soma/components/gradient-picker/gradient-picker-provider.svelte.ts @@ -165,7 +165,7 @@ export class GradientPickerProvider { } get props() { - return this.runtimePart.renderProps(); + return this.runtimePart.props; } } @@ -199,7 +199,7 @@ export class GradientPickerTriggerProvider { get props() { const ariaLabel = this.opts.ariaLabel.current; return { - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, ...(ariaLabel ? { 'aria-label': ariaLabel } : {}), disabled: this.provider.isDisabled || undefined, 'data-disabled': boolToEmptyStrOrUndef(this.provider.isDisabled), @@ -232,6 +232,6 @@ export class GradientPickerValueSwatchProvider { ); get props() { - return { ...this.runtimePart.renderProps(), style: this.backgroundStyle }; + return { ...this.runtimePart.props, style: this.backgroundStyle }; } } diff --git a/src/uix/soma/components/grid-list/grid-list-provider.svelte.test.ts b/src/uix/soma/components/grid-list/grid-list-provider.svelte.test.ts index 8989293c4..a5ae3f807 100644 --- a/src/uix/soma/components/grid-list/grid-list-provider.svelte.test.ts +++ b/src/uix/soma/components/grid-list/grid-list-provider.svelte.test.ts @@ -327,7 +327,7 @@ describe('GridListProvider', () => { }); // `role` is morfo-declared and reaches props through the runtime's - // renderProps() spread — present at runtime, erased from the static type, + // render-bag spread — present at runtime, erased from the static type, // hence the indexed read through a cast. expect((result.cell.props as Record).role).toBe('gridcell'); expect(result.checkbox.props).toMatchObject({ diff --git a/src/uix/soma/components/grid-list/grid-list-provider.svelte.ts b/src/uix/soma/components/grid-list/grid-list-provider.svelte.ts index 857f951b6..c3e7e1bd4 100644 --- a/src/uix/soma/components/grid-list/grid-list-provider.svelte.ts +++ b/src/uix/soma/components/grid-list/grid-list-provider.svelte.ts @@ -75,7 +75,7 @@ export class GridListProvider { context: GridListProvider.ctx, // Per-part sources for the morfo's conditional aria booleans // (aria-multiselectable/disabled/readonly/invalid/required). They - // are field-aware here, so renderProps() resolves the conditions + // are field-aware here, so the render bag resolves the conditions // against the same flags the provider previously hardcoded. props: { multiple: () => this.opts.selectionMode.current === 'multiple', @@ -447,12 +447,12 @@ export class GridListProvider { this.runtimePart.assert({ // Morfo-declared: role=grid + aria-multiselectable/disabled/readonly/ // invalid/required (conditional, resolved from this part's sources). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Raw, never `resolvedDir`: the rows are flex ROWS and the trailing cell is // pushed out with `margin-inline-start: auto` — both read this root's direction. // Soma-owned: aria-labelledby + the data-* flags are not value-backed // in the morfo, so soma supplies them. (aria-label is the morfo's - // naming default and rides `renderProps()` — consumer-first, A-85.) + // naming default and rides `the render bag` — consumer-first, A-85.) 'aria-labelledby': this.opts.ariaLabelledby.current, 'data-disabled': boolToEmptyStrOrUndef(this.isDisabled), 'data-readonly': boolToEmptyStrOrUndef(this.isReadonly), @@ -491,7 +491,7 @@ export class GridListRowProvider { id: opts.id, ref: opts.ref, owner: this, - // row-local disabled (includes provider disabled) → renderProps + // row-local disabled (includes provider disabled) → the render bag // resolves the morfo's conditional aria-disabled. props: { disabled: () => this.isDisabled } }); @@ -520,7 +520,7 @@ export class GridListRowProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: role=row + conditional aria-disabled. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: roving tabindex; aria-selected is removed in 'none' // mode (overrides the morfo's stateRef); the data-* are not // value-backed in the morfo, so soma supplies them. @@ -573,7 +573,7 @@ export class GridListCellProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo-declared: role=gridcell. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, onkeydown: this.onkeydown } as const) ); @@ -603,7 +603,7 @@ export class GridListSelectionCheckboxProvider { owner: this, // The morfo's conditional aria-disabled reads prop `disabled`; the // checkbox is non-interactive when the grid is disabled OR readonly, - // so feed the combined flag → renderProps resolves the condition. + // so feed the combined flag → the render bag resolves the condition. props: { disabled: () => this.provider.isDisabled || this.provider.isReadonly } }); } @@ -635,10 +635,10 @@ export class GridListSelectionCheckboxProvider { const checked = this.rowValue ? this.provider.isSelected(this.rowValue) : false; return this.runtimePart.assert({ // Morfo-declared: role=checkbox + conditional aria-disabled. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: native type=button; aria-checked is computed from the // enclosing row association; data-* are not value-backed in the morfo. - // (aria-label is the morfo's naming default and rides `renderProps()` + // (aria-label is the morfo's naming default and rides `the render bag` // — consumer-first, A-85.) type: 'button' as const, 'aria-checked': checked, diff --git a/src/uix/soma/components/knob/knob-provider.svelte.ts b/src/uix/soma/components/knob/knob-provider.svelte.ts index 493f718a8..6a73ec318 100644 --- a/src/uix/soma/components/knob/knob-provider.svelte.ts +++ b/src/uix/soma/components/knob/knob-provider.svelte.ts @@ -299,7 +299,7 @@ export class KnobControlProvider { id: opts.id, ref: opts.ref, owner: this - // no syncAttrs: renderProps() path — we override aria-valuetext. + // no syncAttrs: render-bag path — we override aria-valuetext. }); } @@ -350,7 +350,7 @@ export class KnobControlProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - ...this.runtimePart.renderProps(), // role=slider, aria-value*, data-* + ...this.runtimePart.props, // role=slider, aria-value*, data-* tabindex: this.provider.opts.disabled.current ? -1 : 0, 'aria-valuetext': this.formatValue(), // soma owns the *value* (formatting) 'data-dragging': boolToEmptyStrOrUndef(this.provider.dragging), diff --git a/src/uix/soma/components/link-preview/link-preview-provider.svelte.test.ts b/src/uix/soma/components/link-preview/link-preview-provider.svelte.test.ts index c07f3a8bb..c85e2fbe9 100644 --- a/src/uix/soma/components/link-preview/link-preview-provider.svelte.test.ts +++ b/src/uix/soma/components/link-preview/link-preview-provider.svelte.test.ts @@ -201,7 +201,7 @@ describe('LinkPreviewProvider', () => { expect(result.content.isPresent).toBe(true); expect(result.content.props).toMatchObject({ id: 'link-preview-content', - // renderProps() emits the morfo-declared ARIA literal as the canonical + // the render bag emits the morfo-declared ARIA literal as the canonical // ARIA STRING "true" (DOM-identical to the old boolean shape). 'aria-hidden': 'true', 'data-state': 'open', diff --git a/src/uix/soma/components/link-preview/link-preview-provider.svelte.ts b/src/uix/soma/components/link-preview/link-preview-provider.svelte.ts index b5d8a7a35..b43fcfb06 100644 --- a/src/uix/soma/components/link-preview/link-preview-provider.svelte.ts +++ b/src/uix/soma/components/link-preview/link-preview-provider.svelte.ts @@ -235,7 +235,7 @@ export class LinkPreviewTriggerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // Morfo declares `data-state` value-less (soma owns the open/closed value). - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-state': getDataOpenClosed(this.provider.opts.open.current), onpointerenter: this.onpointerenter, onpointerleave: this.onpointerleave @@ -358,7 +358,7 @@ export class LinkPreviewContentProvider { // LinkPreview is intentionally hidden from assistive tech (study §2.3). // No `role` → the preview is skipped entirely by screen readers instead // of announcing a phantom dialog that is then suppressed by aria-hidden. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-state': getDataOpenClosed(this.provider.opts.open.current), ...this.floating.props, ...this.provider.contentPresence.transitionAttrs, @@ -396,7 +396,7 @@ export class LinkPreviewArrowProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, ...this.floatingArrow.props } as const) ); diff --git a/src/uix/soma/components/listbox/listbox-provider.svelte.ts b/src/uix/soma/components/listbox/listbox-provider.svelte.ts index c11c436be..1141528e3 100644 --- a/src/uix/soma/components/listbox/listbox-provider.svelte.ts +++ b/src/uix/soma/components/listbox/listbox-provider.svelte.ts @@ -79,7 +79,7 @@ export class ListboxProvider { ref: opts.ref, owner: this, context: ListboxProvider.ctx, - // Per-part sources so `renderProps()` resolves the morfo's role + + // Per-part sources so `the render bag` resolves the morfo's role + // aria-orientation/multiselectable/disabled/readonly/required/invalid // instead of the provider re-declaring them. `multiple` maps the // morfo's boolean prop to this component's `selectionMode`. @@ -347,9 +347,9 @@ export class ListboxProvider { // Morfo-declared (role, aria-orientation/multiselectable/disabled/ // readonly/required/invalid, default aria-label) resolved against this // part's registered sources — no longer re-declared here. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-owned: aria-labelledby is not morfo-modeled. (aria-label is the - // morfo's naming default and rides `renderProps()` — consumer-first, + // morfo's naming default and rides `the render bag` — consumer-first, // A-85 — suppressed by the morfo when this one is set.) 'aria-labelledby': this.opts.ariaLabelledby.current, // Soma-owned data-* (morfo declares them without a value source). @@ -399,7 +399,7 @@ export class ListboxItemProvider { ref: opts.ref, owner: this, context: ListboxItemProvider.ctx, - // Per-part sources so `renderProps()` resolves the morfo's aria-selected + // Per-part sources so `the render bag` resolves the morfo's aria-selected // (stateRef 'selected') and aria-disabled (prop-truthy 'disabled'). states: { selected: () => this.isSelected }, props: { disabled: () => this.isDisabled } @@ -451,7 +451,7 @@ export class ListboxItemProvider { this.runtimePart.assert({ // Morfo-declared (role, aria-selected, aria-disabled) resolved against // this item's registered sources — no longer re-declared here. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, tabindex: this.isRovingTarget ? 0 : -1, // Soma-owned data-* (morfo declares them without a value source). 'data-state': this.isSelected ? 'selected' : 'unselected', @@ -499,8 +499,8 @@ export class ListboxItemIndicatorProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo-declared aria-hidden resolved from renderProps. - ...this.runtimePart.renderProps(), + // Morfo-declared aria-hidden resolved from the render bag. + ...this.runtimePart.props, // Soma-owned data-* (morfo declares no data on this part). 'data-state': this.item.isSelected ? 'selected' : 'unselected' } as const) @@ -537,7 +537,7 @@ export class ListboxGroupProvider { ref: opts.ref, owner: this, context: ListboxGroupProvider.ctx, - // Per-part source so `renderProps()` resolves the morfo's + // Per-part source so `the render bag` resolves the morfo's // aria-labelledby (partRef 'group-label' + part-present condition) // against the registered GroupLabel id. parts: { 'group-label': () => this.labelId || undefined } @@ -546,8 +546,8 @@ export class ListboxGroupProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo-declared role + aria-labelledby resolved from renderProps. - ...this.runtimePart.renderProps() + // Morfo-declared role + aria-labelledby resolved from the render bag. + ...this.runtimePart.props } as const) ); } diff --git a/src/uix/soma/components/mask-field/mask-field-provider.svelte.ts b/src/uix/soma/components/mask-field/mask-field-provider.svelte.ts index a2af6540e..75d24055a 100644 --- a/src/uix/soma/components/mask-field/mask-field-provider.svelte.ts +++ b/src/uix/soma/components/mask-field/mask-field-provider.svelte.ts @@ -343,7 +343,7 @@ export class MaskFieldInputProvider { // Morfo-declared attrs (role=textbox, data-disabled/readonly/invalid, // aria-invalid/required as booleans, aria-label default) resolved // against the component-level sources. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, value: p.opts.value.current, inputmode: p.inputMode, placeholder: p.opts.placeholder.current, diff --git a/src/uix/soma/components/media-player/media-player-provider.svelte.test.ts b/src/uix/soma/components/media-player/media-player-provider.svelte.test.ts index 1c8777e43..262529916 100644 --- a/src/uix/soma/components/media-player/media-player-provider.svelte.test.ts +++ b/src/uix/soma/components/media-player/media-player-provider.svelte.test.ts @@ -502,7 +502,7 @@ describe('MediaPlayerProvider', () => { fake.emit('play', { paused: false }); fake.emit('volumechange', { muted: true }); - // These attrs are now resolved by renderProps (from the registered label + // These attrs are now resolved by the render bag (from the registered label // prop + `on` state sources), so they aren't statically-typed keys on the // props bag — assert via toMatchObject like the initial snapshot above. expect(result.play.props).toMatchObject({ 'data-state': 'playing', 'aria-label': 'Pause' }); diff --git a/src/uix/soma/components/media-player/media-player-provider.svelte.ts b/src/uix/soma/components/media-player/media-player-provider.svelte.ts index 46390cd8f..426f94a78 100644 --- a/src/uix/soma/components/media-player/media-player-provider.svelte.ts +++ b/src/uix/soma/components/media-player/media-player-provider.svelte.ts @@ -805,7 +805,7 @@ export class MediaPlayerProvider { // the chrome inherits. Without it the `dir` PROP reached only the composed // Sliders and the player laid its own controls out the other way round. // data-dir is contract-only in the morfo (declared with `values`, no - // resolvable `value`), so renderProps doesn't emit it — soma owns it. + // resolvable `value`), so the render bag doesn't emit it — soma owns it. 'data-dir': this.resolvedDir, onkeydown: this.onkeydown } as const) @@ -843,9 +843,9 @@ export class MediaPlayerMediaProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // data-media is morfo-declared (propRef('media')), resolved by renderProps + // data-media is morfo-declared (propRef('media')), resolved by the render bag // against the component-level `media` source. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Tap-to-toggle-play gesture on the video surface (YouTube convention); // the audio element is 0-height, so this only fires for video. onclick: () => this.provider.togglePlay() @@ -877,8 +877,8 @@ export class MediaPlayerPosterProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // aria-hidden is morfo-declared (literal), resolved by renderProps. - ...this.runtimePart.renderProps(), + // aria-hidden is morfo-declared (literal), resolved by the render bag. + ...this.runtimePart.props, // data-hidden is contract-only in the morfo (presence flag) — soma owns it. 'data-hidden': boolToEmptyStrOrUndef(this.provider.started) } as const) @@ -909,8 +909,8 @@ export class MediaPlayerBufferingIndicatorProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // aria-hidden is morfo-declared (literal), resolved by renderProps. - ...this.runtimePart.renderProps() + // aria-hidden is morfo-declared (literal), resolved by the render bag. + ...this.runtimePart.props } as const) ); } @@ -963,8 +963,8 @@ export class MediaPlayerCaptionsProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // aria-hidden is morfo-declared (literal), resolved by renderProps. - ...this.runtimePart.renderProps(), + // aria-hidden is morfo-declared (literal), resolved by the render bag. + ...this.runtimePart.props, // data-captions is contract-only in the morfo (presence flag) — soma // owns it. Present only while there is something to read. 'data-captions': boolToEmptyStrOrUndef(this.provider.captionsOn) @@ -1021,7 +1021,7 @@ export class MediaPlayerPlayButtonProvider { id: opts.id, ref: opts.ref, owner: this, - // Source so renderProps resolves the morfo's aria-label (propRef('label')). + // Source so the render bag resolves the morfo's aria-label (propRef('label')). props: { label: () => this.label } }); } @@ -1032,8 +1032,8 @@ export class MediaPlayerPlayButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // type/role/aria-label/aria-keyshortcuts are morfo-declared, resolved by renderProps. - ...this.runtimePart.renderProps(), + // type/role/aria-label/aria-keyshortcuts are morfo-declared, resolved by the render bag. + ...this.runtimePart.props, // data-state is contract-only in the morfo (no resolvable value) — soma owns it. 'data-state': this.provider.paused ? 'paused' : 'playing', onclick: () => this.provider.togglePlay() @@ -1061,7 +1061,7 @@ export class MediaPlayerSeekButtonProvider { id: opts.id, ref: opts.ref, owner: this, - // Sources so renderProps resolves aria-label (propRef('label')) and + // Sources so the render bag resolves aria-label (propRef('label')) and // data-direction (propRef('direction')). props: { label: () => this.label, @@ -1083,8 +1083,8 @@ export class MediaPlayerSeekButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // type/role/aria-label/data-direction are morfo-declared, resolved by renderProps. - ...this.runtimePart.renderProps(), + // type/role/aria-label/data-direction are morfo-declared, resolved by the render bag. + ...this.runtimePart.props, onclick: this.onclick } as const) ); @@ -1118,7 +1118,7 @@ export class MediaPlayerRestartButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-disabled': boolToEmptyStrOrUndef(this.opts.disabled.current), disabled: this.opts.disabled.current || undefined, // Seeking to 0 also clears the skip flag through `seek` — a restart is @@ -1173,7 +1173,7 @@ export class MediaPlayerSkipButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, 'data-disabled': boolToEmptyStrOrUndef(this.unavailable), disabled: this.unavailable || undefined, onclick: () => { @@ -1204,7 +1204,7 @@ export class MediaPlayerMuteButtonProvider { id: opts.id, ref: opts.ref, owner: this, - // Sources so renderProps resolves aria-label (propRef('label')) and the + // Sources so the render bag resolves aria-label (propRef('label')) and the // on-state pair (data-state + aria-pressed via stateRef('on')). props: { label: () => this.label }, states: { on: () => this.provider.muted } @@ -1233,8 +1233,8 @@ export class MediaPlayerMuteButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // type/role/aria-label/aria-keyshortcuts/aria-pressed/data-state are - // morfo-declared, resolved by renderProps. - ...this.runtimePart.renderProps(), + // morfo-declared, resolved by the render bag. + ...this.runtimePart.props, // data-level is contract-only in the morfo (an enum with no resolvable // source) — soma owns the value. 'data-level': this.level, @@ -1262,7 +1262,7 @@ export class MediaPlayerFullscreenButtonProvider { id: opts.id, ref: opts.ref, owner: this, - // Sources so renderProps resolves aria-label (propRef('label')) and the + // Sources so the render bag resolves aria-label (propRef('label')) and the // on-state pair (data-state + aria-pressed via stateRef('on')). props: { label: () => this.label }, states: { on: () => this.provider.fullscreen } @@ -1278,8 +1278,8 @@ export class MediaPlayerFullscreenButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // type/role/aria-label/aria-keyshortcuts/aria-pressed/data-state are - // morfo-declared, resolved by renderProps. - ...this.runtimePart.renderProps(), + // morfo-declared, resolved by the render bag. + ...this.runtimePart.props, onclick: () => void this.provider.toggleFullscreen() } as const) ); @@ -1305,7 +1305,7 @@ export class MediaPlayerTimeProvider { id: opts.id, ref: opts.ref, owner: this, - // Source so renderProps resolves the morfo's data-type (propRef('type')). + // Source so the render bag resolves the morfo's data-type (propRef('type')). props: { type: () => this.opts.type.current } }); } @@ -1323,8 +1323,8 @@ export class MediaPlayerTimeProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // data-type is morfo-declared (propRef('type')), resolved by renderProps. - ...this.runtimePart.renderProps() + // data-type is morfo-declared (propRef('type')), resolved by the render bag. + ...this.runtimePart.props } as const) ); } @@ -1421,7 +1421,7 @@ export class MediaPlayerPipButtonProvider { id: opts.id, ref: opts.ref, owner: this, - // Sources so renderProps resolves aria-label (propRef('label')) and the + // Sources so the render bag resolves aria-label (propRef('label')) and the // on-state pair (data-state + aria-pressed via stateRef('on')). props: { label: () => this.label }, states: { on: () => this.provider.pip } @@ -1435,8 +1435,8 @@ export class MediaPlayerPipButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // type/role/aria-label/aria-pressed/data-state are morfo-declared, - // resolved by renderProps. - ...this.runtimePart.renderProps(), + // resolved by the render bag. + ...this.runtimePart.props, onclick: () => void this.provider.togglePip() } as const) ); @@ -1462,7 +1462,7 @@ export class MediaPlayerCaptionButtonProvider { id: opts.id, ref: opts.ref, owner: this, - // Sources so renderProps resolves aria-label (propRef('label')), the + // Sources so the render bag resolves aria-label (propRef('label')), the // on-state pair (data-state + aria-pressed via stateRef('on')) and the // prop-falsy gate that sheds aria-pressed on a menu-trigger composition. props: { label: () => this.label, menuTrigger: () => this.opts.menuTrigger.current }, @@ -1477,8 +1477,8 @@ export class MediaPlayerCaptionButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ // type/role/aria-label/aria-keyshortcuts/aria-pressed/data-state are - // morfo-declared, resolved by renderProps. - ...this.runtimePart.renderProps(), + // morfo-declared, resolved by the render bag. + ...this.runtimePart.props, // data-disabled is contract-only in the morfo (presence flag) — soma owns // it; `disabled` is the native attr. Disabled until the media exposes at // least one caption/subtitle track. @@ -1641,7 +1641,7 @@ export class MediaPlayerRateButtonProvider { id: opts.id, ref: opts.ref, owner: this, - // Sources so renderProps resolves aria-label (propRef('label')) and + // Sources so the render bag resolves aria-label (propRef('label')) and // data-rate (propRef('rate')). props: { label: () => this.label, @@ -1654,8 +1654,8 @@ export class MediaPlayerRateButtonProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // type/aria-label/data-rate are morfo-declared, resolved by renderProps. - ...this.runtimePart.renderProps(), + // type/aria-label/data-rate are morfo-declared, resolved by the render bag. + ...this.runtimePart.props, onclick: () => this.provider.cycleRate(1) } as const) ); @@ -1686,8 +1686,8 @@ export class MediaPlayerLiveIndicatorProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // aria-hidden is morfo-declared (literal), resolved by renderProps. - ...this.runtimePart.renderProps(), + // aria-hidden is morfo-declared (literal), resolved by the render bag. + ...this.runtimePart.props, // data-live is contract-only in the morfo (presence flag) — soma owns it. 'data-live': boolToEmptyStrOrUndef(this.provider.live) } as const) diff --git a/src/uix/soma/components/menubar/menubar-provider.svelte.test.ts b/src/uix/soma/components/menubar/menubar-provider.svelte.test.ts index 2f360884a..d871bfa98 100644 --- a/src/uix/soma/components/menubar/menubar-provider.svelte.test.ts +++ b/src/uix/soma/components/menubar/menubar-provider.svelte.test.ts @@ -135,7 +135,7 @@ describe('MenubarProvider', () => { type: 'button', role: 'menuitem', 'aria-haspopup': 'menu', - // renderProps() resolves aria-expanded (stateRef 'open') as the ARIA + // the render bag resolves aria-expanded (stateRef 'open') as the ARIA // STRING, not a JS boolean — DOM-identical, correct ARIA shape. 'aria-expanded': 'false', 'data-state': 'closed', @@ -148,7 +148,7 @@ describe('MenubarProvider', () => { // (composition IS the declaration); sibling menu entries keep "menu". result.edit.menu.surface = 'panel'; flushSync(); - // aria-haspopup is a morfo-declared attr (resolved at runtime via renderProps), + // aria-haspopup is a morfo-declared attr (resolved at runtime via the render bag), // so it isn't a static key of the props type — index through a record cast. expect((result.edit.trigger.props as Record)['aria-haspopup']).toBe('dialog'); expect((result.file.trigger.props as Record)['aria-haspopup']).toBe('menu'); @@ -161,7 +161,7 @@ describe('MenubarProvider', () => { expect(opts.value.current).toBe('file'); expect(result.file.openChange).toHaveBeenCalledWith(true); expect(result.file.trigger.props).toMatchObject({ - // renderProps() resolves aria-expanded (stateRef 'open') as the ARIA + // the render bag resolves aria-expanded (stateRef 'open') as the ARIA // STRING, not a JS boolean — DOM-identical, correct ARIA shape. 'aria-expanded': 'true', 'aria-controls': 'menubar-file-content', diff --git a/src/uix/soma/components/menubar/menubar-provider.svelte.ts b/src/uix/soma/components/menubar/menubar-provider.svelte.ts index 187b30d89..296faf2b4 100644 --- a/src/uix/soma/components/menubar/menubar-provider.svelte.ts +++ b/src/uix/soma/components/menubar/menubar-provider.svelte.ts @@ -217,8 +217,8 @@ export class MenubarProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // role + aria-orientation are morfo-declared (resolved by renderProps). - ...this.runtimePart.renderProps(), + // role + aria-orientation are morfo-declared (resolved by the render bag). + ...this.runtimePart.props, // Soma-only: writing direction the morfo does not model. } as const) ); @@ -386,7 +386,7 @@ export class MenubarTriggerProvider { ref: opts.ref, owner: this, // Per-part state source for the morfo's `aria-expanded` (stateRef 'open'). - // With it, `renderProps()` resolves aria-expanded instead of the + // With it, `the render bag` resolves aria-expanded instead of the // provider re-declaring it. states: { open: () => this.isOpen }, // The morfo's `aria-haspopup` is a propRef: 'menu' for a Content @@ -490,11 +490,11 @@ export class MenubarTriggerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo-declared (resolved by renderProps): type, role, aria-haspopup, + // Morfo-declared (resolved by the render bag): type, role, aria-haspopup, // aria-expanded (← 'open' state). data-state / data-disabled / // data-highlighted / data-menubar-value are declared WITHOUT a value // source — soma owns them, so they stay below the spread. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // Soma-only: aria-controls (not modelled by the morfo) + the value-less // data-* markers + native form attr + handlers. 'aria-controls': this.isOpen ? this.menu.menu.contentId.current || undefined : undefined, @@ -719,8 +719,8 @@ export class MenubarPanelContentProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // Morfo-declared (role=dialog, aria-labelledby) via renderProps. - ...this.runtimePart.renderProps(), + // Morfo-declared (role=dialog, aria-labelledby) via the render bag. + ...this.runtimePart.props, // data-state has no morfo value source — soma derives it. 'data-state': getDataOpenClosed(this.menu.isOpen), // Mega-panel marker — the recipe caps the intrinsic size from it. diff --git a/src/uix/soma/components/month-grid/month-grid-provider.svelte.ts b/src/uix/soma/components/month-grid/month-grid-provider.svelte.ts index 2ba064cdf..33febd2a6 100644 --- a/src/uix/soma/components/month-grid/month-grid-provider.svelte.ts +++ b/src/uix/soma/components/month-grid/month-grid-provider.svelte.ts @@ -108,7 +108,7 @@ export class MonthGridProvider { owner: this, context: MonthGridProvider.ctx, // Source for the morfo's `aria-disabled` (propRef('disabled')) so - // renderProps() resolves it instead of the provider re-declaring it. + // the render bag resolves it instead of the provider re-declaring it. props: { disabled: () => opts.disabled.current } }); // Initial focused month follows selection or today. @@ -302,8 +302,8 @@ export class MonthGridProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - // role + aria-disabled are morfo-declared, resolved via renderProps. - ...this.runtimePart.renderProps(), + // role + aria-disabled are morfo-declared, resolved via the render bag. + ...this.runtimePart.props, // Computed accessible name (ariaLabel + heading year) — overrides the // morfo's raw `ariaLabel` propRef (which the provider doesn't source). 'aria-label': this.fullLabel, @@ -394,8 +394,8 @@ abstract class MonthGridPaginationButtonProvider { const disabled = this.disabled(); return this.runtimePart.assert({ // type, aria-label (commonRef month-grid.prev/next-year) and - // aria-disabled are morfo-declared, resolved via renderProps. - ...this.runtimePart.renderProps(), + // aria-disabled are morfo-declared, resolved via the render bag. + ...this.runtimePart.props, 'data-disabled': boolToEmptyStrOrUndef(disabled), disabled, onclick: this.onclick @@ -458,7 +458,7 @@ export class MonthGridGridProvider { ref: opts.ref, owner: this, // Sources for the morfo's `aria-readonly`/`aria-disabled` - // (propRef('readonly')/propRef('disabled')) so renderProps resolves them. + // (propRef('readonly')/propRef('disabled')) so the render bag resolves them. props: { readonly: () => this.provider.opts.readonly.current, disabled: () => this.provider.opts.disabled.current @@ -470,8 +470,8 @@ export class MonthGridGridProvider { const headingId = this.opts.labelledBy.current ?? this.provider.headingId.current; return this.runtimePart.assert({ // role, aria-readonly and aria-disabled are morfo-declared, resolved - // via renderProps. - ...this.runtimePart.renderProps(), + // via the render bag. + ...this.runtimePart.props, // Computed: honours the `labelledBy` prop override + the registered // heading id (overrides the morfo's raw partRef('heading')). 'aria-labelledby': headingId || undefined @@ -545,8 +545,8 @@ export class MonthGridCellProvider { const tabindex = this.focused && !this.disabled ? 0 : -1; return this.runtimePart.assert({ // role, aria-selected and aria-disabled are morfo-declared, resolved - // via renderProps. - ...this.runtimePart.renderProps(), + // via the render bag. + ...this.runtimePart.props, // Native button type (the morfo doesn't model it on the cell). type: 'button' as const, tabindex, diff --git a/src/uix/soma/components/natural-time-picker/natural-time-picker-provider.svelte.ts b/src/uix/soma/components/natural-time-picker/natural-time-picker-provider.svelte.ts index 4e98afcf7..370525295 100644 --- a/src/uix/soma/components/natural-time-picker/natural-time-picker-provider.svelte.ts +++ b/src/uix/soma/components/natural-time-picker/natural-time-picker-provider.svelte.ts @@ -245,7 +245,7 @@ export class NaturalTimePickerProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // The in-place half paints from this: the trigger's `text-align: start` // and its icon's `margin-inline-start: auto`, plus the inline Panel's own // auto inline-start margins. Raw, per the note on `resolvedDir` above — diff --git a/src/uix/soma/components/nav-tree/nav-tree-provider.svelte.ts b/src/uix/soma/components/nav-tree/nav-tree-provider.svelte.ts index 96d1b06dc..17d8fa7a2 100644 --- a/src/uix/soma/components/nav-tree/nav-tree-provider.svelte.ts +++ b/src/uix/soma/components/nav-tree/nav-tree-provider.svelte.ts @@ -211,7 +211,7 @@ export class NavTreeProvider { // Raw, not `resolvedDir`: no assertion means no attribute (direction contract). } as const) ); - readonly listProps = $derived.by(() => this.listPart.renderProps()); + readonly listProps = $derived.by(() => this.listPart.props); } // ── Item (per node) ──────────────────────────────────────────────────────────── @@ -333,9 +333,9 @@ export class NavTreeItemProvider { void this.runtime.trigger(this.expanded ? 'emerge-collapse' : 'emerge-expand'); } - readonly itemProps = $derived.by(() => this.itemPart.renderProps()); + readonly itemProps = $derived.by(() => this.itemPart.props); readonly linkProps = $derived.by(() => ({ - ...this.linkPart.renderProps(), + ...this.linkPart.props, // A disabled node keeps its row but loses the destination: no `href` means // nothing to follow by pointer, keyboard or context menu (a CSS-only // `pointer-events: none` would still let Enter and «open in new tab» @@ -343,12 +343,12 @@ export class NavTreeItemProvider { href: this.disabled ? undefined : this.opts.item.current.node.href })); readonly triggerProps = $derived.by(() => ({ - ...this.triggerPart.renderProps(), + ...this.triggerPart.props, onclick: () => this.toggle() })); - readonly badgeProps = $derived.by(() => this.badgePart.renderProps()); + readonly badgeProps = $derived.by(() => this.badgePart.props); readonly groupProps = $derived.by(() => ({ - ...this.groupPart.renderProps(), + ...this.groupPart.props, hidden: this.expanded ? undefined : true })); diff --git a/src/uix/soma/components/navigation-menu/navigation-menu-provider.svelte.ts b/src/uix/soma/components/navigation-menu/navigation-menu-provider.svelte.ts index 9ea36a111..b0c586d77 100644 --- a/src/uix/soma/components/navigation-menu/navigation-menu-provider.svelte.ts +++ b/src/uix/soma/components/navigation-menu/navigation-menu-provider.svelte.ts @@ -363,7 +363,7 @@ export class NavigationMenuProvider { readonly props = $derived.by(() => this.runtimePart.assert({ - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // dir is soma-resolved (prop → prefs → 'ltr'); the morfo doesn't model // it. data-orientation is a morfo enum without a value source, so soma // supplies the value. @@ -393,7 +393,7 @@ export class NavigationMenuListProvider { ref: opts.ref, owner: this, // Per-part source for the morfo's `aria-orientation` - // (propRef:orientation). renderProps resolves it instead of the + // (propRef:orientation). the render bag resolves it instead of the // provider re-declaring the attr. props: { orientation: () => this.provider.opts.orientation.current @@ -406,7 +406,7 @@ export class NavigationMenuListProvider { // aria-orientation (propRef:orientation) is morfo-declared, resolved // against the registered source above. data-orientation is a morfo // enum without a value source, so soma supplies it. - ...this.runtimePart.renderProps(), + ...this.runtimePart.props, // No naming here: the accessible name is the LANDMARK's and lives on // the `