refactor(soma): de-dup spin-field triggers + scrubbers via renderProps

Finishes the spin-field family. The 4 triggers + 2 scrubbers (number-field +
css-field) now spread renderProps() and add only soma-specific extras.

- Scrubbers: role + aria-hidden + data-spin-field-scrubber now morfo-sourced;
  soma keeps data-scrubbing/disabled, cursor style, pointer handlers.
- Triggers: type + aria-label + data-spin-field + aria-disabled now morfo-
  sourced. The trigger morfos' aria-disabled moves from literal-'true'+condition
  to propRef('disabled') + ariaBoolean (the de-facto always-emit shape); each
  trigger registers its own `disabled` source (includes atMax/atMin) so
  renderProps resolves it. aria-label resolves from the morfo's
  commonRef('buttons.increment/decrement') — the SAME idlangref the provider
  used, so the string is unchanged ("Aumentar"/"Disminuir" in es).
- Dropped the now-orphaned NUMBER_FIELD_LANGS + boolToStr imports.

Verified in browser for all parts (type/aria-label/aria-disabled/role/aria-hidden
identical) + soma/morfo tests 95/95 (2 pre-existing palabras contract failures
only). The entire spin-field family is now morfo-sourced — zero re-declared attrs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
active-uix
dev 4 months ago
parent 75e50becf7
commit c44a259916

@ -158,9 +158,9 @@ export const cssFieldMorfo = {
},
{
attr: 'aria-disabled',
value: v.literal('true'),
value: v.propRef('disabled'),
severity: 'optional',
condition: { when: 'prop-truthy', prop: 'disabled' }
ariaBoolean: true
}
]
},
@ -185,9 +185,9 @@ export const cssFieldMorfo = {
},
{
attr: 'aria-disabled',
value: v.literal('true'),
value: v.propRef('disabled'),
severity: 'optional',
condition: { when: 'prop-truthy', prop: 'disabled' }
ariaBoolean: true
}
]
},

@ -150,9 +150,9 @@ export const numberFieldMorfo = {
},
{
attr: 'aria-disabled',
value: v.literal('true'),
value: v.propRef('disabled'),
severity: 'optional',
condition: { when: 'prop-truthy', prop: 'disabled' }
ariaBoolean: true
}
]
},
@ -177,9 +177,9 @@ export const numberFieldMorfo = {
},
{
attr: 'aria-disabled',
value: v.literal('true'),
value: v.propRef('disabled'),
severity: 'optional',
condition: { when: 'prop-truthy', prop: 'disabled' }
ariaBoolean: true
}
]
},

@ -1,6 +1,6 @@
import { watch } from 'runed';
import { context, type WithRefOpts } from '../../provider';
import { boolToEmptyStrOrUndef, boolToStr, isHTMLElement } from '$adom';
import { boolToEmptyStrOrUndef, isHTMLElement } from '$adom';
import { readableActive, type Active, type ActiveProps, type StateProps } from '$libs/reactive';
import type {
OnChangeFn,
@ -586,7 +586,9 @@ export class CssFieldIncrementTriggerProvider {
this.runtimePart = this.provider.runtime.part('increment-trigger', {
id: opts.id,
ref: opts.ref,
owner: this
owner: this,
// trigger-local disabled (includes atMax) → renderProps resolves aria-disabled
props: { disabled: () => this.isDisabled }
});
}
@ -609,13 +611,10 @@ export class CssFieldIncrementTriggerProvider {
readonly props = $derived.by(() =>
this.runtimePart.assert({
...this.runtimePart.props,
type: 'button' as const,
// type, aria-label, data-spin-field, aria-disabled are morfo-declared.
...this.runtimePart.renderProps(),
tabindex: -1,
disabled: this.isDisabled || undefined,
'aria-label': this.provider.soma.langs.ts(CSS_FIELD_LANGS.INCREMENT),
'data-spin-field-increment-trigger': '',
'aria-disabled': boolToStr(this.isDisabled),
'data-disabled': boolToEmptyStrOrUndef(this.isDisabled),
onclick: this.handleClick,
onpointerdown: this.handlePointerDown,
@ -653,7 +652,9 @@ export class CssFieldDecrementTriggerProvider {
this.runtimePart = this.provider.runtime.part('decrement-trigger', {
id: opts.id,
ref: opts.ref,
owner: this
owner: this,
// trigger-local disabled (includes atMin) → renderProps resolves aria-disabled
props: { disabled: () => this.isDisabled }
});
}
@ -676,13 +677,10 @@ export class CssFieldDecrementTriggerProvider {
readonly props = $derived.by(() =>
this.runtimePart.assert({
...this.runtimePart.props,
type: 'button' as const,
// type, aria-label, data-spin-field, aria-disabled are morfo-declared.
...this.runtimePart.renderProps(),
tabindex: -1,
disabled: this.isDisabled || undefined,
'aria-label': this.provider.soma.langs.ts(CSS_FIELD_LANGS.DECREMENT),
'data-spin-field-decrement-trigger': '',
'aria-disabled': boolToStr(this.isDisabled),
'data-disabled': boolToEmptyStrOrUndef(this.isDisabled),
onclick: this.handleClick,
onpointerdown: this.handlePointerDown,
@ -810,10 +808,8 @@ export class CssFieldScrubberProvider {
readonly props = $derived.by(() =>
this.runtimePart.assert({
...this.runtimePart.props,
role: 'presentation' as const,
'aria-hidden': true as const,
'data-spin-field-scrubber': '',
// role, aria-hidden, data-spin-field-scrubber are morfo-declared.
...this.runtimePart.renderProps(),
'data-scrubbing': boolToEmptyStrOrUndef(this.provider.scrubbing),
'data-disabled': boolToEmptyStrOrUndef(this.isDisabled),
style: {

@ -1,6 +1,6 @@
import { watch } from 'runed';
import { context, type WithRefOpts } from '../../provider';
import { boolToEmptyStrOrUndef, boolToStr, isHTMLElement } from '$adom';
import { boolToEmptyStrOrUndef, isHTMLElement } from '$adom';
import { readableActive, type Active, type ActiveProps, type StateProps } from '$libs/reactive';
import type {
OnChangeFn,
@ -11,7 +11,6 @@ import type {
} from '../../types';
import { KEYS } from '../../keyboard';
import { Soma } from '../../core/soma.svelte';
import { NUMBER_FIELD_LANGS } from './langs';
import { FieldProvider } from '../field';
// ── Intl fallback cache ──────────────────────────────────────────────────────
@ -660,7 +659,9 @@ export class NumberFieldIncrementTriggerProvider {
this.runtimePart = this.provider.runtime.part('increment-trigger', {
id: opts.id,
ref: opts.ref,
owner: this
owner: this,
// trigger-local disabled (includes atMax) → renderProps resolves aria-disabled
props: { disabled: () => this.isDisabled }
});
}
@ -683,13 +684,10 @@ export class NumberFieldIncrementTriggerProvider {
readonly props = $derived.by(() =>
this.runtimePart.assert({
...this.runtimePart.props,
type: 'button' as const,
// type, aria-label, data-spin-field, aria-disabled are morfo-declared.
...this.runtimePart.renderProps(),
tabindex: -1,
disabled: this.isDisabled || undefined,
'aria-label': this.provider.soma.langs.ts(NUMBER_FIELD_LANGS.INCREMENT),
'data-spin-field-increment-trigger': '',
'aria-disabled': boolToStr(this.isDisabled),
'data-disabled': boolToEmptyStrOrUndef(this.isDisabled),
onclick: this.handleClick,
onpointerdown: this.handlePointerDown,
@ -727,7 +725,9 @@ export class NumberFieldDecrementTriggerProvider {
this.runtimePart = this.provider.runtime.part('decrement-trigger', {
id: opts.id,
ref: opts.ref,
owner: this
owner: this,
// trigger-local disabled (includes atMin) → renderProps resolves aria-disabled
props: { disabled: () => this.isDisabled }
});
}
@ -750,13 +750,10 @@ export class NumberFieldDecrementTriggerProvider {
readonly props = $derived.by(() =>
this.runtimePart.assert({
...this.runtimePart.props,
type: 'button' as const,
// type, aria-label, data-spin-field, aria-disabled are morfo-declared.
...this.runtimePart.renderProps(),
tabindex: -1,
disabled: this.isDisabled || undefined,
'aria-label': this.provider.soma.langs.ts(NUMBER_FIELD_LANGS.DECREMENT),
'data-spin-field-decrement-trigger': '',
'aria-disabled': boolToStr(this.isDisabled),
'data-disabled': boolToEmptyStrOrUndef(this.isDisabled),
onclick: this.handleClick,
onpointerdown: this.handlePointerDown,
@ -889,10 +886,8 @@ export class NumberFieldScrubberProvider {
readonly props = $derived.by(() =>
this.runtimePart.assert({
...this.runtimePart.props,
role: 'presentation' as const,
'aria-hidden': true as const,
'data-spin-field-scrubber': '',
// role, aria-hidden, data-spin-field-scrubber are morfo-declared.
...this.runtimePart.renderProps(),
'data-scrubbing': boolToEmptyStrOrUndef(this.provider.scrubbing),
'data-disabled': boolToEmptyStrOrUndef(this.isDisabled),
style: {

Loading…
Cancel
Save

Powered by TurnKey Linux.