docs+style: close last 3 NEEDS-WORK → 77/77 PASS audit

`npm run component:audit` now reports 77 / 77 PASS, 0 NEEDS-WORK,
0 BROKEN.

**month-grid + year-grid READMEs**

Add Baseline / Comparativa / Decisiones / Gaps sections (F-1.1 /
F-1.2 / F-1.3 / F-1.4). Both grids are standalone primitives
extracted from DatePicker's `kind: 'month' | 'year'` views (task
`#70`); they have no air counterpart. Documented Sema event design
(single `commit-set` for value mutation, `nav-step` for paginated
header navigation), the 4×3 grid choice, configurable `decadeSize`
for YearGrid, and known gaps (multi-year range, quick-jump,
localized month names — all deferred).

**Audit script `focusMoveActions` extension**

`A-3.7` was firing on month-grid/year-grid because the script didn't
recognise `next-row`, `prev-row`, `first-month`, `last-month`,
`first-year`, `last-year`, `next-page`, `prev-page` as focus moves.
They ARE focus moves inside their respective grids (same semantic as
`next-day` / `first-day-of-week` inside Calendar). Added to the
focusMoveActions set in `scripts/component-audit.ts` so the
event-vs-keyboard ratio heuristic no longer over-counts.

**time-range-picker**

- README Gaps section rewritten as a table with explicit disposition
  markers (`implementar` / `diferir` / `descartar`) per F-1.4.
- Recipe `time-range-picker.css` now styles `[data-readonly]` (lower
  opacity + pointer-events: none on trigger/clear) and `[data-invalid]`
  (threat-colour border + focus ring) — closes R-1.3 / R-1.4. Both
  attrs were declared in morfo but unstyled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
active-uix
dev 5 months ago
parent fdb0e68028
commit ec66999b6c

@ -344,6 +344,17 @@ function checkMorfo(kebab: string, src: string, info: ComponentReport): CheckRes
'prev-year', 'prev-year',
'first-day-of-week', 'first-day-of-week',
'last-day-of-week', 'last-day-of-week',
// Month-grid / year-grid intra-grid navigation. Same semantics as
// next-day / first-day-of-week in a Calendar — these move focus
// inside the grid, they don't mutate the selected value.
'next-row',
'prev-row',
'first-month',
'last-month',
'first-year',
'last-year',
'next-page',
'prev-page',
// Generic listbox / menu / tablist navigation // Generic listbox / menu / tablist navigation
'next-item', 'next-item',
'prev-item', 'prev-item',

@ -34,3 +34,50 @@ Parts: `Provider`, `Header`, `Heading`, `PrevButton`, `NextButton`, `Grid`,
- `commit-set` — `commit.set` on `cell`, `intent: 'affirm'`, sequence `post`. - `commit-set` — `commit.set` on `cell`, `intent: 'affirm'`, sequence `post`.
- `nav-step` — `shift.navigate` on `provider`, sequence `post`. - `nav-step` — `shift.navigate` on `provider`, sequence `post`.
## Baseline
Origen: `air/components/calendar/calendar-grid-body` adaptado a un
contexto standalone year-as-12-months (no día por día). El soma
MonthGrid es nuevo en UIX (no había contraparte 1:1 en air): la
extracción se hizo cuando DatePicker promovió `kind: 'month'` como
view-mode canónico, para que el grid de meses pudiera reutilizarse
fuera del DatePicker (filtros, reportes, año fiscal).
## Comparativa
| Capacidad | UIX MonthGrid | React Spectrum (Adobe) | Ark UI DatePicker (month view) | Radix Themes |
| --- | --- | --- | --- | --- |
| Standalone month-grid component | Sí | No — embebido en DatePicker | No — embebido en DatePicker | No — no expone month view |
| `value: { year, month }` bindable | Sí | Sólo combinado con día | Idem | — |
| Navegación por flechas + PageUp/Down | Sí (4-col grid) | Sí | Sí | — |
| Año anterior/siguiente (PageUp/PageDown) | Sí | Sí | Sí | — |
| `data-readonly` / `data-disabled` | Sí | Sí | Sí | — |
| `min` / `max` value (rango limitado) | Sí | Sí | Sí | — |
| Composable Header + PrevButton + NextButton + Heading | Sí | Parcial | Sí | — |
| Highlight current month (`data-current`) | Sí | Sí | Sí | — |
## Decisiones
- **Componente standalone, no sólo view del DatePicker** — extraído
para reusabilidad (`#70` task). DatePicker `kind: 'month'` lo
consume internamente.
- **4 columnas × 3 filas** — match natural para 12 meses; la mayoría
de date pickers lo hacen igual.
- **`select` único evento mutante; el resto son focus moves** — el
audit script (`A-3.7`) reconoce `next-month`/`prev-month`/`next-row`/
`prev-row`/`first-month`/`last-month`/`prev-year`/`next-year` como
focus moves (no mutan el valor seleccionado). Sólo Enter/Space y la
navegación por PrevButton/NextButton mutan estado.
- **`nav-step` cubre toda la paginación** — un solo evento sema para
cualquier shift del header (PrevButton / NextButton / PageUp / PageDown).
Family `shift.navigate` semánticamente correcto.
## Gaps
| Gap | Disposición | Detalle |
| --- | --- | --- |
| Multi-año selection (range) | **diferir** | Caso de uso emergente; resolver con un MonthRangePicker dedicado cuando llegue demanda real. |
| Quick-jump dropdown del año | **diferir** | Útil para escalar años pero el patrón actual (PrevButton/NextButton + PageUp/Down) cubre. |
| Localized month names en el cell | **implementar** | Hoy se renderiza el número/short label desde el snippet; las locales que necesitan formatos mes-largo no se aplican automáticamente. Backlog i18n. |
| Renombrar `nav-step` a `shift-step` o `navigate-page` | **diferir** | El warning A-3.6 sugiere {verb}-X. Cambiar requiere actualizar consumidores; bajo prioridad. |

@ -93,6 +93,8 @@ toggle acepta `type: 'start' | 'end'`.
## Gaps ## Gaps
- No hay vista "preset list" (Hoy, Última hora…). Se puede componer fuera. | Gap | Disposición | Detalle |
- No hay swap automático visible si el usuario fija `end < start` — la | --- | --- | --- |
validación queda en manos del `validate` opt. | Vista "preset list" (Hoy, Última hora, Last 5 min…) | **diferir** | Se puede componer fuera con un Picker.Header + botones que llamen `setRange(a, b)`. Si emerge demanda recurrente, promover a parte canónica. |
| Swap automático visible cuando `end < start` | **diferir** | Hoy la validación vive en el `validate` opt y el `data-invalid` resultante. Auto-swap silencioso oculta intención del usuario — preferimos que el formulario muestre el error. |
| Estilado visual de `[data-readonly]` y `[data-invalid]` en el recipe | **implementar** | Audit `R-1.3` / `R-1.4`: las attrs se emiten desde el morfo pero el recipe no las consume. Pendiente añadir reglas en `time-range-picker.css`. |

@ -313,6 +313,27 @@
white-space: nowrap; white-space: nowrap;
} }
/* Readonly + invalid surfaces. Soma emits both attrs on the provider
* (declared in morfo); the recipe lowers opacity / blocks pointer
* events for readonly and paints an error ring for invalid. */
[data-time-range-picker][data-readonly] [data-time-range-picker-trigger],
[data-time-range-picker][data-readonly] [data-time-range-picker-clear] {
pointer-events: none;
opacity: 0.7;
cursor: default;
}
[data-time-range-picker][data-invalid] [data-time-range-picker-trigger] {
border-color: var(--color-threat-border);
box-shadow:
0 0 0 var(--focus-ring-offset) var(--color-surface-default),
0 0 0 calc(var(--focus-ring-offset) + var(--focus-ring-width)) var(--color-threat-border);
}
[data-time-range-picker][data-invalid] [data-time-range-picker-trigger]:hover {
border-color: var(--color-threat-border);
}
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
[data-time-range-picker-trigger] { [data-time-range-picker-trigger] {
transition: none; transition: none;

@ -34,3 +34,48 @@ Parts: `Provider`, `Header`, `Heading`, `PrevButton`, `NextButton`, `Grid`,
- `commit-set` — `commit.set` on `cell`, `intent: 'affirm'`, sequence `post`. - `commit-set` — `commit.set` on `cell`, `intent: 'affirm'`, sequence `post`.
- `nav-step` — `shift.navigate` on `provider`, sequence `post`. - `nav-step` — `shift.navigate` on `provider`, sequence `post`.
## Baseline
Origen: extracción standalone del year-view de DatePicker (tarea `#70`,
junto con MonthGrid). Air no tenía componente equivalente — la
selección de año vivía sólo dentro del DatePicker. Soma YearGrid es
nuevo en UIX; sirve para escenarios de selección de año aislada
(reportes anuales, filtros, navegación de archivos por año).
## Comparativa
| Capacidad | UIX YearGrid | React Spectrum (Adobe) | Ark UI DatePicker (year view) | Radix Themes |
| --- | --- | --- | --- | --- |
| Standalone year-grid component | Sí | No — embebido en DatePicker | No — embebido en DatePicker | No |
| `value: { year }` bindable | Sí | Sólo combinado con día | Idem | — |
| `decadeSize` configurable (años por página) | Sí (default 12) | Default 12, no configurable | Default 12 | — |
| Paginación PageUp/Down (década anterior/siguiente) | Sí | Sí | Sí | — |
| Navegación por flechas dentro del grid | Sí (4 columnas) | Sí | Sí | — |
| Highlight año actual (`data-current`) | Sí | Sí | Sí | — |
| `data-readonly` / `data-disabled` | Sí | Sí | Sí | — |
| `min` / `max` value (rango limitado) | Sí | Sí | Sí | — |
| Composable Header + PrevButton + NextButton + Heading | Sí | Parcial | Sí | — |
## Decisiones
- **Componente standalone, no sólo view del DatePicker** — extraído
para reusabilidad. DatePicker `kind: 'year'` lo consume internamente.
- **`decadeSize` configurable** — algunos diseños prefieren 10 (década
natural) o 20 (dos décadas). UIX permite ambos sin nuevo componente.
- **Mismo modelo de eventos que MonthGrid** — `commit-set` y `nav-step`,
para que la pareja month-grid/year-grid sea sema-simétrica.
- **`select` único evento mutante; resto son focus moves** — el audit
script reconoce `next-year`/`prev-year`/`next-row`/`prev-row`/
`first-year`/`last-year`/`next-page`/`prev-page` como focus moves
(no mutan el valor). Sólo Enter/Space y los botones de paginación
mutan estado.
## Gaps
| Gap | Disposición | Detalle |
| --- | --- | --- |
| Multi-año selection (range) | **diferir** | Resolver con YearRangePicker cuando llegue caso real. |
| Quick-jump a una década específica (input + go) | **diferir** | Útil para escalar 100+ años; la paginación cubre el caso común. |
| Renombrar `nav-step` a `shift-step` o `navigate-page` | **diferir** | El warning A-3.6 sugiere {verb}-X. Cambiar requiere actualizar consumidores; bajo prioridad. |
| Highlight visual de la década actual (extra al año) | **diferir** | Hoy se marca sólo el año actual con `data-current`. La década podría marcarse en el heading. |

Loading…
Cancel
Save

Powered by TurnKey Linux.