DataTable: neue Spalten ruecken in die gespeicherte Reihenfolge nach
Die Gruppen-Spalte fehlte im Spalten-Menue - aus zwei Gruenden, und der zweite ist der ernstere. 1) order wurde EINMAL beim Mounten berechnet: gespeicherte Reihenfolge plus alles, was zu dem Zeitpunkt in columns stand. Aendert eine Seite ihre Spalten danach - die Artikelliste zeigt "Verwaltung" nur bei Gegenstaenden -, fehlt die neue Spalte in order und faellt damit aus allOrdered heraus. Sie ist dann nicht bloss aus dem Menue verschwunden, sondern aus der TABELLE. Ein useEffect zieht fehlende Schluessel jetzt nach; angehaengt, nicht einsortiert, damit eine selbst gewaehlte Reihenfolge nicht umspringt. 2) Die Liste im Menue war fest auf 240px begrenzt. Bei zehn Spalten stand der letzte Eintrag ausserhalb, und weil macOS die Bildlaufleiste ausblendet, sah es aus, als gaebe es ihn nicht. Die Hoehe richtet sich jetzt nach dem freien Platz bis zum unteren Bildrand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,22 @@ export default function DataTable({
|
|||||||
const rest = columns.map((c) => c.key).filter((k) => !saved.includes(k));
|
const rest = columns.map((c) => c.key).filter((k) => !saved.includes(k));
|
||||||
return [...saved, ...rest];
|
return [...saved, ...rest];
|
||||||
});
|
});
|
||||||
|
// `order` ist eine Momentaufnahme vom Mounten. Seiten duerfen ihre Spalten
|
||||||
|
// aber danach aendern (die Artikelliste zeigt „Verwaltung" nur bei
|
||||||
|
// Gegenstaenden), und eine neu hinzugefuegte Spalte steht ohnehin nie in der
|
||||||
|
// gespeicherten Reihenfolge. Fehlt sie dort, faellt sie aus allOrdered heraus
|
||||||
|
// und ist WEG – nicht nur aus dem Spalten-Menue, sondern aus der Tabelle.
|
||||||
|
// Deshalb hier nachziehen. Angehaengt, nicht einsortiert: eine selbst
|
||||||
|
// gewaehlte Reihenfolge soll nicht umspringen.
|
||||||
|
const schluessel = columns.map((c) => c.key).join("|");
|
||||||
|
useEffect(() => {
|
||||||
|
setOrder((alt) => {
|
||||||
|
const fehlend = columns.map((c) => c.key).filter((k) => !alt.includes(k));
|
||||||
|
return fehlend.length ? [...alt, ...fehlend] : alt;
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [schluessel]);
|
||||||
|
|
||||||
const [widths, setWidths] = useState(() => loadPersisted(id).widths || {});
|
const [widths, setWidths] = useState(() => loadPersisted(id).widths || {});
|
||||||
// Filter + Sortierung bleiben erhalten (localStorage je Tabelle), bis man
|
// Filter + Sortierung bleiben erhalten (localStorage je Tabelle), bis man
|
||||||
// „Filter löschen" drückt oder anders sortiert – auch nach Navigieren/Neuladen.
|
// „Filter löschen" drückt oder anders sortiert – auch nach Navigieren/Neuladen.
|
||||||
@@ -98,7 +114,14 @@ export default function DataTable({
|
|||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (chooserOpen && chooserBtn.current) {
|
if (chooserOpen && chooserBtn.current) {
|
||||||
const r = chooserBtn.current.getBoundingClientRect();
|
const r = chooserBtn.current.getBoundingClientRect();
|
||||||
setChooserPos({ left: Math.min(r.left, window.innerWidth - 240), top: r.bottom + 4 });
|
// Hoehe an den freien Platz koppeln. Eine feste Obergrenze schnitt die
|
||||||
|
// letzten Eintraege ab, ohne dass man es sah – auf macOS blendet das
|
||||||
|
// System die Bildlaufleiste aus, und dann fehlt die Spalte scheinbar.
|
||||||
|
setChooserPos({
|
||||||
|
left: Math.min(r.left, window.innerWidth - 240),
|
||||||
|
top: r.bottom + 4,
|
||||||
|
maxHeight: Math.max(180, window.innerHeight - r.bottom - 24),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [chooserOpen]);
|
}, [chooserOpen]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -316,7 +339,7 @@ export default function DataTable({
|
|||||||
</div>
|
</div>
|
||||||
{chooserOpen && chooserPos && createPortal(
|
{chooserOpen && chooserPos && createPortal(
|
||||||
<div ref={chooserPop} className="dt-pop" style={{ left: chooserPos.left, top: chooserPos.top }}>
|
<div ref={chooserPop} className="dt-pop" style={{ left: chooserPos.left, top: chooserPos.top }}>
|
||||||
<div className="dt-pop-list">
|
<div className="dt-pop-list" style={{ maxHeight: chooserPos.maxHeight }}>
|
||||||
{allOrdered.map((c) => (
|
{allOrdered.map((c) => (
|
||||||
<label key={c.key} className="dt-pop-opt">
|
<label key={c.key} className="dt-pop-opt">
|
||||||
<input type="checkbox" checked={!hidden.has(c.key)} onChange={() => toggleHidden(c.key)} />
|
<input type="checkbox" checked={!hidden.has(c.key)} onChange={() => toggleHidden(c.key)} />
|
||||||
|
|||||||
@@ -646,7 +646,9 @@ td select { width: auto; min-width: 0; max-width: 100%; }
|
|||||||
}
|
}
|
||||||
.dt-pop-search { width: 100%; margin: 0 0 var(--sp-2); padding: 5px 8px; font-size: 0.85rem; }
|
.dt-pop-search { width: 100%; margin: 0 0 var(--sp-2); padding: 5px 8px; font-size: 0.85rem; }
|
||||||
.dt-pop-actions { display: flex; gap: var(--sp-3); margin-bottom: var(--sp-2); }
|
.dt-pop-actions { display: flex; gap: var(--sp-3); margin-bottom: var(--sp-2); }
|
||||||
.dt-pop-list { max-height: 240px; overflow-y: auto; display: flex; flex-direction: column; }
|
/* Die Hoehe setzt DataTable je nach freiem Platz (siehe chooserPos); hier
|
||||||
|
nur der Notnagel, falls sie einmal fehlt. */
|
||||||
|
.dt-pop-list { max-height: 60vh; overflow-y: auto; display: flex; flex-direction: column; }
|
||||||
.dt-pop-opt { display: flex; align-items: center; gap: 8px; padding: 3px 4px; font-size: 0.85rem; cursor: pointer; margin: 0; }
|
.dt-pop-opt { display: flex; align-items: center; gap: 8px; padding: 3px 4px; font-size: 0.85rem; cursor: pointer; margin: 0; }
|
||||||
.dt-pop-opt:hover { background: var(--surface-2); }
|
.dt-pop-opt:hover { background: var(--surface-2); }
|
||||||
/* Ohne diese Ausnahme erben die Checkboxen die globale input-Regel (width:100%,
|
/* Ohne diese Ausnahme erben die Checkboxen die globale input-Regel (width:100%,
|
||||||
|
|||||||
Reference in New Issue
Block a user