GridList
A list of rows that can hold buttons, checkboxes and links, which the user can select and act on.
Notes.txt
Photos
Budget.xlsx
Music
Selected: none. Opened: nothing yet.
<script>
import { $bind, $signal } from 'brumate/signals';
import Button from '@brumate/ui/Button.brm';
import Checkbox from '@brumate/ui/Checkbox.brm';
import GridList from '@brumate/ui/GridList.brm';
import GridListItem from '@brumate/ui/GridListItem.brm';
import type { Key } from '@brumate/ui/collection.ts';
import { moveKeys } from '@brumate/ui/data.ts';
import type { ReorderEvent } from '@brumate/ui/drop.ts';
import Icon from '../../src/components/Icon.brm';
let picked = $signal<Key[]>([]);
let opened = $signal('nothing yet');
let files = $signal(['Notes.txt', 'Photos', 'Budget.xlsx', 'Music']);
// A drag moves the rows, from the pointer or from the grip and the keys.
function reorder(event: ReorderEvent) {
const after = event.target.dropPosition === 'after';
files = moveKeys(files, event.target.key, event.keys, after, (file) => file);
}
</script>
<GridList
class="ui-gridlist"
aria-label="Files"
selectionMode="multiple"
selectedKeys=$bind(picked)
onReorder=reorder
>
#for (file of files by file) {
<GridListItem id=file class="ui-gridrow" textValue=file>
<Button slot="drag" class="ui-grip"><Icon name="grip" size=(16) /></Button>
<Checkbox slot="selection" class="ui-box" aria-label=(`Select ${file}`)>
<span class="ui-mark" aria-hidden="true"><Icon name="check" size=(12) /><Icon name="minus" size=(12) /></span>
</Checkbox>
{file}
<Button class="ui-button" onPress=(() => (opened = file))>Open</Button>
</GridListItem>
}
</GridList>
<p>Selected: {picked.join(', ') || 'none'}. Opened: {opened}.</p>
<GridList aria-label="Files" selectionMode="multiple" selectedKeys=$bind(picked)>
#for (file of files by file) {
<GridListItem id=file textValue=file>
<Checkbox slot="selection" aria-label=(`Select ${file}`) />
{file}
<Button onPress=(() => open(file))>Open</Button>
</GridListItem>
}
</GridList>
- A
GridListholdsGridListItemelements, each with anid, its key. It needsaria-labeloraria-labelledby. - A GridList can take
items, each with anid, and anitemtemplate that renders aGridListItemfor each, as a Tree does. WithrowHeight, or in a Virtualizer, it renders only the rows in view, and its keys read the data of all the items.getTextValuereturns the text of an item for the letters that move to a row, for the rows out of view. - Selection is as in a ListBox:
selectionMode,selectionBehavior,selectedKeys,disabledKeysandonSelectionChange. - A
Checkboxwithslot="selection"and novaluein a row shows whether the row is selected, and toggles it. onActiongets the key of a row that Enter acts on, or a press when nothing is selectable.onActionon a row runs too.
Keyboard
- Tab stops at the list once, at the row that had focus last, or else the first selected row, or the first row. Tab from inside the list leaves it.
- Up and Down move between rows, and Home and End go to the first and the last row. Letters move to the row whose text starts with them.
textValuesets that text for a row whose content is more than text. - Left and Right move through the controls in a row, and back to the row. Right to left, they turn around.
- Space selects the row that has focus, and Shift extends a range from it. Ctrl+A or Cmd+A selects all but the disabled rows, and Escape clears the selection, also from a control in a row.
- PageUp and PageDown move by the height of the list.
- With
itemsin a windowed list, Home, End, the page keys, Ctrl+A, ranges with Shift and letters reach rows that are out of view. A move to one scrolls it into view, then focuses it. - A text field in a row keeps its own keys, other than Up, Down and Tab.
Rows
The GridList is a grid, and each GridListItem is a row with one cell, as in React Aria. A press on a control in a row, such as a Button, is the control's own, and does not select the row. A row shows data-selected, data-disabled, data-pressed, data-hovered, data-focused and data-focus-visible.
Drag and drop
Rows drag to new places with onReorder, and out of the list, such as to a DropZone, with getItems.
<GridList aria-label="Files" onReorder=reorder getItems=((keys) => keys.map((key) => ({ 'text/plain': key })))>
#for (file of files by file) {
<GridListItem id=file textValue=file>
<Button slot="drag">≡</Button>
{file}
</GridListItem>
}
</GridList>
onReordergetskeys, the rows that move, andtarget, with thekeyof a row and adropPositionofbeforeorafter.moveKeysof the data helpers, ormoveBeforeandmoveAfteroflistData, move them.getItemsgets the keys of the rows that a drag moves, in the order of the list, and returns the data of each row, as a string for each type, such astext/plain. With nogetItems, the data is the keys as text. A DropZone gets one item for each row. Other targets get the text of each type joined with new lines.- A drop on a row that moves, in a gap of the list, or that leaves the order as it is does not call
onReorder. A drag that the list did not start, such as a file, is not taken. - A drag takes the selected rows when the dragged row is selected, and else that row.
- A row shows
data-draggingwhile a drag moves it, anddata-drop-target,beforeorafter, while a drag would drop by it.
From the keys:
- A
Buttonwithslot="drag"in a row starts a drag. Its label is "Drag" and thetextValueof the row. It works withonReorderonly, and is disabled withgetItemsalone, since a drag from the keys needs a place to drop in the list. - Up and Down choose where the rows go. The row by that place takes the focus.
- Enter drops. Escape and Tab cancel, and so does focus leaving the list.
- A live region says what happens, for screen readers.