brumate/ui

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>

Keyboard

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>

From the keys: