Calendar/.workbench/inspiration.md
Janus C. H. Knudsen c16e432b29 Refactors rendering pipeline with flexible data handling
Removes hardcoded data from render context, allowing more dynamic renderer implementation

Changes include:
- Decoupling data retrieval from render context
- Simplifying renderer interface
- Supporting more flexible data traversal strategies

Prepares for more modular and adaptable rendering approach
2025-12-09 15:24:19 +01:00

5.1 KiB
Raw Blame History

Selvfølgelig—her er en opdateret, selvstændig .md-spec, som understøtter variable antal resources per team, dynamisk kolonnebredde, ingen inline layout-styles, pipelinerendering i grupper, og CSS-controlling via custom properties.

Kopier → gem som fx: grid-render-pipeline-dynamic-columns.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

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.

.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:

.team-header {
  grid-column: span var(--team-cols, 1);
  font-weight: 700;
  border-bottom: 1px solid #ccc;
  padding: 4px 2px;
}

Resources (2. række)

.resource-cell {
  padding: 4px 2px;
  background: #f5f5f5;
  border-radius: 4px;
  text-align: center;
  font-weight: 600;
}

Dates (3. række)

.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)

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:

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

interface Renderer {
  id: string;
  next: Renderer | null;
  render(ctx: RenderContext): void;
}

Factory

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

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

const pipeline = buildPipeline([
  teamsRenderer,
  resourcesRenderer,
  datesRenderer
]);

pipeline.run(ctx);

🚀 Kørsel

// 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.

---