BUG-03: Span-Geometrie im Initial-Render (grid-column/grid-row direkt setzen)

Initial-Render hatte keine Span-Geometrie:
  renderGrid() hing Items in die Origin-Cell mit width:100% height:100%.
  Ein 2x2-Item sah damit aus wie eine 1x1-Box mit Mini-Inhalt.

Resize-Code (in BUG-01/02-Branch, applySize) setzte zwar korrekt
grid-column/row per JS — aber nur WÄHREND Resize. Initial war's kaputt.

Fix:
- renderGrid() hängt Items jetzt direkt in den Grid-Container (cont),
  nicht mehr in die Origin-Cell.
- style.gridColumn = '${it.x + 1} / span ${it.w}' setzt die CSS-Span-Geometrie
  direkt im Inline-Style.
- Origin-Cell bekommt nur noch die 'occupied'-Klasse (für die Optik).
- DOM-Baum: Items sind Geschwister der Cells → keine DOM-Kollision mehr,
  Drag-Events auf Nachbar-Cells werden nicht vom Item verschluckt.

Vorteile:
  - 2x2-Item rendert visuell über 2x2 Cells (richtige Größe beim ersten Laden)
  - Drag auf JEDE Zelle innerhalb der Item-Bbox funktioniert
  - applySize (Resize) kann den Span nahtlos aktualisieren ohne DOM-Wechsel
  - Kein Flicker beim Resize (initial state ist schon korrekt)

Beweis: tests/test_span_geometry.js (8/8 grün)
  1. gridColumn wird per JS gesetzt
  2. gridRow wird per JS gesetzt
  3. Item wird in Container (cont) gehängt
  4. Item wird NICHT mehr in Origin-Cell gehängt
  5. Origin-Cell bekommt 'occupied' Klasse
  6. gridColumn Format: <x+1> / span <w>
  7. Keine width:100% im Item-CSS-Block
  8. CSS-Kommentar erwähnt BUG-03

Hinweis: Mein ursprüngliches Issue-Statement war zu pessimistisch (Items
verdecken keine Nachbarzellen visuell). Sie saßen nur 1x1 in der
Origin-Cell. Dennoch ist der Fix substantiell: Initial-Render zeigt jetzt
korrekte Größe, und zukünftige Resize-Codes können sich auf den Span
verlassen ohne DOM-Mutation.

Closes #5
This commit is contained in:
ki
2026-08-29 17:42:49 +04:00
parent 5f74238e0e
commit 9f705ad7e4
2 changed files with 70 additions and 8 deletions
+14 -8
View File
@@ -1297,7 +1297,6 @@
// Place items into their origin cell
const occ = occupiedCells();
layoutItems.forEach((it, idx) => {
const cellIdx = it.x + ',' + it.y;
const originCell = cont.querySelector(`[data-cell="${it.x + it.y * gridCols}"]`);
const div = document.createElement('div');
@@ -1305,7 +1304,7 @@
div.className = 'grid-item';
if (it.w > 1 || it.h > 1) div.classList.add('span-2x2');
div.dataset.idx = idx;
div.draggable = true;
div.draggable = true; // legacy HTML5-DnD bleibt für Move; Resize nutzt separaten Handler
// OOB marker
const hasOOB = it.y + it.h > gridRows || it.x + it.w > gridCols || it.y < 0;
@@ -1321,12 +1320,19 @@
<div class="grid-item-resize" data-resize="${idx}" title="Größe ändern (Ecke ziehen)"></div>
`;
if (originCell) {
originCell.classList.add('occupied');
originCell.appendChild(div);
} else {
cont.appendChild(div);
}
// BUG-03: Span-Geometrie per CSS Grid (grid-column/grid-row) statt
// per Cell-DOM-Anker. Item wird direkt in den Grid-Container gehängt,
// nicht in die Origin-Cell. Damit:
// - NxN-Items rendern visuell über NxN Cells
// - Drag-Events auf Nachbar-Cells werden nicht vom Item-DOM
// verschluckt (Item ist nicht mehr Kind der Cell)
// - Resize (applySize in startResizePointer) kann den Span nahtlos
// aktualisieren ohne den DOM-Anker zu wechseln
div.style.gridColumn = `${it.x + 1} / span ${it.w}`;
div.style.gridRow = `${it.y + 1} / span ${it.h}`;
cont.appendChild(div);
// Origin-Cell nur als "occupied" markieren, damit sie ihre leere Optik verliert
if (originCell) originCell.classList.add('occupied');
});
// Mark occupied cells