fix: Einstellungskarten in feste Spalten statt Spaltenfluss

Beim Abrufen und beim Bearbeiten sprang die halbe Seite. Ursache war der
CSS-Spaltenfluss: Aendert eine Karte ihre Hoehe, verteilt der Browser ALLE
Karten neu auf die Spalten. Ich hatte das zweimal am Symptom kuriert
(Formular in den Dialog verschoben), aber die naechste wachsende Karte
brachte es zurueck.

Jetzt bekommt jede Karte fest ihre Spalte. Waechst eine, verschiebt sich
nur, was in derselben Spalte darunter steht. Die Spaltenzahl kommt aus der
tatsaechlichen Containerbreite (ResizeObserver), nicht aus der
Fensterbreite – in der schmalen Seitenleisten-Ansicht stimmte das sonst
nicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-22 20:54:03 +02:00
co-authored by Claude Opus 5
parent 899241f991
commit e93f565629
4 changed files with 53 additions and 20 deletions
+47
View File
@@ -0,0 +1,47 @@
import { Children, useEffect, useRef, useState } from "react";
/**
* Karten in feste Spalten verteilen.
*
* Warum nicht CSS: Ein Raster richtet die Karten zeilenweise aus – die zweite
* Reihe beginnt erst unter der höchsten Karte der ersten, was bei sehr
* unterschiedlich hohen Karten grosse Löcher hinterlässt. Der CSS-Spaltenfluss
* (`column-width`) füllt zwar schön auf, verteilt aber ALLE Karten neu, sobald
* eine ihre Höhe ändert. Beim Bearbeiten oder nach einem Abruf sprang deshalb
* die halbe Seite.
*
* Hier bekommt jede Karte fest ihre Spalte (reihum). Wächst eine, verschiebt
* sich nur, was in derselben Spalte darunter steht – alles andere bleibt liegen.
*/
export default function Kartenspalten({ children, min = 380, gap = "var(--sp-4)" }) {
const container = useRef(null);
const [spalten, setSpalten] = useState(1);
useEffect(() => {
const el = container.current;
if (!el) return undefined;
const messen = () => {
const breite = el.getBoundingClientRect().width;
setSpalten(Math.max(1, Math.floor((breite + 24) / (min + 24))));
};
messen();
const beobachter = new ResizeObserver(messen);
beobachter.observe(el);
return () => beobachter.disconnect();
}, [min]);
const karten = Children.toArray(children).filter(Boolean);
const verteilt = Array.from({ length: spalten }, () => []);
karten.forEach((karte, i) => verteilt[i % spalten].push(karte));
return (
<div ref={container} style={{ display: "flex", alignItems: "flex-start", gap }}>
{verteilt.map((spalte, i) => (
<div key={i} style={{ flex: "1 1 0", minWidth: 0, display: "flex",
flexDirection: "column", gap }}>
{spalte}
</div>
))}
</div>
);
}