Vier Anzeigefehler aus dem Gebrauch

"NaN Glaeser" auf der Karte "Artikel im Blick". Die Karte reichte einen
ARTIKEL an articlePrimary() weiter, das fuer CHARGEN geschrieben ist und
die Menge unter dem Namen `quantity` sucht. Ein Artikel nennt sie
`stock` - also undefined / 190 = NaN. Neu bestandPrimary() und
bestandSecondary() in units.js als die eine Stelle, an der zwischen den
beiden Namen uebersetzt wird; sie delegieren an die vorhandenen
Formatierer, damit es nicht zwei Fassungen gibt. Steckte seit 8959814
drin.

Dazu gibt fmt() fuer nicht-endliche Zahlen jetzt einen Strich aus statt
"NaN". Dem Benutzer "NaN" hinzuschreiben ist nie richtig - der eigentliche
Fehler wird trotzdem an der Quelle behoben, nicht hier.

Die Spalte "Verbrauch" schnitt ab. Zwei beschriftete Knoepfe brauchen
rund 230 px, die Spalte hatte 190, und .datatable ist table-layout:fixed
mit overflow:hidden. Weil die Zelle rechtsbuendig ist, fiel der ANFANG
weg - sichtbar stand dort "rbrauchen". Jetzt 240 px, dazu flex-wrap in
der Zelle, damit die Knoepfe umbrechen statt abzuschneiden, wenn jemand
die Spalte von Hand schmaler zieht.

Die LETZTE Spalte liess sich nicht verbreitern. Ihr Anfasser sitzt exakt
am rechten Rand des Rollbereichs (gemessen: 0 px Luft), und nach rechts
ist dort Schluss - der Zeiger kann nicht weiter, also kam nie ein
positiver Abstand zum Startpunkt zustande. Der Resize rechnet jetzt mit
der BEWEGUNG seit dem letzten Schritt statt mit dem Abstand zum Start,
und in der Randzone waechst die Spalte von selbst weiter, solange die
Taste haelt; ein Taktgeber treibt das an, denn ein stehender Zeiger
meldet keine Bewegung. Die Tabelle rollt dabei mit, damit der Anfasser
unter dem Zeiger bleibt.

"3 Glas" statt "3 Glaeser" in der Chargenliste, waehrend die Uebersicht
"3 Glaeser" schrieb. Die Mehrzahlformen liegen in einer Modulvariablen,
weil auch Nicht-Komponenten sie brauchen - eine Aenderung daran loest
aber kein Neuzeichnen aus. Wer seine Liste zeichnete, bevor
/package-types zurueck war, blieb bei der Einzahl. Ein Zaehler im
Einstellungs-Kontext zieht die Formatierer jetzt nach.

Von Hand geprueft: Karte zeigt "3 Glaeser / 570 g"; beide Knoepfe stehen
vollstaendig in der Spalte; die letzte Spalte waechst beim Halten am
Rand von 240 auf ueber 470 und bleibt beim Loslassen stehen, waehrend
eine mittlere Spalte weiterhin exakt der Ziehstrecke folgt (170 -> 230);
Chargenliste schreibt "3 Glaeser".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-02 15:39:44 +02:00
co-authored by Claude Opus 5
parent 4972feef09
commit 98db3339a2
5 changed files with 95 additions and 12 deletions
+49 -6
View File
@@ -265,34 +265,77 @@ export default function DataTable({
}
// --- Resize (Pointer) ---
//
// Gerechnet wird mit der BEWEGUNG seit dem letzten Schritt, nicht mit dem
// Abstand zum Startpunkt. Nur so laesst sich die letzte Spalte verbreitern:
// Ihr Anfasser sitzt am rechten Rand, und dort ist fuer den Zeiger Schluss -
// nach rechts gibt es kein Fenster mehr. Steht er in der Randzone, waechst die
// Spalte deshalb von selbst weiter, Schritt fuer Schritt, solange die Taste
// haelt. Ein Taktgeber treibt das an, denn ein stehender Zeiger meldet keine
// Bewegung mehr.
const resizing = useRef(null);
const rollTimer = useRef(null);
const RAND = 36; // Breite der Randzone
const SCHRITT = 14; // Pixel je Takt in der Randzone
function startResize(e, c) {
e.preventDefault(); e.stopPropagation();
const startW = widthOf(c);
// Die flexible (grow-)Spalte auf ihre aktuelle Pixelbreite einfrieren. Sonst
// "klaut" das Breiterziehen einer Spalte der grow-Spalte den Platz – dann
// "klaut" das Breiterziehen einer Spalte der grow-Spalte den Platz - dann
// schrumpft die Nachbarspalte und alles wandert nach links. Danach verhalten
// sich die Spalten wie in Excel (unabhängig, notfalls scrollt die Tabelle).
// sich die Spalten wie in Excel (unabhaengig, notfalls scrollt die Tabelle).
setWidths((old) => {
const next = { ...old };
for (const gc of growCols) if (next[gc.key] == null) next[gc.key] = Math.round(widthOf(gc));
return next;
});
resizing.current = { key: c.key, startX: e.clientX, startW };
resizing.current = { key: c.key, breite: widthOf(c), zeigerX: e.clientX, letzterX: e.clientX };
rollTimer.current = window.setInterval(anwenden, 30);
window.addEventListener("pointermove", onResizeMove);
window.addEventListener("pointerup", endResize);
}
function anwenden() {
const r = resizing.current; if (!r) return;
let delta = r.zeigerX - r.letzterX;
r.letzterX = r.zeigerX;
const el = wrapRef.current;
let amRand = false;
if (el) {
const kasten = el.getBoundingClientRect();
if (r.zeigerX > kasten.right - RAND) { delta += SCHRITT; amRand = true; }
else if (r.zeigerX < kasten.left + RAND) delta -= SCHRITT;
}
if (!delta) return;
const neu = Math.max(60, r.breite + delta);
if (neu === r.breite) return;
r.breite = neu;
setWidths((old) => ({ ...old, [r.key]: Math.round(neu) }));
// Mitlaufen, damit der Anfasser unter dem Zeiger bleibt, waehrend die
// Tabelle nach rechts waechst.
if (amRand && el) requestAnimationFrame(() => { el.scrollLeft = el.scrollWidth; });
}
function onResizeMove(e) {
const r = resizing.current; if (!r) return;
const w = Math.max(60, Math.round(r.startW + (e.clientX - r.startX)));
setWidths((old) => ({ ...old, [r.key]: w }));
r.zeigerX = e.clientX;
anwenden();
}
function endResize() {
resizing.current = null;
if (rollTimer.current) { window.clearInterval(rollTimer.current); rollTimer.current = null; }
window.removeEventListener("pointermove", onResizeMove);
window.removeEventListener("pointerup", endResize);
}
// Beim Verlassen der Seite mitten im Ziehen bliebe sonst ein Taktgeber laufen.
useEffect(() => () => {
if (rollTimer.current) window.clearInterval(rollTimer.current);
}, []);
// --- Reorder (HTML5 Drag) ---
const dragKey = useRef(null);
const [dropKey, setDropKey] = useState(null);