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/month-grid.ts

236 lines
6.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import type { Morfo } from '../types';
import { v } from '../types';
export const monthGridMorfo = {
name: 'MonthGrid',
kebab: 'month-grid',
scope: ['soma', 'sema', 'eidos'],
// Pack shipped 2026-07-07 (checkpoint S3b — calendar-family coherence).
expression: 'pack',
apg: 'https://www.w3.org/WAI/ARIA/apg/patterns/grid/',
texts: {
label: '#?components.month-grid.label|Month grid',
prevYear: '#?common.month-grid.prev-year|Previous year',
nextYear: '#?common.month-grid.next-year|Next year'
},
events: [
{
name: 'commit-set',
semantic: {
family: 'commit',
verb: 'set',
target: v.partRef('cell'),
intent: 'affirm',
sequence: 'post'
}
},
{
// Paginar al año anterior/siguiente. Per book cap. 27 §5,
// `shift.navigate` cubre "Lista → detalle. Página A → página B" —
// el usuario está en otro tramo del eje temporal. La variante
// `step` mantiene la huella semántica de paso discreto.
name: 'shift-navigate-step',
semantic: {
family: 'shift',
verb: 'navigate',
// Target is the `grid`, not the `provider`: the grid is the SUBJECT
// of the reframing — it is the cell surface that swaps to another
// year, while the header stays put and the arrow is only the
// instrument. The provider would drag the header into the stamp.
// Aligned with the rest of the calendar family so the
// directional `shift` signature slides the grid in all four alike.
target: v.partRef('grid'),
sequence: 'post'
}
}
],
direction: {},
focus: {
kind: 'roving',
parts: ['cell'],
// One tabbable month cell (provider: `focused ? 0 : -1`, documented
// there as "roving tabindex"), arrows move the stop on the month grid.
orientation: 'grid',
executor: {
custom:
'2D month-grid traversal (rows × columns of months) — executed by the shared 2D grid-walk primitive (soma/keyboard/grid-walk), not the 1D RovingFocusGroup'
}
},
parts: [
{
name: 'Provider',
kebab: 'provider',
archetype: 'provider',
kind: 'public',
defaultElement: 'div',
// No role — removed 2026-07-07 (checkpoint C4): the inner
// `role='grid'` carries the pattern (family-wide decision).
optional: false,
data: [
{ attr: 'data-readonly', severity: 'optional' },
{ attr: 'data-disabled', severity: 'optional' }
],
aria: [
{ attr: 'aria-label', value: v.propRef('ariaLabel'), severity: 'recommended' },
// Provider emitted `boolToStr(disabled)` (always "true"/"false"), so
// `propRef` + `ariaBoolean` lets the render bag emit the same string.
{
attr: 'aria-disabled',
value: v.propRef('disabled'),
severity: 'optional',
ariaBoolean: true
}
],
keyboard: [
{ key: 'ArrowRight', action: 'next-month' },
{ key: 'ArrowLeft', action: 'prev-month' },
{ key: 'ArrowDown', action: 'next-row' },
{ key: 'ArrowUp', action: 'prev-row' },
{ key: 'PageUp', action: 'prev-year' },
{ key: 'PageDown', action: 'next-year' },
{ key: 'Home', action: 'first-month' },
{ key: 'End', action: 'last-month' },
{ key: 'Enter', action: 'select' },
{ key: ' ', action: 'select' }
]
},
{
name: 'Header',
kebab: 'header',
archetype: 'header',
kind: 'public',
defaultElement: 'div',
optional: true,
data: [],
aria: []
},
{
name: 'Heading',
kebab: 'heading',
archetype: 'header',
kind: 'public',
defaultElement: 'div',
optional: true,
data: [],
aria: []
},
{
name: 'PrevButton',
kebab: 'prev-button',
archetype: 'trigger',
kind: 'public',
defaultElement: 'button',
role: 'button',
optional: true,
data: [{ attr: 'data-disabled', severity: 'optional' }],
attrs: [{ attr: 'type', value: v.literal('button') }],
aria: [
{
attr: 'aria-label',
value: v.commonRef('month-grid.prev-year', 'Previous year'),
severity: 'recommended'
},
// Provider emitted `boolToStr(disabled)` (always "true"/"false"), so
// `propRef` + `ariaBoolean` lets the render bag emit the same string.
{
attr: 'aria-disabled',
value: v.propRef('disabled'),
severity: 'optional',
ariaBoolean: true
}
]
},
{
name: 'NextButton',
kebab: 'next-button',
archetype: 'trigger',
kind: 'public',
defaultElement: 'button',
role: 'button',
optional: true,
data: [{ attr: 'data-disabled', severity: 'optional' }],
attrs: [{ attr: 'type', value: v.literal('button') }],
aria: [
{
attr: 'aria-label',
value: v.commonRef('month-grid.next-year', 'Next year'),
severity: 'recommended'
},
// Provider emitted `boolToStr(disabled)` (always "true"/"false"), so
// `propRef` + `ariaBoolean` lets the render bag emit the same string.
{
attr: 'aria-disabled',
value: v.propRef('disabled'),
severity: 'optional',
ariaBoolean: true
}
]
},
{
name: 'Grid',
kebab: 'grid',
kind: 'public',
defaultElement: 'div',
role: 'grid',
optional: false,
data: [],
aria: [
{
attr: 'aria-labelledby',
value: v.partRef('heading'),
condition: { when: 'part-present', part: 'heading' },
severity: 'recommended'
},
// Provider emitted `boolToStr(...)` (always "true"/"false") for both;
// `propRef` + `ariaBoolean` lets the render bag emit the same string.
{
attr: 'aria-readonly',
value: v.propRef('readonly'),
severity: 'optional',
ariaBoolean: true
},
{
attr: 'aria-disabled',
value: v.propRef('disabled'),
severity: 'optional',
ariaBoolean: true
}
]
},
{
name: 'Cell',
kebab: 'cell',
// No archetype: display gridcell container, not a menu `item` (the
// inner month button owns the interaction styling).
kind: 'public',
defaultElement: 'button',
role: 'gridcell',
optional: false,
data: [
{ attr: 'data-value' },
{ attr: 'data-selected', severity: 'optional' },
{ attr: 'data-current', severity: 'optional' },
{ attr: 'data-disabled', severity: 'optional' },
{ attr: 'data-focused', severity: 'optional' }
],
aria: [
// Provider emitted `boolToStr(...)` (always "true"/"false") for both;
// `propRef` + `ariaBoolean` lets the render bag emit the same string.
{
attr: 'aria-selected',
value: v.propRef('selected'),
severity: 'optional',
ariaBoolean: true
},
{
attr: 'aria-disabled',
value: v.propRef('disabled'),
severity: 'optional',
ariaBoolean: true
}
]
}
]
} as const satisfies Morfo;

Powered by TurnKey Linux.