Playground
Mit typedList<T>
lässt sich eine List für einen bestimmten Datentyp erzeugen.
Im Tab Develop stehen verschiedene Anleitungen zum technischen Aufbau bereit.
import { ActionGroup, AlertBadge, Avatar, Button, ContextMenu, Heading, IconDomain, IconSubdomain, MenuItem, Text, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const DomainList = typedList<Domain>(); return ( <DomainList.List batchSize={4} aria-label="Domains" defaultViewMode="list" > <DomainList.StaticData data={domains} /> <ActionGroup> <Button color="accent">Anlegen</Button> </ActionGroup> <DomainList.Search /> <DomainList.Filter property="type" mode="some" name="Typ" /> <DomainList.Sorting property="hostname" name="Domain A bis Z" direction="asc" /> <DomainList.Sorting property="hostname" name="Domain Z bis A" direction="desc" /> <DomainList.Table> <DomainList.TableHeader> <DomainList.TableColumn> Name </DomainList.TableColumn> <DomainList.TableColumn> Type </DomainList.TableColumn> <DomainList.TableColumn> TLD </DomainList.TableColumn> <DomainList.TableColumn> Hostname </DomainList.TableColumn> </DomainList.TableHeader> <DomainList.TableBody> <DomainList.TableRow> <DomainList.TableCell> {(domain) => domain.domain} </DomainList.TableCell> <DomainList.TableCell> {(domain) => domain.type} </DomainList.TableCell> <DomainList.TableCell> {(domain) => domain.tld} </DomainList.TableCell> <DomainList.TableCell> {(domain) => domain.hostname} </DomainList.TableCell> </DomainList.TableRow> </DomainList.TableBody> </DomainList.Table> <DomainList.Item textValue={(domain) => domain.domain} showTiles > {(domain) => ( <DomainList.ItemView> <Avatar color={ domain.type === "Domain" ? "blue" : "teal" } > {domain.type === "Domain" ? ( <IconDomain /> ) : ( <IconSubdomain /> )} </Avatar> <Heading> {domain.hostname} {!domain.verified && ( <AlertBadge status="warning"> Unverifiziert </AlertBadge> )} </Heading> <Text>{domain.type}</Text> <ContextMenu> <MenuItem>Details anzeigen</MenuItem> <MenuItem>Löschen</MenuItem> </ContextMenu> </DomainList.ItemView> )} </DomainList.Item> </DomainList.List> ); }
Ansichten
Die Liste unterstützt die Ansichten Liste, Raster und Tabelle. Wird mehr als
eine Ansicht verwendet, kann über ein Menü zwischen den Ansichten gewechselt
werden. Die Default-Ansicht wird über das Property defaultViewMode
festgelegt.
Listenansicht
Nutze <List.Item />
, um die List in der Listenansicht darzustellen.
import { AlertBadge, Avatar, ContextMenu, Heading, IconDomain, IconSubdomain, MenuItem, Text, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const DomainList = typedList<Domain>(); return ( <DomainList.List batchSize={4} aria-label="Domains" hidePagination > <DomainList.StaticData data={domains} /> <DomainList.Item textValue={(domain) => domain.domain} > {(domain) => ( <DomainList.ItemView> <Avatar color={ domain.type === "Domain" ? "blue" : "teal" } > {domain.type === "Domain" ? ( <IconDomain /> ) : ( <IconSubdomain /> )} </Avatar> <Heading> {domain.hostname} {!domain.verified && ( <AlertBadge status="warning"> Unverifiziert </AlertBadge> )} </Heading> <Text>{domain.type}</Text> <ContextMenu> <MenuItem>Details anzeigen</MenuItem> <MenuItem>Löschen</MenuItem> </ContextMenu> </DomainList.ItemView> )} </DomainList.Item> </DomainList.List> ); }
Rasteransicht
Für die Rasteransicht wird ebenfalls das <List.Item />
verwendet. Nutze
showTiles
, um diese Ansicht zu aktivieren. Die Listenansicht kann deaktiviert
werden, indem showList
auf false
gesetzt wird.
Über das maxTileWidth
-Property lässt sich die maximale Breite der Kacheln steuern.
import { AlertBadge, Avatar, ContextMenu, Heading, IconDomain, IconSubdomain, MenuItem, Text, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const DomainList = typedList<Domain>(); return ( <DomainList.List batchSize={4} aria-label="Domains" hidePagination defaultViewMode="tiles" > <DomainList.StaticData data={domains} /> <DomainList.Item textValue={(domain) => domain.domain} showTiles showList={false} > {(domain) => ( <DomainList.ItemView> <Avatar color={ domain.type === "Domain" ? "blue" : "teal" } > {domain.type === "Domain" ? ( <IconDomain /> ) : ( <IconSubdomain /> )} </Avatar> <Heading> {domain.hostname} {!domain.verified && ( <AlertBadge status="warning"> Unverifiziert </AlertBadge> )} </Heading> <Text>{domain.type}</Text> <ContextMenu> <MenuItem>Details anzeigen</MenuItem> <MenuItem>Löschen</MenuItem> </ContextMenu> </DomainList.ItemView> )} </DomainList.Item> </DomainList.List> ); }
Tabellenansicht
Nutze <List.Table />
, um die List als Table darzustellen.
import { typedList } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const DomainList = typedList<Domain>(); return ( <DomainList.List batchSize={4} defaultViewMode="table" aria-label="Domains" hidePagination > <DomainList.StaticData data={domains} /> <DomainList.Table> <DomainList.TableHeader> <DomainList.TableColumn> Name </DomainList.TableColumn> <DomainList.TableColumn> Type </DomainList.TableColumn> <DomainList.TableColumn> TLD </DomainList.TableColumn> <DomainList.TableColumn> Hostname </DomainList.TableColumn> </DomainList.TableHeader> <DomainList.TableBody> <DomainList.TableRow> <DomainList.TableCell> {(domain) => domain.domain} </DomainList.TableCell> <DomainList.TableCell> {(domain) => domain.type} </DomainList.TableCell> <DomainList.TableCell> {(domain) => domain.tld} </DomainList.TableCell> <DomainList.TableCell> {(domain) => domain.hostname} </DomainList.TableCell> </DomainList.TableRow> </DomainList.TableBody> </DomainList.Table> </DomainList.List> ); }
ListItems
In einer List lassen sich ListItems mit unterschiedlichen Interaktions- und Aufbaumöglichkeiten einsetzen.
Mit Link
Ein ListItem bietet das Property href
an, um das Element zu verlinken.
import { Avatar, ContextMenu, Heading, IconDomain, MenuItem, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const List = typedList<Domain>(); return ( <List.List batchSize={2} hidePagination aria-label="Domains" > <List.StaticData data={domains} /> <List.Item href={() => "#"} textValue={(domain) => domain.domain} > {(domain) => ( <List.ItemView> <Avatar> <IconDomain /> </Avatar> <Heading>{domain.hostname}</Heading> <ContextMenu> <MenuItem>Details anzeigen</MenuItem> </ContextMenu> </List.ItemView> )} </List.Item> </List.List> ); }
Mit Accordion
Das Accordion-Verhalten wird über die accordion
-Property aktiviert. Dadurch lässt sich
ein ListItem per Klick ein- oder ausklappen. Der erweiterte Inhalt wird in <Content slot="bottom" />
platziert.
import { Avatar, Content, Heading, IconDomain, ListItemView, Text, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const List = typedList<Domain>(); return ( <List.List batchSize={2} hidePagination accordion aria-label="Domains" > <List.StaticData data={domains} /> <List.Item textValue={(domain) => domain.domain}> {(domain) => ( <ListItemView> <Avatar> <IconDomain /> </Avatar> <Heading>{domain.hostname}</Heading> <Text>{domain.type}</Text> <Content slot="bottom">Mehr Inhalt</Content> </ListItemView> )} </List.Item> </List.List> ); }
Mit Checkboxen
Checkboxen in einem ListItem werden automatisch am Anfang der Zeile angeordnet. Die Funktionalität der Checkbox wird nicht von der List gesteuert und muss individuell implementiert werden. Achte bei der Implementierung jedoch
darauf, dass die gesamte Zeile zur Auswahl des Elements genutzt werden kann.
Nutze dafür onAction
der List.
import { Avatar, Checkbox, Heading, IconDomain, Text, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; import { useState } from "react"; export default () => { const List = typedList<Domain>(); const [selectedDomains, setSelectedDomains] = useState< Domain[] >([]); const onSelected = ( domain: Domain, selected: boolean, ) => { if (selected) { setSelectedDomains((prev) => [...prev, domain]); } else { setSelectedDomains((prev) => prev.filter((d) => d.id !== domain.id), ); } }; const isSelected = (domain: Domain) => { return ( selectedDomains.find((d) => d.id === domain.id) !== undefined ); }; return ( <List.List hidePagination batchSize={2} aria-label="Domains" onAction={(domain) => { onSelected(domain, !isSelected(domain)); }} > <List.StaticData data={domains} /> <List.Item showTiles textValue={(domain) => domain.hostname} > {(domain) => ( <List.ItemView> <Checkbox isSelected={isSelected(domain)} onChange={(value) => onSelected(domain, value) } aria-label={`${domain.hostname} auswählen`} /> <Avatar> <IconDomain /> </Avatar> <Heading>{domain.hostname}</Heading> <Text>{domain.type}</Text> </List.ItemView> )} </List.Item> <List.Table> <List.TableHeader> <List.TableColumn> <Checkbox aria-label="Alle auswählen" onChange={(v) => setSelectedDomains(v ? domains : []) } /> </List.TableColumn> <List.TableColumn>Domain</List.TableColumn> </List.TableHeader> <List.TableBody> <List.TableRow> <List.TableCell> {(domain) => ( <Checkbox isSelected={isSelected(domain)} onChange={(value) => onSelected(domain, value) } aria-label={`${domain.hostname} auswählen`} /> )} </List.TableCell> <List.TableCell> {(domain) => domain.hostname} </List.TableCell> </List.TableRow> </List.TableBody> </List.Table> </List.List> ); }
Mit Content Slots
In einem ListItem kann zusätzlicher <Content/ >
(Top und Bottom Content) platziert werden. Die Position wird über das slot
-Property gesteuert.
import { Avatar, Content, ContextMenu, Heading, IconDomain, MenuItem, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const List = typedList<Domain>(); return ( <List.List batchSize={2} hidePagination aria-label="Domains" > <List.StaticData data={domains} /> <List.Item showTiles textValue={(domain) => domain.domain} > {(domain) => ( <List.ItemView> <Avatar> <IconDomain /> </Avatar> <Heading>{domain.hostname}</Heading> <Content slot="top">Top Content</Content> <Content slot="bottom">Bottom Content</Content> <ContextMenu> <MenuItem>Details anzeigen</MenuItem> </ContextMenu> </List.ItemView> )} </List.Item> </List.List> ); }
Mit ColumnLayout
Dem ListItem können die ColumnLayout Properties s
, m
und l
mitgegeben
werden, um das Seitenverhältnis sowie das Umbruchverhalten von Header und Content zu
steuern.
import { Avatar, Content, ContextMenu, Heading, IconEmail, Label, MenuItem, ProgressBar, typedList, } from "@mittwald/flow-react-components"; export default () => { const List = typedList<{ mail: string }>(); return ( <List.List batchSize={2} aria-label="E-Mail-Adressen" hidePagination > <List.StaticData data={[ { mail: "john@doe.com" }, { mail: "max@mustermann.de" }, ]} /> <List.Item textValue={(mail) => mail.mail}> {(mail) => ( <List.ItemView l={[3, 1]} m={[2, 1]} s={[1]}> <Avatar> <IconEmail /> </Avatar> <Heading>{mail.mail}</Heading> <Content> <ProgressBar size="s" value={50}> <Label>Speicherplatz</Label> </ProgressBar> </Content> <ContextMenu> <MenuItem>Details anzeigen</MenuItem> </ContextMenu> </List.ItemView> )} </List.Item> </List.List> ); }
Da für die ColumnLayout-Spalten auch null
gesetzt werden kann, ist es möglich,
nicht zwingend benötigten Content in kleineren Ansichten auszublenden. In diesem Fall werden auch die entsprechenden Content Slots nicht angezeigt.
import { Avatar, Content, ContextMenu, Heading, IconEmail, Label, MenuItem, ProgressBar, typedList, } from "@mittwald/flow-react-components"; export default () => { const List = typedList<{ mail: string }>(); return ( <div style={{ width: 400 }}> <List.List batchSize={2} aria-label="E-Mail-Adressen" hidePagination > <List.StaticData data={[ { mail: "john@doe.com" }, { mail: "max@mustermann.de" }, ]} /> <List.Item textValue={(mail) => mail.mail}> {(mail) => ( <List.ItemView l={[3, 1]} m={[2, 1]} s={[1, null]} > <Avatar> <IconEmail /> </Avatar> <Heading>{mail.mail}</Heading> <Content> <ProgressBar size="s" value={50}> <Label>Speicherplatz</Label> </ProgressBar> </Content> <ContextMenu> <MenuItem>Details anzeigen</MenuItem> </ContextMenu> </List.ItemView> )} </List.Item> </List.List> </div> ); }
Mit ActionGroup
Verwende eine ActionGroup innerhalb des <Content />
, um Buttons in der List
zu platzieren.
import { ActionGroup, Avatar, Button, Content, Heading, IconDomain, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const List = typedList<Domain>(); return ( <List.List batchSize={2} hidePagination aria-label="Domains" > <List.StaticData data={domains} /> <List.Item textValue={(domain) => domain.domain}> {(domain) => ( <List.ItemView> <Avatar> <IconDomain /> </Avatar> <Heading>{domain.hostname}</Heading> <Content> <ActionGroup> <Button variant="soft" color="secondary"> Bearbeiten </Button> <Button variant="soft" color="danger"> Löschen </Button> </ActionGroup> </Content> </List.ItemView> )} </List.Item> </List.List> ); }
Einstellmöglichkeiten
Die List bietet Sortierung, Filter, Suche und Pagination an. Detaillierte Anleitungen zu den einzelnen Einstellmöglichkeiten stehen unter dem Develop-Tab der List zur Verfügung.
import { ActionGroup, AlertBadge, Avatar, Button, ContextMenu, Heading, IconDomain, IconSubdomain, MenuItem, Text, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const DomainList = typedList<Domain>(); return ( <DomainList.List batchSize={4} aria-label="Domains" defaultViewMode="list" > <DomainList.StaticData data={domains} /> <ActionGroup> <Button color="accent">Anlegen</Button> </ActionGroup> <DomainList.Search /> <DomainList.Filter property="type" mode="some" name="Typ" /> <DomainList.Sorting property="hostname" name="Domain A bis Z" direction="asc" /> <DomainList.Sorting property="hostname" name="Domain Z bis A" direction="desc" /> <DomainList.Table> <DomainList.TableHeader> <DomainList.TableColumn> Name </DomainList.TableColumn> <DomainList.TableColumn> Type </DomainList.TableColumn> <DomainList.TableColumn> TLD </DomainList.TableColumn> <DomainList.TableColumn> Hostname </DomainList.TableColumn> </DomainList.TableHeader> <DomainList.TableBody> <DomainList.TableRow> <DomainList.TableCell> {(domain) => domain.domain} </DomainList.TableCell> <DomainList.TableCell> {(domain) => domain.type} </DomainList.TableCell> <DomainList.TableCell> {(domain) => domain.tld} </DomainList.TableCell> <DomainList.TableCell> {(domain) => domain.hostname} </DomainList.TableCell> </DomainList.TableRow> </DomainList.TableBody> </DomainList.Table> <DomainList.Item textValue={(domain) => domain.domain} showTiles > {(domain) => ( <DomainList.ItemView> <Avatar color={ domain.type === "Domain" ? "blue" : "teal" } > {domain.type === "Domain" ? ( <IconDomain /> ) : ( <IconSubdomain /> )} </Avatar> <Heading> {domain.hostname} {!domain.verified && ( <AlertBadge status="warning"> Unverifiziert </AlertBadge> )} </Heading> <Text>{domain.type}</Text> <ContextMenu> <MenuItem>Details anzeigen</MenuItem> <MenuItem>Löschen</MenuItem> </ContextMenu> </DomainList.ItemView> )} </DomainList.Item> </DomainList.List> ); }
Sortierung
Nutze <List.Sorting />
innerhalb der List, um eine Sortiermethode anzulegen.
Filter
Über <List.Filter />
lassen sich Filtermöglichkeiten für die List anlegen.
Suche
Verwende <List.Search />
innerhalb der List, um ein
SearchField anzuzeigen.
Standardmäßig wird die Suche automatisch gestartet. Soll die Suche nur beim Drücken auf Enter ausgelöst werden, kann das Property autoSubmit
auf false
gesetzt werden.
Pagination
Die Pagination ist standardmäßig bei jeder List aktiviert, kann jedoch über
hidePagination
deaktiviert werden. Über die batchSize
-Property kann
festgelegt werden, wie viele Einträge gleichzeitig angezeigt werden sollen.
Kombiniere mit ...
ActionGroup
Verwende <ActionGroup />
innerhalb der List, um eine
ActionGroup anzuzeigen. Hier können
eine oder mehrere Aktionen definiert werden, die sich direkt auf die Liste beziehen.
import { ActionGroup, Avatar, Button, Heading, IconDomain, Text, typedList, } from "@mittwald/flow-react-components"; import { type Domain, domains, } from "@/content/03-components/structure/list/examples/domainApi"; export default () => { const DomainList = typedList<Domain>(); return ( <DomainList.List batchSize={2} hidePagination aria-label="Domains" > <DomainList.StaticData data={domains} /> <ActionGroup> <Button color="accent">Anlegen</Button> </ActionGroup> <DomainList.Item textValue={(domain) => domain.domain} > {(domain) => ( <DomainList.ItemView> <Avatar> <IconDomain /> </Avatar> <Heading>{domain.hostname}</Heading> <Text>{domain.type}</Text> </DomainList.ItemView> )} </DomainList.Item> </DomainList.List> ); }
Summary
Verwende eine <ListSummary />
, um eine Zusammenfassung anzuzeigen, beispielsweise die
Gesamtsumme der Beträge. Über das position
-Property wird festgelegt, ob die Summary oberhalb oder unterhalb der List erscheint.
import { Flex, Heading, ListItemView, ListSummary, Text, typedList, } from "@mittwald/flow-react-components"; export default () => { const InvoiceList = typedList<{ id: string; amount: string; }>(); return ( <InvoiceList.List batchSize={2} hidePagination aria-label="Rechnungen" > <ListSummary position="bottom"> <Flex justify="end"> <Text> <b>Gesamt: 37,00 €</b> </Text> </Flex> </ListSummary> <InvoiceList.StaticData data={[ { id: "Rechnung 1", amount: "25,00 €", }, { id: "Rechnung 2", amount: "12,00 €", }, ]} /> <InvoiceList.Item textValue={(invoice) => invoice.id}> {(invoice) => ( <ListItemView> <Heading>{invoice.id}</Heading> <Text>{invoice.amount}</Text> </ListItemView> )} </InvoiceList.Item> </InvoiceList.List> ); }