import type { Morfo } from '../types'; import { v } from '../types'; export const carouselMorfo = { name: 'Carousel', kebab: 'carousel', scope: ['soma', 'sema', 'eidos'], expression: 'family-default', apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/carousel/', texts: { label: '#?components.carousel.label|Carousel', prev: '#?components.carousel.prev|Previous slide', next: '#?components.carousel.next|Next slide', indicator: '#?components.carousel.indicator|Go to slide {{index}}', slide: '#?components.carousel.slide|Slide {{index}} of {{total}}' }, events: [ { // Slide change — user-initiated (button/indicator) or auto-rotation. // Per book cap. 27 §5 shift.navigate ("Página A → página B"). Variant // `slide` preserves the carousel-specific label. name: 'shift-navigate-slide', semantic: { family: 'shift', verb: 'navigate', target: v.partRef('item'), sequence: 'post' } } ], direction: {}, focus: { kind: 'roving', parts: ['item'], orientation: 'horizontal', executor: { custom: 'Slide membership is viewport-virtualised: the roving stop follows the scroller and snap state, not a static candidate query — the shared primitive cannot see it (eje focus-first F2).' } }, parts: [ { name: 'Provider', kebab: 'provider', archetype: 'provider', kind: 'public', defaultElement: 'section', role: 'region', optional: false, data: [ { attr: 'data-orientation', values: ['horizontal', 'vertical'] }, { attr: 'data-dir', values: ['ltr', 'rtl'], severity: 'optional' }, { attr: 'data-disabled', severity: 'optional' }, { attr: 'data-dragging', severity: 'optional' }, { attr: 'data-autoplay', severity: 'optional' } ], aria: [ { attr: 'aria-roledescription', value: v.literal('carousel') }, { attr: 'aria-label', value: v.translationRef('#?components.carousel.label|Carousel'), severity: 'recommended' } ], keyboard: [ { key: 'ArrowRight', action: 'next-item' }, { key: 'ArrowLeft', action: 'prev-item' }, { key: 'ArrowDown', action: 'next-item' }, { key: 'ArrowUp', action: 'prev-item' }, { key: 'Home', action: 'first-item' }, { key: 'End', action: 'last-item' } ] }, { name: 'Viewport', kebab: 'viewport', archetype: 'viewport', kind: 'public', defaultElement: 'div', optional: false, data: [{ attr: 'data-orientation', values: ['horizontal', 'vertical'] }], aria: [] }, { name: 'ItemGroup', kebab: 'item-group', archetype: 'group', kind: 'public', defaultElement: 'div', optional: false, data: [ { attr: 'data-orientation', values: ['horizontal', 'vertical'] }, { attr: 'data-dragging', severity: 'optional' } ], aria: [] }, { name: 'Item', kebab: 'item', archetype: 'item', kind: 'public', defaultElement: 'div', role: 'group', optional: false, states: ['active', 'inactive'], data: [ { attr: 'data-state', values: ['active', 'inactive'] }, { attr: 'data-index' }, { attr: 'data-orientation', values: ['horizontal', 'vertical'] } ], aria: [ { attr: 'aria-roledescription', value: v.literal('slide') }, { attr: 'aria-label', value: v.propRef('index'), severity: 'recommended' } ] }, { name: 'PrevTrigger', kebab: 'prev-trigger', archetype: 'trigger', kind: 'public', defaultElement: 'button', role: 'button', optional: true, data: [{ attr: 'data-disabled', severity: 'optional' }], aria: [ { attr: 'type', value: v.literal('button') }, { attr: 'aria-label', value: v.translationRef('#?components.carousel.prev|Previous slide'), severity: 'recommended' } ] }, { name: 'NextTrigger', kebab: 'next-trigger', archetype: 'trigger', kind: 'public', defaultElement: 'button', role: 'button', optional: true, data: [{ attr: 'data-disabled', severity: 'optional' }], aria: [ { attr: 'type', value: v.literal('button') }, { attr: 'aria-label', value: v.translationRef('#?components.carousel.next|Next slide'), severity: 'recommended' } ] }, { name: 'IndicatorGroup', kebab: 'indicator-group', archetype: 'group', kind: 'public', defaultElement: 'div', role: 'tablist', optional: true, data: [{ attr: 'data-orientation', values: ['horizontal', 'vertical'] }], aria: [{ attr: 'aria-orientation', value: v.propRef('orientation') }] }, { name: 'Indicator', kebab: 'indicator', archetype: 'indicator', kind: 'public', defaultElement: 'button', role: 'tab', optional: true, states: ['active', 'inactive'], data: [{ attr: 'data-state', values: ['active', 'inactive'] }, { attr: 'data-index' }], aria: [ { attr: 'type', value: v.literal('button') }, { attr: 'aria-selected', value: v.stateRef('active') }, { attr: 'aria-label', value: v.propRef('ariaLabel'), severity: 'recommended' } ] } ] } as const satisfies Morfo;