You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/morfo/components/field.ts

199 lines
6.0 KiB

import type { Morfo } from '../types';
import { v } from '../types';
export const fieldMorfo = {
name: 'Field',
kebab: 'field',
scope: ['soma', 'sema', 'eidos'],
// The base shell stays on the family voice: the concrete fields carry the
// packs; Field.Input's Enter submit speaks with the commit family's base
// signature (adoption of the written family doctrine — see the event note).
expression: 'family-default',
// Field.Input is the single-line text input — same pattern the password /
// textarea siblings cite.
apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/textbox/',
texts: {
label: '#?components.field.label|Field',
'required-indicator': '#?components.field.required-indicator|*',
'optional-indicator': '#?components.field.optional-indicator|(optional)'
},
events: [
// User pressed Enter on the plain-text Field.Input. ADOPTION of the
// family doctrine written (with incident) in password-field's morfo:
// per-keystroke typing is NOT a commit — `commit` is for terminal
// actions, and Enter-to-submit IS the text-input pattern (aligned with
// password / textarea / editable / number-field / search / combobox).
// This closed the text-input pair's asymmetry (audit ficha field F-2:
// textarea had `commit-submit`, the base Input did not).
{
name: 'commit-submit',
semantic: {
family: 'commit',
verb: 'submit',
target: v.partRef('input'),
intent: 'neutral',
sequence: 'post'
}
}
],
parts: [
{
name: 'Provider',
kebab: 'provider',
archetype: 'provider',
kind: 'public',
defaultElement: 'div',
optional: false,
// data-state (5-value aggregate) was pruned 2026-07-07 (checkpoint
// S10): zero CSS consumers catalog-wide and redundant with the flags
// below — one representation per concept. The resolved state still
// drives Input's aria-busy via its state condition.
data: [
{ attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' },
{ attr: 'data-readonly', value: v.propRef('readonly'), severity: 'optional' },
{ attr: 'data-required', value: v.propRef('required'), severity: 'optional' },
{ attr: 'data-invalid', value: v.propRef('invalid'), severity: 'optional' },
{ attr: 'data-dirty', value: v.propRef('dirty'), severity: 'optional' },
{ attr: 'data-touched', value: v.propRef('touched'), severity: 'optional' },
{ attr: 'data-field-name', value: v.propRef('name'), emit: 'value', severity: 'optional' }
],
aria: []
},
{
name: 'Label',
kebab: 'label',
archetype: 'label',
kind: 'public',
defaultElement: 'label',
optional: true,
data: [
{ attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' },
{ attr: 'data-invalid', value: v.propRef('invalid'), severity: 'optional' },
{ attr: 'data-required', value: v.propRef('required'), severity: 'optional' }
],
attrs: [
{
attr: 'for',
value: v.partRef('input'),
condition: { when: 'part-present', part: 'input' },
severity: 'recommended'
}
],
aria: []
},
{
name: 'RequiredIndicator',
kebab: 'required-indicator',
archetype: 'indicator',
kind: 'public',
defaultElement: 'span',
optional: true,
data: [{ attr: 'data-required', value: v.propRef('required'), severity: 'optional' }],
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
},
{
name: 'OptionalIndicator',
kebab: 'optional-indicator',
archetype: 'indicator',
kind: 'public',
defaultElement: 'span',
optional: true,
data: [{ attr: 'data-required', value: v.propRef('required'), severity: 'optional' }],
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
},
{
name: 'Control',
kebab: 'control',
kind: 'public',
defaultElement: 'div',
optional: true,
data: [
{ attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' },
{ attr: 'data-invalid', value: v.propRef('invalid'), severity: 'optional' }
],
aria: []
},
{
name: 'Input',
kebab: 'input',
archetype: 'input',
kind: 'public',
defaultElement: 'input',
optional: true,
// `states` stays: the resolved field state drives aria-busy below
// (state-equals 'loading'). Its data-state attr was pruned with the
// other two (checkpoint S10, 2026-07-07).
states: ['state'],
data: [],
attrs: [{ attr: 'type', value: v.propRef('type') }],
aria: [
{
attr: 'aria-labelledby',
value: v.partRef('label'),
condition: { when: 'part-present', part: 'label' },
severity: 'recommended'
},
{ attr: 'aria-invalid', value: v.propRef('invalid'), ariaBoolean: true },
{
attr: 'aria-describedby',
value: v.partRef('helper-text'),
condition: { when: 'part-present', part: 'helper-text' },
severity: 'recommended'
},
{
attr: 'aria-busy',
value: v.literal('true'),
severity: 'optional',
condition: { when: 'state-equals', state: 'state', value: 'loading' }
},
{
attr: 'aria-required',
value: v.literal('true'),
severity: 'optional',
condition: { when: 'prop-truthy', prop: 'required' }
}
],
keyboard: [{ key: 'Enter', action: 'submit' }]
},
{
name: 'HelperText',
kebab: 'helper-text',
archetype: 'description',
kind: 'public',
defaultElement: 'p',
optional: true,
data: [{ attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' }],
aria: []
},
{
name: 'ErrorText',
kebab: 'error-text',
archetype: 'description',
kind: 'public',
defaultElement: 'p',
role: 'alert',
optional: true,
data: [],
aria: [{ attr: 'aria-live', value: v.literal('polite') }]
},
{
name: 'Prefix',
kebab: 'prefix',
kind: 'public',
defaultElement: 'span',
optional: true,
data: [],
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
},
{
name: 'Suffix',
kebab: 'suffix',
kind: 'public',
defaultElement: 'span',
optional: true,
data: [],
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
}
]
} as const satisfies Morfo;

Powered by TurnKey Linux.