Virtualizer
A box that scrolls a long collection, such as thousands of rows, and renders only the rows in view.
Photo 0001.jpg
Photo 0002.jpg
Photo 0003.jpg
Photo 0004.jpg
Photo 0005.jpg
Photo 0006.jpg
Photo 0007.jpg
Photo 0008.jpg
Photo 0009.jpg
Photo 0010.jpg
Photo 0011.jpg
Photo 0012.jpg
Photo 0013.jpg
Photo 0014.jpg
Photo 0015.jpg
Photo 0016.jpg
Photo 0017.jpg
Photo 0018.jpg
Photo 0019.jpg
Photo 0020.jpg
<script>
import Tree from '@brumate/ui/Tree.brm';
import TreeItem from '@brumate/ui/TreeItem.brm';
import Virtualizer from '@brumate/ui/Virtualizer.brm';
interface File {
id: string;
name: string;
}
const files: File[] = Array.from({ length: 5000 }, (_, index) => ({
id: `file-${index}`,
name: `Photo ${String(index + 1).padStart(4, '0')}.jpg`,
}));
</script>
<Virtualizer class="ui-virtualizer" rowHeight=(32)>
<Tree class="ui-tree" aria-label="5000 photos" items=files item=file />
</Virtualizer>
#template file(file: File) {
<TreeItem class="ui-treerow" textValue=(file.name)>{file.name}</TreeItem>
}
<Virtualizer rowHeight=(32) style="height: 300px">
<Tree aria-label="Photos" items=files item=file />
</Virtualizer>
- A
Virtualizeris a box that scrolls. Give it a height.rowHeightis the height of a row in pixels, 32 by default. - A Tree in it renders only the rows in view, as the user scrolls. A Tree takes
rowHeightitself too, in a box of your own that scrolls. - The server renders the first rows, with spacers for the height of the rest, so the page has its height before its code loads.
- A row that leaves the view keeps its state while it is in the list.
Other collections
A GridList, a ListBox or a Table takes items, as a Tree does, and renders only the rows in view when it is in a Virtualizer:
<Virtualizer rowHeight=(32) style="height: 300px">
<ListBox aria-label="Files" items=files item=file getTextValue=((file) => file.name) />
</Virtualizer>
A Table takes items and getTextValue, and its TableBody takes item. Their keys read the data, as the keyboard section says.
Rows that you write out or render from a #for of your own have no data to read. Give the #for window(height), and it renders only the rows in view, in the box that scrolls:
<Virtualizer style="height: 300px">
<Table aria-label="Files">
<TableHeader>…</TableHeader>
<TableBody>
#for (file of files by file.id) window(32) {
<Row id=(file.id)>…</Row>
}
</TableBody>
</Table>
</Virtualizer>
See windowed lists for how it renders.
Keyboard
- In a Tree, and in a GridList, a ListBox or a Table that has
items, the keys read the data of all the rows, not only those that render. Home, End, PageUp and PageDown, Ctrl+A, a range with Shift, and letters reach rows that are out of view. In a Tree, Left reaches the row that holds a row. A move to a row that is not rendered scrolls it into view, then focuses it. - In a Table, the box in the header selects every row that can be selected, also those that are not rendered.
- Letters match the text of a row that renders, and for the others the text that
getTextValuereturns for the item, such asgetTextValue=((file) => file.name). Without it, letters match the rows in view only. - The Tree has
aria-rowcountwith the count of all its rows, and each rowaria-rowindex, since not all the rows are in the page. The other collections do not have them yet. - A ListBox windows a vertical list that no field filters, so a ListBox in a ComboBox renders all its items. A GridList windowed with
onReorderdrags only between the rows that render. - The rows of a collection that has no
itemsmove between the rows that are rendered. A move to the edge of the view scrolls it, and the rows that come into view render, so Up and Down go through the whole list. Home and End go to the first and the last row that are rendered.