tuix-table
Eine Tabelle mit Slot-Zellen, Kartenlayout auf Mobile und Sortierung und Pagination, die die Anwendung steuert.
tuix-table zeichnet die Zeilen, die es bekommt. Es sortiert, filtert und
schneidet sie nie von selbst.
Sortierung und Pagination sind gesteuert. Die Tabelle meldet, was der Nutzer angeklickt hat, und die Anwendung entscheidet, was danach passiert:
Die Tabelle feuert ein Event. sort, next-page, previous-page oder page-size-change. Die Tabelle
selbst ändert nichts.
Der eigene Code antwortet. Er sortiert oder holt die Zeilen und schreibt data und den passenden Zustand
zurück auf das Element.
In der Tabelle bewegt sich nichts, bis der letzte Schritt läuft. Jedes Beispiel unten zeigt ihn in Angular, Vue und React.
Eine erste Tabelle
Zwei Properties genügen: headers beschreibt die Spalten, data hält die
Zeilen.
Jede Zeile braucht eine eindeutige id. Sie ist der Schlüssel beim Rendern und benennt die Slots der Zellen.
Spalten
Ein Objekt pro Spalte. Nötig sind nur dataKey und width.
| Feld | Typ | Wofür |
|---|---|---|
dataKey | keyof TRowData | Welches Feld der Zeile gedruckt wird. Nötig |
width | number | Die relative Breite der Spalte. Nötig |
name | string | Der Titel in der Kopfzeile |
sortKey | keyof TRowData | Macht die Spalte sortierbar, siehe Sortieren |
isSlot | boolean | Statt des Werts einen Slot drucken |
slotName | string | Der Basisname dieses Slots. Nötig mit isSlot |
cellColor | (row) => string | undefined | Eine TextVariant für den Text der Zelle, Zeile für Zeile |
cellBackgroundColor | (row) => string | undefined | Eine CSS-Farbe für die Zelle, Zeile für Zeile |
Spaltenbreiten
width ist ein Gewicht, keine Pixelbreite. Die Breiten aller Spalten werden
summiert und jede Spalte bekommt ihren Anteil, 10 / 30 / 20 und 1 / 3 / 2
ergeben also dasselbe Layout. Sind alle Breiten 0, teilen sich die Spalten den
Platz gleichmässig.
Slot-Zellen
Mit isSlot: true druckt die Zelle <slot name="{slotName}-{row.id}"> statt
eines Werts. Die id der Zeile steckt im Namen des Slots, jede Zelle hat also
ihren eigenen:
Ein Slot nimmt so viele Elemente auf, wie die Zeile braucht. Das Beispiel legt
ein Archivieren- und ein Benachrichtigen-Icon in jede Zelle. Die Framework-Tabs
zeigen dasselbe Paar als @for-Block, als v-for und als map.
Sortieren
Die Tabelle zeichnet die Bedienelemente. Sortiert wird in der Anwendung.
Eine Spalte mit sortKey bekommt das Pfeilpaar, wird fokussierbar und reagiert
auf einen Klick, auf Enter und auf die Leertaste. Unten ist jede Spalte
sortierbar:
Die drei Schritte
Die Spalte kennzeichnen. sortKey nennt das Feld, nach dem geordnet wird. Es muss nicht der dataKey
sein, eine formatierte Spalte oder eine mit Slot kann also nach jedem Feld der Zeile sortieren.
Auf sort hören. Das Detail trägt die Spalte und die Richtung, die die Tabelle verlangt: asc für eine
neue Spalte, und die Gegenrichtung, wenn sortBy sie bereits nennt.
Sortieren und den Zustand zurückschreiben. data bekommt die neue Reihenfolge, sortBy und
sortDirection sagen der Kopfzeile, wo sie steht.
Wird nur data umsortiert, bewegen sich die Zeilen, während die Kopfzeile die vorige Spalte hervorhebt. Pfeil und
aria-sort richten sich nach sortBy und sortDirection, nicht nach dem Klick.
Die Zeilen sortieren
Array.prototype.sort sortiert an Ort und Stelle, also eine Kopie sortieren. Das
neue Array ist auch das, was React, Vue und Angular zum Neuzeichnen brauchen:
Ein paar Punkte dazu:
- Nach Typ vergleichen. Der Standardvergleich läuft über Text und stellt
damit
10vor2. Zahlen subtrahieren, Text durchlocaleCompareschicken und Daten über ihre Zeitstempel vergleichen. - Festlegen, wohin leere Werte fallen. Ein Feld, das
nullsein kann, braucht eine eigene Regel, damit diese Zeilen immer auf derselben Seite landen. - Eine Spalte auf einmal. Das Detail trägt nie einen zweiten Key. Eine Ordnung über mehrere Spalten gehört dem eigenen Code, hinter demselben Event.
- Paginierte Tabellen sortieren den ganzen Satz, nicht die sichtbare Seite.
Auf dem Server sortieren, die erste Seite der neuen Ordnung holen und
currentPagezurück auf1setzen. Die Tabelle setzt sie nicht selbst zurück. sortByerst setzen, wenn eine Spalte angeklickt wurde. Vorher ist kein Pfeil hervorgehoben und die Zeilen stehen so, wie sie übergeben wurden.
Pagination
Derselbe Vertrag wie beim Sortieren: die Fusszeile fragt, der eigene Code antwortet. Die Demo blättert durch 23 Zeilen.
Die Fusszeile einschalten. paginated zeigt sie, totalItems ist die Zahl über alle Seiten, und
pageSizeOptions füllt die Auswahl der Seitengrösse.
Auf die drei Events hören. next-page, previous-page und page-size-change tragen alle
{ page, pageSize }, also die Seite, die die Tabelle verlangt.
Die Seite zurückschreiben. data bekommt die Zeilen dieser Seite, currentPage und currentSize den
Zustand.
Die Pfeile stoppen bei der ersten und bei der letzten Seite, die die Tabelle aus
totalItems und der Seitengrösse berechnet. Eine andere Seitengrösse verlangt
immer Seite 1.
Laden
isLoadingData ersetzt die Zeilen durch einen Loader. Kopfzeile und
Pagination-Fusszeile bleiben stehen, und die Auswahl der Seitengrösse ist
deaktiviert:
loader bestimmt sein Aussehen, jedes Feld ist optional:
| Feld | Typ | Standard | Wofür |
|---|---|---|---|
size | string | '14px' | Der Durchmesser eines Punkts |
color | string | Palette.MAIN | Die Farbe der Punkte |
visible | boolean | true | false blendet den Loader ganz aus |
ariaLabel | string | 'Loading' | Was ein Screenreader ansagt |
wrapperStyle | Record<string, string | number> | Keiner | Überschreibt den zentrierenden Wrapper |
Verhalten auf kleinen Bildschirmen
| Viewport | Layout |
|---|---|
| Über 1280px | Tabelle |
| 640px bis 1280px | Tabelle, oder Karten mit showMobileTableInTablet |
| Unter 640px | Karten: ein Block pro Zeile, jedes Feld auf eigener Zeile |
disableMobileTable behält das Tabellenlayout in jeder Breite. Das Layout ist
fest: eine Zelle, die überläuft, scrollt für sich statt umzubrechen, und die
Tabelle scrollt seitwärts, wenn die Spalten ihr Minimum von 650px nicht mehr
einhalten.
API
Properties
| Property | Attribut | Typ | Standard | Beschreibung |
|---|---|---|---|---|
data | data | Record<string, any>[] | [] | Die Zeilen der aktuellen Seite. Property oder JSON im Attribut |
headers | headers | IDataTableHeader[] | [] | Die Spalten. Property oder JSON im Attribut |
paginated | paginated | boolean | false | Die Pagination-Fusszeile zeigen |
totalItems | totalitems | number | data.length | Summe über alle Seiten, für den Zähler |
currentPage | currentpage | number | 1 | Die gezeigte Seite, 1-basiert |
currentSize | currentsize | number | erstes pageSizeOptions | Zeilen pro Seite |
pageSizeOptions | pagesizeoptions | number[] | [5, 10, 20] | Auswahl der Seitengrösse. Property oder JSON |
sortBy | sortby | string | Keiner | Der sortKey, nach dem gerade sortiert ist |
sortDirection | sortdirection | 'asc' | 'desc' | 'asc' | Die Richtung der aktuellen Sortierung |
isLoadingData | isloadingdata | boolean | false | Die Zeilen durch den Loader ersetzen |
loader | loader | ThreeDotsLoaderConfig | Keiner | Aussehen des Loaders. Property oder JSON im Attribut |
disableMobileTable | disablemobiletable | boolean | false | Das Tabellenlayout auf kleinen Bildschirmen behalten |
showMobileTableInTablet | showmobiletableintablet | boolean | false | Das Kartenlayout auch bis zum Tablet-Breakpoint nutzen |
dark | dark | boolean | false | Die dunkle Palette rendern |
data, headers, pageSizeOptions und loader sind Array- und
Objekt-Properties, ihre Attribute lesen also JSON (data='[{"id":1}]'). Nur die
Property kann die Funktionen tragen, die eine Spalte haben kann, etwa
cellColor.
dark schaltet das ganze Element um, auch die tuix-text- und tuix-icon-Elemente, die es zeichnet. Inhalt, den
man in eine Zelle slottet, gehört einem selbst, dark also dorthin weiterreichen.
Events
Alle vier steigen auf und überqueren die Shadow-Grenze, und alle vier tragen den Zustand, den die Tabelle verlangt:
| Event | Detail | Wann |
|---|---|---|
sort | { sortKey: string, sortDirection: SortDirection } | Eine sortierbare Kopfzelle wird geklickt oder mit Enter/Leertaste ausgelöst |
next-page | { page: number, pageSize: number } | Der Pfeil zur nächsten Seite wird geklickt |
previous-page | { page: number, pageSize: number } | Der Pfeil zur vorigen Seite wird geklickt |
page-size-change | { page: 1, pageSize: number } | Eine andere Seitengrösse wird gewählt |
TypeScript
TuixTableElement<TRowData> typisiert eine Referenz auf das Element mit all
seinen Properties, genau das, was ein useRef oder ein querySelector braucht: