From e2d2347524f1ebd4b0f33df9089478ce4566a337 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Mon, 27 Jul 2026 17:58:01 +0200 Subject: [PATCH] Web: DataTable - Spalten ein-/ausblenden (Spalten-Waehler) Neuer 'Spalten'-Button in der Tabellen-Toolbar oeffnet ein Popover mit einer Checkbox je Spalte. Abgewaehlte Spalten werden ausgeblendet (Kopf, Filterzeile, Zellen), wieder anhaken zeigt sie an alter Position. Die Auswahl merkt sich der Browser (localStorage, je Tabelle), mindestens eine Spalte bleibt sichtbar, 'Spalten zuruecksetzen' zeigt wieder alle. Co-Authored-By: Claude Opus 4.8 --- web/src/components/DataTable.jsx | 60 ++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/web/src/components/DataTable.jsx b/web/src/components/DataTable.jsx index cb61d9e..298e492 100644 --- a/web/src/components/DataTable.jsx +++ b/web/src/components/DataTable.jsx @@ -44,8 +44,14 @@ export default function DataTable({ const [filterShown, setFilterShown] = useState(true); const [openFilter, setOpenFilter] = useState(null); const [collapsed, setCollapsed] = useState(() => new Set()); + const [hidden, setHidden] = useState( + () => new Set((loadPersisted(id).hidden || []).filter((k) => colByKey[k])), + ); + const [chooserOpen, setChooserOpen] = useState(false); - useEffect(() => { savePersisted(id, { order, widths }); }, [id, order, widths]); + useEffect(() => { + savePersisted(id, { order, widths, hidden: [...hidden] }); + }, [id, order, widths, hidden]); // Containerbreite messen, damit die grow-Spalte den Rest füllt. const wrapRef = useRef(null); @@ -59,7 +65,39 @@ export default function DataTable({ return () => ro.disconnect(); }, []); - const orderedCols = order.map((k) => colByKey[k]).filter(Boolean); + const allOrdered = order.map((k) => colByKey[k]).filter(Boolean); + const orderedCols = allOrdered.filter((c) => !hidden.has(c.key)); + + // Spalten-Wähler-Popover. + const chooserBtn = useRef(null); + const chooserPop = useRef(null); + const [chooserPos, setChooserPos] = useState(null); + useLayoutEffect(() => { + if (chooserOpen && chooserBtn.current) { + const r = chooserBtn.current.getBoundingClientRect(); + setChooserPos({ left: Math.min(r.left, window.innerWidth - 240), top: r.bottom + 4 }); + } + }, [chooserOpen]); + useEffect(() => { + if (!chooserOpen) return undefined; + function onDoc(e) { + if (chooserBtn.current?.contains(e.target) || chooserPop.current?.contains(e.target)) return; + setChooserOpen(false); + } + document.addEventListener("mousedown", onDoc); + return () => document.removeEventListener("mousedown", onDoc); + }, [chooserOpen]); + + function toggleHidden(key) { + setHidden((s) => { + const n = new Set(s); + if (n.has(key)) { n.delete(key); return n; } + // Mindestens eine Spalte sichtbar lassen. + if (allOrdered.filter((c) => !n.has(c.key) && c.key !== key).length === 0) return s; + n.add(key); + return n; + }); + } const isGrow = (c) => c.grow && widths[c.key] == null; const fixedWidth = (c) => widths[c.key] ?? c.width ?? DEFAULT_WIDTH; @@ -224,6 +262,7 @@ export default function DataTable({ function resetColumns() { setOrder(columns.map((c) => c.key)); setWidths({}); + setHidden(new Set()); savePersisted(id, {}); } @@ -235,9 +274,26 @@ export default function DataTable({ Filter + {toolbarExtra} + {chooserOpen && chooserPos && createPortal( +
+
+ {allOrdered.map((c) => ( + + ))} +
+
, + document.body, + )}