diff --git a/scripts/component-audit.ts b/scripts/component-audit.ts index eeecf2cbe..4133e7205 100644 --- a/scripts/component-audit.ts +++ b/scripts/component-audit.ts @@ -344,6 +344,17 @@ function checkMorfo(kebab: string, src: string, info: ComponentReport): CheckRes 'prev-year', 'first-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 'next-item', 'prev-item', diff --git a/src/uix/eidos/components/month-grid/README.md b/src/uix/eidos/components/month-grid/README.md index 32c1bbf5c..3960a4191 100644 --- a/src/uix/eidos/components/month-grid/README.md +++ b/src/uix/eidos/components/month-grid/README.md @@ -34,3 +34,50 @@ Parts: `Provider`, `Header`, `Heading`, `PrevButton`, `NextButton`, `Grid`, - `commit-set` — `commit.set` on `cell`, `intent: 'affirm'`, 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. | diff --git a/src/uix/eidos/components/time-range-picker/README.md b/src/uix/eidos/components/time-range-picker/README.md index 71b7a4ade..10c1ab2e3 100644 --- a/src/uix/eidos/components/time-range-picker/README.md +++ b/src/uix/eidos/components/time-range-picker/README.md @@ -93,6 +93,8 @@ toggle acepta `type: 'start' | 'end'`. ## Gaps -- No hay vista "preset list" (Hoy, Última hora…). Se puede componer fuera. -- No hay swap automático visible si el usuario fija `end < start` — la - validación queda en manos del `validate` opt. +| Gap | Disposición | Detalle | +| --- | --- | --- | +| 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`. | diff --git a/src/uix/eidos/components/time-range-picker/time-range-picker.css b/src/uix/eidos/components/time-range-picker/time-range-picker.css index 5554bfb59..b973163de 100644 --- a/src/uix/eidos/components/time-range-picker/time-range-picker.css +++ b/src/uix/eidos/components/time-range-picker/time-range-picker.css @@ -313,6 +313,27 @@ 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) { [data-time-range-picker-trigger] { transition: none; diff --git a/src/uix/eidos/components/year-grid/README.md b/src/uix/eidos/components/year-grid/README.md index 8b08eb312..5f5c6fb56 100644 --- a/src/uix/eidos/components/year-grid/README.md +++ b/src/uix/eidos/components/year-grid/README.md @@ -34,3 +34,48 @@ Parts: `Provider`, `Header`, `Heading`, `PrevButton`, `NextButton`, `Grid`, - `commit-set` — `commit.set` on `cell`, `intent: 'affirm'`, 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. |