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/toast.ts

243 lines
5.6 KiB

import type { Morfo } from '../types';
import { v } from '../types';
import { INTENTS } from '../../intent';
export const toastMorfo = {
name: 'Toast',
kebab: 'toast',
scope: ['soma', 'sema'],
apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/alert/',
texts: {
label: '#?components.toast.label|Toast'
},
events: [
{
name: 'present',
semantic: {
family: 'emerge',
verb: 'present',
target: v.partRef('item'),
sequence: 'pre'
}
},
{
// Persistence: explicit `transient` even though the book §6.2
// canon would imply `untilFix` / `untilAction` for `signal +
// risk` / `signal + threat`. The Toast component owns its own
// lifecycle via `Toast.Provider`'s auto-dismiss timer + user
// dismiss; the `data-event-*` projection is a one-shot pulse
// when the toast enters. Persistence of the toast surface
// itself lives in `data-state` (open/closed) on the Item, not
// in the signal projection.
name: 'announce',
semantic: {
family: 'signal',
verb: 'announce',
target: v.partRef('item'),
sequence: 'coincident',
intent: {
fromProp: 'intent',
default: 'neutral',
supported: INTENTS
},
persistence: 'transient'
}
},
{
name: 'dismiss',
semantic: {
family: 'emerge',
verb: 'dismiss',
target: v.partRef('item'),
sequence: 'pre'
}
}
],
parts: [
{
name: 'Provider',
kebab: 'provider',
archetype: 'provider',
kind: 'virtual',
defaultElement: 'none',
optional: false,
data: [],
aria: []
},
{
name: 'Viewport',
kebab: 'viewport',
archetype: 'viewport',
kind: 'public',
defaultElement: 'div',
role: 'region',
optional: false,
data: [],
aria: [
{
attr: 'aria-label',
value: v.propRef('label'),
severity: 'recommended'
},
{ attr: 'aria-live', value: v.literal('polite') }
]
},
{
name: 'Item',
kebab: 'item',
archetype: 'item',
kind: 'public',
defaultElement: 'div',
optional: false,
states: ['open', 'closed'],
data: [
{ attr: 'data-state', values: ['open', 'closed'], value: v.stateRef('open') },
{ attr: 'data-starting-style', severity: 'optional' },
{ attr: 'data-ending-style', severity: 'optional' },
{
attr: 'data-intent',
values: INTENTS,
value: v.propRef('intent')
},
{ attr: 'data-loading', severity: 'optional', value: v.propRef('loading') },
{
attr: 'data-swipe',
values: ['start', 'move', 'cancel', 'end'],
severity: 'optional',
value: v.propRef('swipeState')
},
{
attr: 'data-swipe-direction',
values: ['up', 'down', 'left', 'right'],
value: v.propRef('swipeDirection')
}
],
aria: [
{
attr: 'role',
value: v.mapRef(v.propRef('intent'), {
neutral: 'status',
affirm: 'status',
fulfill: 'status',
risk: 'alert',
threat: 'alert',
// `loss` is a consummated negative consequence (no urgency
// to interrupt) — `status` is the appropriate ARIA role.
loss: 'status'
})
},
{
attr: 'aria-live',
value: v.mapRef(v.propRef('intent'), {
neutral: 'polite',
affirm: 'polite',
fulfill: 'polite',
risk: 'assertive',
threat: 'assertive',
loss: 'polite'
}),
severity: 'recommended'
},
{ attr: 'aria-atomic', value: v.literal('true') },
{
attr: 'aria-labelledby',
value: v.partRef('title'),
condition: { when: 'part-present', part: 'title' },
severity: 'recommended'
},
{
attr: 'aria-describedby',
value: v.partRef('description'),
condition: { when: 'part-present', part: 'description' },
severity: 'optional'
}
],
keyboard: [{ key: 'Escape', action: 'close' }]
},
{
name: 'Title',
kebab: 'title',
archetype: 'title',
kind: 'public',
defaultElement: 'div',
optional: true,
data: [],
aria: []
},
{
name: 'Description',
kebab: 'description',
archetype: 'description',
kind: 'public',
defaultElement: 'div',
optional: true,
data: [],
aria: []
},
{
/**
* Status disc — the leading-edge icon area whose color follows
* the Item's `data-intent`. Optional visual structural part:
* eidos's flat template ships one by default, but a consumer
* compounding the parts can omit it (the recipe's grid auto-
* collapses) or replace its contents.
*/
name: 'Status',
kebab: 'status',
archetype: 'indicator',
kind: 'public',
defaultElement: 'span',
optional: true,
data: [],
aria: [{ attr: 'aria-hidden', value: v.literal('true') }]
},
{
/**
* Main column — stack container for title + description + action.
* Pure layout part (no semantics, no states); declared so the
* recipe's `[data-toast-main]` selector is morfo-backed instead
* of an off-contract data-attr.
*/
name: 'Main',
kebab: 'main',
kind: 'public',
defaultElement: 'div',
optional: true,
data: [],
aria: []
},
{
name: 'Action',
kebab: 'action',
archetype: 'action',
kind: 'public',
defaultElement: 'button',
role: 'button',
optional: true,
data: [],
aria: [
{ attr: 'type', value: v.literal('button') },
{ attr: 'aria-label', value: v.propRef('altText'), severity: 'recommended' }
]
},
{
name: 'Close',
kebab: 'close',
archetype: 'close',
kind: 'public',
defaultElement: 'button',
role: 'button',
optional: true,
data: [],
aria: [
{ attr: 'type', value: v.literal('button') },
{
attr: 'aria-label',
value: v.commonRef('buttons.close', 'Close'),
severity: 'recommended'
}
]
}
]
} as const satisfies Morfo;

Powered by TurnKey Linux.