Files
nexarch/web/dms-browser/lib/virtualization.ts
T
patrickandClaude Sonnet 5 e973457d1a DOC-10: Ordner-Navigation & Dokumentenliste
internal/folderapi: neue Backend-Voraussetzung - GET /folders/{id}/
children (lazy Baum-Ladung), .../breadcrumb (ueber DOC-09s Materialized
Path), .../documents (Dokumentenliste, whitelisted Sortierspalten).
folder.go ergaenzt ListChildren/Breadcrumb (additiv, keine bestehende
Funktion geaendert).

web/dms-browser: Next.js/React/TypeScript. Navigationsbaum laedt Kinder
lazy je Ebene, Dokumentenliste ist virtualisiert (lib/virtualization.ts,
abhaengigkeitsfrei - nur sichtbarer Ausschnitt + Overscan wird gerendert),
Mehrfachauswahl (lib/selection.ts: Klick/Strg/Umschalt), Tastatur-
navigation im Baum (lib/treeKeyboard.ts). Aktueller Ordner steckt in der
URL (Catch-all-Route /browse/[[...path]]) statt nur im React-State - der
Browser verwaltet Vor-/Zurueck-Navigation selbst.

Echter Bug beim Schreiben der Virtualisierungs-Tests gefunden und
behoben: startIndex war bei scrollTop jenseits des tatsaechlichen Inhalts
nicht auf totalRows geklemmt (haette zu ungueltigem slice() gefuehrt).

Pruefungen:
1. Liste mit 10.000 Zeilen scrollt ruckelfrei - Virtualisierung rendert
   nachweislich nur ~20 statt 10.000 Zeilen
2. Navigation vollstaendig per Tastatur - 5 Vitest-Tests fuer Pfeiltasten/
   Enter im Baum
3. Zustand bleibt beim Zurueck-Navigieren erhalten - Ordner-ID in der URL,
   Browser-History uebernimmt die Wiederherstellung

Regressionsfrei: alle bestehenden Go-Pakete PASS.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-17 20:22:09 +02:00

42 lines
1.7 KiB
TypeScript

// Reine Virtualisierungs-Logik (Akzeptanzkriterium 2: Liste bleibt bei
// vielen Einträgen flüssig / Pflichtprüfung 1: 10.000 Zeilen ruckelfrei)
// — bewusst ohne Drittanbieter-Bibliothek: fixe Zeilenhöhe genügt für eine
// tabellarische Dokumentenliste, die Berechnung ist eine reine Funktion
// von Scrollposition/Containerhöhe, unabhängig vom DOM testbar.
export interface VisibleRange {
startIndex: number;
endIndex: number; // exklusiv
offsetY: number; // Pixel-Versatz des ersten sichtbaren Elements
}
// computeVisibleRange berechnet, welche Zeilen tatsächlich gerendert
// werden müssen — nur der sichtbare Bereich plus overscan-Puffer
// (verhindert kurzes Weiß-Aufblitzen bei schnellem Scrollen), NICHT alle
// totalRows Zeilen. Das ist der gesamte Trick der Virtualisierung.
export function computeVisibleRange(
scrollTop: number,
containerHeight: number,
rowHeight: number,
totalRows: number,
overscan = 5
): VisibleRange {
if (totalRows <= 0 || rowHeight <= 0) {
return { startIndex: 0, endIndex: 0, offsetY: 0 };
}
const firstVisible = Math.floor(scrollTop / rowHeight);
const visibleCount = Math.ceil(containerHeight / rowHeight);
// Sowohl start- als auch endIndex muessen auf totalRows geklemmt werden
// - ein (z.B. veralteter) scrollTop jenseits des tatsaechlichen Inhalts
// durfte sonst einen startIndex > totalRows liefern.
const startIndex = Math.min(Math.max(0, firstVisible - overscan), totalRows);
const endIndex = Math.min(totalRows, firstVisible + visibleCount + overscan);
return { startIndex, endIndex, offsetY: startIndex * rowHeight };
}
export function totalContentHeight(totalRows: number, rowHeight: number): number {
return totalRows * rowHeight;
}