Selvfølgelig—her er en **opdateret, selvstændig `.md`-spec**, som **understøtter variable antal resources per team**, dynamisk kolonnebredde, ingen inline layout-styles, pipeline‐rendering i grupper, og CSS-controlling via custom properties. Kopier → gem som fx: `grid-render-pipeline-dynamic-columns.md` --- ````md # Grid Render Pipeline — Dynamic Columns Spec Denne specifikation beskriver en generisk render-pipeline til at bygge et dynamisk CSS Grid layout, hvor hver "gruppe" (teams, resources, dates) har sin egen renderer og pipeline-styring. Layoutet understøtter **variable antal resources pr. team** og beregner automatisk antal kolonner. Ingen inline-styles til positionering anvendes. --- ## ✨ Formål - Ét globalt CSS Grid. - Variabelt antal resources pr. team → dynamisk antal kolonner. - CSS-grid auto-placerer rækker. - Ingen inline styling af layout (ingen `element.style.gridRow = ...`). - CSS custom properties bruges til at definere dynamiske spænder. - Renderere har ens interface og bindes i pipeline. - `pipeline.run(ctx)` executer alle renderers i rækkefølge. - Hver renderer kan hente sin egen data (API, async osv.). --- ## 🧩 Data Model ```ts type DateString = string; interface Resource { id: string; name: string; dates: DateString[]; } interface Team { id: string; name: string; resources: Resource[]; } ```` --- ## 🧠 Context ```ts interface RenderContext { grid: HTMLElement; // root grid container teams: Team[]; // data } ``` `grid` er HTML-elementet med `display:grid`, og `teams` er data. --- ## 🎨 CSS Layout Grid kolonner bestemmes dynamisk via CSS variablen `--total-cols`. ```css .grid { display: grid; grid-template-columns: repeat(var(--total-cols), minmax(0, 1fr)); gap: 6px 10px; } .cell { font-size: 0.9rem; } ``` ### Teams (øverste række) Hver team-header spænder **antal resources for team'et**: ```css .team-header { grid-column: span var(--team-cols, 1); font-weight: 700; border-bottom: 1px solid #ccc; padding: 4px 2px; } ``` ### Resources (2. række) ```css .resource-cell { padding: 4px 2px; background: #f5f5f5; border-radius: 4px; text-align: center; font-weight: 600; } ``` ### Dates (3. række) ```css .dates-cell { padding: 2px 0; } .dates-list { display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; } .date-pill { padding: 3px 6px; background: #e3e3e3; border-radius: 4px; font-size: 0.8rem; } ``` --- ## 🔧 Beregning af kolonner **Total cols = sum(resources.length for all teams)** ```ts const totalCols = ctx.teams.reduce((sum, t) => sum + t.resources.length, 0); ctx.grid.style.setProperty('--total-cols', totalCols.toString()); ``` For hvert team defineres hvor mange kolonner det spænder: ```ts cell.style.setProperty('--team-cols', team.resources.length.toString()); ``` > Bemærk: vi bruger **kun CSS vars** til layoutparametre – ikke inline > grid-row/grid-column. --- ## ⚙ Renderer Interface ```ts interface Renderer { id: string; next: Renderer | null; render(ctx: RenderContext): void; } ``` ### Factory ```ts function createRenderer(id: string, fn: (ctx: RenderContext) => void): Renderer { return { id, next: null, render(ctx) { fn(ctx); if (this.next) this.next.render(ctx); } }; } ``` --- ## 🧱 De tre render-lag (grupper) ### Teams * Appender én `.team-header` per team. * Sætter `--team-cols`. ### Resources * Appender én `.resource-cell` per resource. * Foregår i teams-orden → CSS auto-row sørger for næste række. ### Dates * Appender én `.dates-cell` per resource. * Hver celle indeholder flere `.date-pill`. Append-rækkefølge giver 3 rækker automatisk: 1. teams, 2) resources, 3) dates. --- ## 🔗 Pipeline ```ts function buildPipeline(renderers: Renderer[]) { for (let i = 0; i < renderers.length - 1; i++) { renderers[i].next = renderers[i + 1]; } const first = renderers[0] ?? null; return { run(ctx: RenderContext) { if (first) first.render(ctx); } }; } ``` ### Brug ```ts const pipeline = buildPipeline([ teamsRenderer, resourcesRenderer, datesRenderer ]); pipeline.run(ctx); ``` --- ## 🚀 Kørsel ```ts // 1) beregn total kolonner const totalCols = ctx.teams.reduce((sum, t) => sum + t.resources.length, 0); ctx.grid.style.setProperty('--total-cols', totalCols); // 2) pipeline pipeline.run(ctx); ``` CSS klarer resten. --- ## 🧽 Principper * **Ingen inline style-positionering**. * **CSS Grid** owner layout. * **JS** owner data & rækkefølge. * **Renderers** er udskiftelige og genbrugelige. * **Append i grupper** = rækker automatisk. * **CSS vars** styrer spans dynamisk. --- ## ✔ TL;DR * Grid-cols bestemmes ud fra data. * Team-header `span = resources.length`. * Append rækkefølge = rækker. * Renderere i pipeline. * Ingen koordinater, ingen inline layout-styles. ``` --- ```