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/radio-group.ts

166 lines
4.8 KiB

import type { Morfo } from '../types';
import { v } from '../types';
export const radioGroupMorfo = {
name: 'RadioGroup',
kebab: 'radio-group',
scope: ['soma', 'sema', 'eidos'],
expression: 'pack',
texts: {
label: '#?components.radio-group.label|Radio Group'
},
apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/radio/',
events: [
{
// Selecting a radio is always an affirmation — there is no
// "unselect" path (the only way to unset is to select another
// option). Intent affirm carries the slight positive lift via
// SEMA_MAP.intents.affirm.deltas.
name: 'commit-select',
semantic: {
family: 'commit',
verb: 'select',
target: v.partRef('item'),
intent: 'affirm',
sequence: 'pre'
}
}
],
parts: [
{
name: 'Provider',
kebab: 'provider',
archetype: 'provider',
kind: 'public',
defaultElement: 'div',
role: 'radiogroup',
optional: false,
data: [
{
attr: 'data-orientation',
values: ['horizontal', 'vertical'],
value: v.propRef('orientation')
},
{ attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' },
{ attr: 'data-required', value: v.propRef('required'), severity: 'optional' },
{ attr: 'data-readonly', value: v.propRef('readonly'), severity: 'optional' },
{ attr: 'data-invalid', value: v.propRef('invalid'), severity: 'optional' }
],
aria: [
{ attr: 'aria-orientation', value: v.propRef('orientation') },
{
attr: 'aria-label',
value: v.propRef('ariaLabel'),
condition: { when: 'prop-truthy', prop: 'ariaLabel' },
severity: 'recommended'
},
{
attr: 'aria-labelledby',
value: v.propRef('ariaLabelledby'),
condition: { when: 'prop-truthy', prop: 'ariaLabelledby' },
severity: 'recommended'
},
{
attr: 'aria-required',
value: v.literal('true'),
severity: 'optional',
condition: { when: 'prop-truthy', prop: 'required' }
},
{
attr: 'aria-disabled',
value: v.literal('true'),
severity: 'optional',
condition: { when: 'prop-truthy', prop: 'disabled' }
},
{
attr: 'aria-readonly',
value: v.literal('true'),
severity: 'optional',
condition: { when: 'prop-truthy', prop: 'readonly' }
},
{
attr: 'aria-invalid',
value: v.literal('true'),
severity: 'optional',
condition: { when: 'prop-truthy', prop: 'invalid' }
}
]
},
{
name: 'Item',
kebab: 'item',
archetype: 'item',
kind: 'public',
defaultElement: 'button',
role: 'radio',
optional: false,
states: ['checked', 'unchecked'],
data: [
{ attr: 'data-state', values: ['checked', 'unchecked'], value: v.stateRef('checked') },
{ attr: 'data-value' },
{ attr: 'data-disabled', value: v.propRef('disabled'), severity: 'optional' },
{ attr: 'data-readonly', value: v.propRef('readonly'), severity: 'optional' },
{
attr: 'data-orientation',
values: ['horizontal', 'vertical'],
value: v.propRef('orientation')
}
],
aria: [
{ attr: 'type', value: v.literal('button') },
{ attr: 'aria-checked', value: v.stateRef('checked') }
],
keyboard: [
{ key: 'ArrowDown', action: 'next-item' },
{ key: 'ArrowUp', action: 'prev-item' },
{ key: 'ArrowRight', action: 'next-item' },
{ key: 'ArrowLeft', action: 'prev-item' }
]
},
{
// Visual indicator (filled dot) inside an Item button. Optional
// — the eidos wrapper renders it by default with `<SvgDot>`,
// headless consumers can omit. Marker is contract-backed so
// eidos selectors don't drift.
name: 'Indicator',
kebab: 'indicator',
archetype: 'indicator',
kind: 'public',
defaultElement: 'span',
optional: true,
states: ['checked', 'unchecked'],
data: [{ attr: 'data-state', values: ['checked', 'unchecked'], value: v.stateRef('checked') }],
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
},
{
// Native `<input type="radio">` rendered alongside the button so
// the browser includes name+value in form submissions and
// participates in HTMLFormElement validation. Visually hidden,
// kept out of the AT tree (`aria-hidden`) since the button's
// role="radio" is the AT-visible control.
name: 'HiddenInput',
kebab: 'hidden-input',
archetype: 'input',
kind: 'public',
defaultElement: 'input',
optional: true,
data: [],
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
},
{
// Group-level label part. When present, the Provider gets
// `aria-labelledby="<label id>"` instead of relying on a string
// `aria-label`. Either form (string aria-label or this part) is
// required by the radiogroup APG pattern.
name: 'Label',
kebab: 'label',
archetype: 'label',
kind: 'public',
defaultElement: 'div',
optional: true,
data: [],
aria: []
}
]
} as const satisfies Morfo;

Powered by TurnKey Linux.