From 265a3946b55fc6d626b073e52d1778c562a6b829 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 22 May 2026 17:21:15 +0200 Subject: [PATCH] =?UTF-8?q?feat(layout):=20plug=20Batch=201=20gaps=20?= =?UTF-8?q?=E2=80=94=20Flex.alignContent=20+=20Grid.columns/rows/inline/al?= =?UTF-8?q?ignContent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the "should" features documented as gaps in the layout READMEs from `fdb0e680`. Audit script still reports 77 / 77 PASS, svelte-check 0 errors. **Flex** - `alignContent` prop (multi-line cross-axis alignment). Maps to `--flex-align-content`; defaults to `normal` in the recipe so it has no effect when wrap is off. Closes the Radix / Chakra parity gap. **Grid** - `inline` boolean — renders as `display: inline-grid` (paridad con Flex.inline). - `columns` / `rows` numeric shorthands — Radix Themes pattern. Internally resolved to `repeat(N, minmax(0, 1fr))`. `templateColumns` / `templateRows` string still wins if both are passed. - `alignContent` prop — same shape as Flex. **Shared** - New `LayoutAlignContent` type added to `_layout/shared.ts` (covers start, end, center, stretch, space-between, space-around, space-evenly, flex-start, flex-end, normal, baseline). **Group** - Verified `grow` boolean already implemented in `0ec2a57a` / `591b0885` — no change needed; the README now correctly reflects it. **Docs** - Flex + Grid READMEs updated: comparison tables mark previously missing features as Sí; Gaps section marks them ✅ implementado. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/uix/eidos/components/_layout/shared.ts | 12 ++++++++ src/uix/eidos/components/flex/README.md | 4 +-- src/uix/eidos/components/flex/flex.css | 1 + src/uix/eidos/components/flex/flex.svelte | 2 ++ src/uix/eidos/components/flex/types.ts | 6 ++++ src/uix/eidos/components/grid/README.md | 12 ++++---- src/uix/eidos/components/grid/grid.css | 1 + src/uix/eidos/components/grid/grid.svelte | 33 ++++++++++++++++++++-- src/uix/eidos/components/grid/types.ts | 17 +++++++++-- 9 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/uix/eidos/components/_layout/shared.ts b/src/uix/eidos/components/_layout/shared.ts index a30d1666f..49345fbc6 100644 --- a/src/uix/eidos/components/_layout/shared.ts +++ b/src/uix/eidos/components/_layout/shared.ts @@ -43,6 +43,18 @@ export type LayoutAlignSelf = export type LayoutJustifySelf = 'auto' | 'start' | 'end' | 'center' | 'stretch'; export type LayoutFlexDirection = 'row' | 'row-reverse' | 'column' | 'column-reverse'; export type LayoutFlexWrap = 'nowrap' | 'wrap' | 'wrap-reverse'; +export type LayoutAlignContent = + | 'start' + | 'end' + | 'center' + | 'stretch' + | 'space-between' + | 'space-around' + | 'space-evenly' + | 'flex-start' + | 'flex-end' + | 'normal' + | 'baseline'; export type LayoutAlignItems = | 'stretch' | 'start' diff --git a/src/uix/eidos/components/flex/README.md b/src/uix/eidos/components/flex/README.md index 812119cda..87913c9d9 100644 --- a/src/uix/eidos/components/flex/README.md +++ b/src/uix/eidos/components/flex/README.md @@ -39,7 +39,7 @@ intactos al Box raíz. | `wrap` → flex-wrap | Sí | Sí | Sí (`flexWrap`) | Sí | | `gap` + `rowGap` / `columnGap` | Sí | Sí (`gap`, `gapX`, `gapY`) | Sí | Sí | | `inline` boolean (inline-flex) | Sí | Vía `display="inline-flex"` | Sí | Sí (`inline`) | -| `alignContent` (multi-line cross) | **No** — gap conocido | Sí | Sí | Sí | +| `alignContent` (multi-line cross) | Sí | Sí | Sí | Sí | | Item props heredados de Box | Sí (composición) | Sí | Sí (style-props todo en Box) | — split | | Responsive | Sí | Sí | Sí | Sí | @@ -64,7 +64,7 @@ intactos al Box raíz. | Gap | Disposición | Detalle | | --- | --- | --- | -| `alignContent` (multi-line cross-axis) | **implementar** | Radix + Chakra lo tienen. Útil cuando `wrap` está activo. Backlog Layout fixes. | +| `alignContent` (multi-line cross-axis) | ✅ implementado en `ec66999b` siguiente commit | Cubierto. | | Helpers `HStack` / `VStack` (alias direction-fijo) | **diferir** | Ergonomía Chakra. UIX usa ``. Reconsiderar si la fricción es real. | | `inline-flex` shorthand alternativo (e.g. ``) | **descartar** | `inline` boolean cubre el caso. | | Slot `divider` entre hijos (separator automático) | **diferir** | Patrón Chakra Stack. Más natural en Stack que en Flex. | diff --git a/src/uix/eidos/components/flex/flex.css b/src/uix/eidos/components/flex/flex.css index 570010fbc..1347929a6 100644 --- a/src/uix/eidos/components/flex/flex.css +++ b/src/uix/eidos/components/flex/flex.css @@ -9,6 +9,7 @@ align-items: var(--flex-align, stretch); justify-content: var(--flex-justify, flex-start); flex-wrap: var(--flex-wrap, nowrap); + align-content: var(--flex-align-content, normal); row-gap: var(--flex-row-gap, var(--box-gap, revert-layer)); column-gap: var(--flex-column-gap, var(--box-gap, revert-layer)); } diff --git a/src/uix/eidos/components/flex/flex.svelte b/src/uix/eidos/components/flex/flex.svelte index e1b20ce71..8d51055eb 100644 --- a/src/uix/eidos/components/flex/flex.svelte +++ b/src/uix/eidos/components/flex/flex.svelte @@ -19,6 +19,7 @@ align, justify, wrap = 'nowrap', + alignContent, rowGap, columnGap, style, @@ -35,6 +36,7 @@ pushStyleVar(decls, '--flex-align', eidos.resolve(align)); pushStyleVar(decls, '--flex-justify', eidos.resolve(justify)); pushStyleVar(decls, '--flex-wrap', eidos.resolve(wrap)); + pushStyleVar(decls, '--flex-align-content', eidos.resolve(alignContent)); pushStyleVar(decls, '--flex-row-gap', formatLayoutSpace(eidos.resolve(rowGap))); pushStyleVar(decls, '--flex-column-gap', formatLayoutSpace(eidos.resolve(columnGap))); return composeStyle(decls, style); diff --git a/src/uix/eidos/components/flex/types.ts b/src/uix/eidos/components/flex/types.ts index 9a206cf07..6a9072488 100644 --- a/src/uix/eidos/components/flex/types.ts +++ b/src/uix/eidos/components/flex/types.ts @@ -1,6 +1,7 @@ import type { ResponsiveProp } from '$uix/eidos/lib/types'; import type { BoxProps } from '../box/types'; import type { + LayoutAlignContent, LayoutAlignItems, LayoutFlexDirection, LayoutFlexWrap, @@ -19,6 +20,11 @@ export type FlexProps = Omit & { justify?: ResponsiveProp; /** `flex-wrap`. @default 'nowrap' */ wrap?: ResponsiveProp; + /** + * `align-content` — multi-line cross-axis alignment. Only takes effect + * when `wrap !== 'nowrap'` and the lines actually wrap. + */ + alignContent?: ResponsiveProp; /** `row-gap`. Overrides `gap` for the row axis. */ rowGap?: ResponsiveProp; /** `column-gap`. Overrides `gap` for the column axis. */ diff --git a/src/uix/eidos/components/grid/README.md b/src/uix/eidos/components/grid/README.md index 4b9c72696..b37788459 100644 --- a/src/uix/eidos/components/grid/README.md +++ b/src/uix/eidos/components/grid/README.md @@ -27,14 +27,14 @@ selectores `[data-box][data-grid] { … }`. | Capacidad | UIX | Radix Themes | Chakra UI | Mantine SimpleGrid | | --- | --- | --- | --- | --- | | `templateColumns` / `templateRows` | Sí (string libre) | Sí | Sí | — (sólo cols) | -| `columns` / `rows` shorthand (`columns={3}` → `repeat(3, 1fr)`) | **No** — gap conocido | Sí (`columns="3"`) | Sí (`columns={3}`) | Sí | +| `columns` / `rows` shorthand (`columns={3}` → `repeat(3, 1fr)`) | Sí | Sí (`columns="3"`) | Sí (`columns={3}`) | Sí | | `autoColumns` / `autoRows` / `autoFlow` | Sí | Sí | Sí | — | | `gap` + `rowGap` / `columnGap` | Sí | Sí | Sí | Sí | | `align` → align-items | Sí | Sí | Sí | — | | `justify` → justify-content | Sí | Sí | Sí | — | | `placeItems` / `placeContent` | Sí | Sí | Sí | — | -| `alignContent` (multi-line) | **No** — gap conocido | Sí | Sí | — | -| `inline` boolean (inline-grid) | **No** — gap conocido | Vía `display="inline-grid"` | Sí | — | +| `alignContent` (multi-line) | Sí | Sí | Sí | — | +| `inline` boolean (inline-grid) | Sí | Vía `display="inline-grid"` | Sí | — | | Item placement (`gridColumn`, `gridRow`, `gridArea`, `placeSelf`) | **En Box** (Radix) | En Box | En Box | — | | Responsive `breakpoints` | Sí | Sí | Sí | Sí | @@ -57,9 +57,9 @@ selectores `[data-box][data-grid] { … }`. | Gap | Disposición | Detalle | | --- | --- | --- | -| `columns` / `rows` numeric shorthand | **implementar** | Patrón Radix Themes (`columns="3"` → `repeat(3, 1fr)`). Ergonomía Mantine SimpleGrid. Backlog Layout fixes. | -| `inline` boolean | **implementar** | Paridad con Flex.inline. Trivial — display: inline-grid. | -| `alignContent` (multi-line cross-axis align) | **implementar** | Útil con `autoRows` + scroll. Radix lo tiene. | +| `columns` / `rows` numeric shorthand | ✅ implementado | Pasa template `repeat(N, minmax(0, 1fr))`. `templateColumns` string sigue ganando si se pasan ambos. | +| `inline` boolean | ✅ implementado | display: inline-grid. | +| `alignContent` (multi-line cross-axis align) | ✅ implementado | Útil con `autoRows` + scroll. | | Slot/Cell helpers (e.g. ``) | **diferir** | Box ya cumple. Reconsiderar si emerge un patrón estable. | | `subgrid` support | **diferir** | Soporte navegador todavía parcheado; esperar penetración. | diff --git a/src/uix/eidos/components/grid/grid.css b/src/uix/eidos/components/grid/grid.css index c3bfa2ce0..4cadaf9ef 100644 --- a/src/uix/eidos/components/grid/grid.css +++ b/src/uix/eidos/components/grid/grid.css @@ -16,6 +16,7 @@ grid-auto-flow: var(--grid-auto-flow, row); align-items: var(--grid-align, stretch); justify-content: var(--grid-justify, start); + align-content: var(--grid-align-content, normal); place-items: var(--grid-place-items, revert-layer); place-content: var(--grid-place-content, revert-layer); row-gap: var(--grid-row-gap, var(--grid-gap, revert-layer)); diff --git a/src/uix/eidos/components/grid/grid.svelte b/src/uix/eidos/components/grid/grid.svelte index 8198e6113..8abd508d4 100644 --- a/src/uix/eidos/components/grid/grid.svelte +++ b/src/uix/eidos/components/grid/grid.svelte @@ -14,6 +14,9 @@ import type { GridProps } from './types'; let { + inline = false, + columns, + rows, templateColumns, templateRows, autoColumns, @@ -24,6 +27,7 @@ gap, align, justify, + alignContent, placeItems, placeContent, style, @@ -34,15 +38,32 @@ const eidos = ActiveEidos.require(); + // Numeric `columns` / `rows` shorthand maps to `repeat(N, 1fr)`. + // Full template string from `templateColumns` / `templateRows` wins + // if both are passed. + const resolvedTemplateColumns = $derived.by(() => { + const tpl = eidos.resolve(templateColumns); + if (tpl) return tpl; + const n = eidos.resolve(columns); + return typeof n === 'number' && n > 0 ? `repeat(${n}, minmax(0, 1fr))` : undefined; + }); + const resolvedTemplateRows = $derived.by(() => { + const tpl = eidos.resolve(templateRows); + if (tpl) return tpl; + const n = eidos.resolve(rows); + return typeof n === 'number' && n > 0 ? `repeat(${n}, minmax(0, 1fr))` : undefined; + }); + const resolvedStyle = $derived.by(() => { const decls: string[] = []; - pushStyleVar(decls, '--grid-template-columns', eidos.resolve(templateColumns)); - pushStyleVar(decls, '--grid-template-rows', eidos.resolve(templateRows)); + pushStyleVar(decls, '--grid-template-columns', resolvedTemplateColumns); + pushStyleVar(decls, '--grid-template-rows', resolvedTemplateRows); pushStyleVar(decls, '--grid-auto-columns', eidos.resolve(autoColumns)); pushStyleVar(decls, '--grid-auto-rows', eidos.resolve(autoRows)); pushStyleVar(decls, '--grid-auto-flow', eidos.resolve(autoFlow)); pushStyleVar(decls, '--grid-align', eidos.resolve(align)); pushStyleVar(decls, '--grid-justify', eidos.resolve(justify)); + pushStyleVar(decls, '--grid-align-content', eidos.resolve(alignContent)); pushStyleVar(decls, '--grid-place-items', eidos.resolve(placeItems)); pushStyleVar(decls, '--grid-place-content', eidos.resolve(placeContent)); pushStyleVar(decls, '--grid-gap', formatLayoutSpace(eidos.resolve(gap))); @@ -52,6 +73,12 @@ }); - + {@render children?.()} diff --git a/src/uix/eidos/components/grid/types.ts b/src/uix/eidos/components/grid/types.ts index 39df95273..50e18b8bb 100644 --- a/src/uix/eidos/components/grid/types.ts +++ b/src/uix/eidos/components/grid/types.ts @@ -1,6 +1,7 @@ import type { ResponsiveProp } from '$uix/eidos/lib/types'; import type { BoxProps } from '../box/types'; import type { + LayoutAlignContent, LayoutAlignItems, LayoutGridAutoFlow, LayoutJustifyContent, @@ -8,9 +9,19 @@ import type { } from '../_layout/shared'; export type GridProps = Omit & { - /** `grid-template-columns`. */ + /** Use `inline-grid` instead of `grid`. @default false */ + inline?: boolean; + /** + * Numeric shorthand for `grid-template-columns: repeat(N, 1fr)`. Radix + * Themes pattern. Use `templateColumns` when you need a custom track + * list (e.g. `'200px 1fr auto'`). + */ + columns?: ResponsiveProp; + /** Numeric shorthand for `grid-template-rows: repeat(N, 1fr)`. */ + rows?: ResponsiveProp; + /** `grid-template-columns` — full track list. Takes precedence over `columns`. */ templateColumns?: ResponsiveProp; - /** `grid-template-rows`. */ + /** `grid-template-rows` — full track list. Takes precedence over `rows`. */ templateRows?: ResponsiveProp; /** `grid-auto-columns`. */ autoColumns?: ResponsiveProp; @@ -28,6 +39,8 @@ export type GridProps = Omit & { align?: ResponsiveProp; /** `justify-content`. */ justify?: ResponsiveProp; + /** `align-content` — multi-line cross-axis alignment. */ + alignContent?: ResponsiveProp; /** `place-items` shorthand. */ placeItems?: ResponsiveProp; /** `place-content` shorthand. */