# RowActions

> Package `@umriss-ui/table`, version 0.40.0. Demo page: <https://romanhaendler.github.io/umriss-ui/table/rowactions/>. Every page in one line: [llms.txt](https://romanhaendler.github.io/umriss-ui/table/llms.txt); every page in full: [llms-full.txt](https://romanhaendler.github.io/umriss-ui/table/llms-full.txt).

Buttons at the end of each row for what the user does to that row – open, approve, reject. Mark one as a bulk action and it also acts on every ticked row at once.

```ts
import { useTable } from "@umriss-ui/table";
```

Demo page: https://romanhaendler.github.io/umriss-ui/table/rowactions/

Up to two actions stand in the row as buttons; from three on, all of them move into one menu per row, so the column keeps its width and no action changes place when another is added. Each is read out with the row's name – “Open: INV-26-0318”.

The buttons are always visible: quiet in the secondary colour at rest, in the accent while the pointer is over the row or the focus inside it.

A `bulk` action always receives a list – one row when pressed at the row, the selection within the filtered set when pressed in the toolbar. One function, and three rows are one confirmation, not three.

## Examples

### Two actions

Put `Action`s into `RowActions`; `onSelect` receives the row, and each button is read out with the row's name, as in “Open: INV-26-0318”.

```tsx
import { useState } from "react";
import { Stack, Text } from "@umriss-ui/core";
import { useTable } from "@umriss-ui/table";

interface Invoice {
  id: string;
  supplier: string;
}

const INVOICES: Invoice[] = [
  { id: "INV-26-0318", supplier: "Brandlow Office Supply" },
  { id: "INV-26-0317", supplier: "Kettering & Shaw Events" },
  { id: "INV-26-0309", supplier: "Nimbrel Software" },
];

export default function TwoActions() {
  const [last, setLast] = useState("No action yet.");
  const { Table, Column, RowActions, Action } = useTable(INVOICES, { rowKey: (i) => i.id });

  return (
    <Stack gap={3}>
      <Table ariaLabel="Invoices">
        <Column value="id" label="Invoice" rowHeader />
        <Column value="supplier" label="Supplier" />
        <RowActions>
          <Action onSelect={(i) => setLast(`${i.id} opened`)}>Open</Action>
          <Action onSelect={(i) => setLast(`${i.id} forwarded`)}>Forward</Action>
        </RowActions>
      </Table>
      <Text size="sm" tone="secondary" role="status">
        {last}
      </Text>
    </Stack>
  );
}
```

### Act on a selection

Mark an action `bulk` and it receives a list: one row at the row, the selection in the `Toolbar` while rows are ticked.

```tsx
import { useState } from "react";
import { Stack, Text } from "@umriss-ui/core";
import { Toolbar, useTable } from "@umriss-ui/table";

interface Invoice {
  id: string;
  supplier: string;
  costCentre: string;
}

const INVOICES: Invoice[] = [
  { id: "INV-26-0318", supplier: "Brandlow Office Supply", costCentre: "Facilities" },
  { id: "INV-26-0317", supplier: "Kettering & Shaw Events", costCentre: "Marketing" },
  { id: "INV-26-0315", supplier: "Harlow Print Works", costCentre: "Marketing" },
  { id: "INV-26-0312", supplier: "Corrin Travel", costCentre: "Sales" },
];

export default function ActOnASelection() {
  const [last, setLast] = useState("Nothing approved yet.");
  const { Table, Column, RowActions, Action } = useTable(INVOICES, { rowKey: (i) => i.id });

  return (
    <Stack gap={3}>
      <Table selectable ariaLabel="Invoices awaiting approval">
        <Toolbar />
        <Column value="id" label="Invoice" rowHeader />
        <Column value="supplier" label="Supplier" />
        <Column value="costCentre" label="Cost centre" />
        <RowActions>
          <Action bulk onSelect={(list) => setLast(`Approved: ${list.map((i) => i.id).join(", ")}`)}>
            Approve
          </Action>
        </RowActions>
      </Table>
      <Text size="sm" tone="secondary" role="status">
        {last}
      </Text>
    </Stack>
  );
}
```

### More than two actions

From three actions on, all of them move into one menu per row, so the column keeps its width; `tone="danger"` marks the destructive one.

```tsx
import { useState } from "react";
import { Stack, Text } from "@umriss-ui/core";
import { useTable } from "@umriss-ui/table";

interface Invoice {
  id: string;
  supplier: string;
}

const INVOICES: Invoice[] = [
  { id: "INV-26-0318", supplier: "Brandlow Office Supply" },
  { id: "INV-26-0317", supplier: "Kettering & Shaw Events" },
  { id: "INV-26-0309", supplier: "Nimbrel Software" },
];

export default function MoreThanTwo() {
  const [last, setLast] = useState("No action yet.");
  const { Table, Column, RowActions, Action } = useTable(INVOICES, { rowKey: (i) => i.id });

  return (
    <Stack gap={3}>
      <Table ariaLabel="Invoices">
        <Column value="id" label="Invoice" rowHeader />
        <Column value="supplier" label="Supplier" />
        <RowActions>
          <Action onSelect={(i) => setLast(`${i.id} opened`)}>Open</Action>
          <Action onSelect={(i) => setLast(`${i.id} forwarded`)}>Forward</Action>
          <Action tone="danger" onSelect={(i) => setLast(`${i.id} rejected`)}>
            Reject
          </Action>
        </RowActions>
      </Table>
      <Text size="sm" tone="secondary" role="status">
        {last}
      </Text>
    </Stack>
  );
}
```

## Keyboard

| Key | Action |
|---|---|
| `Enter / Space` | On an action: run it. On the row's menu button: open the menu. |
| `↓ / ↑` | In the menu: the next or previous entry, wrapping around. |
| `Home / End` | In the menu: the first or last entry. |
| `Escape` | Close the menu; the focus returns to its button. |
| `Tab` | Close the menu and move on. |

## Accessibility

Each action is a button named after itself and the row - “Open: INV-26-0318” - so a reader that lists the buttons tells the rows apart. From three actions on, the row's menu button is named “Actions: INV-26-0318”. The row's name is its `rowHeader` cell.

What an action does is the application's to say: a result that should be heard - a row approved, a row gone - goes to a status region or core's `useToast`.

## API

### `RowActionsProps`

| Prop | Type | Default | Description |
|---|---|---|---|
| `children` *required* | `ReactNode` | — | The row's `Action`s.<br>Shown in: [Two actions](https://romanhaendler.github.io/umriss-ui/table/rowactions/#two-actions), [Act on a selection](https://romanhaendler.github.io/umriss-ui/table/rowactions/#act-on-a-selection), [More than two actions](https://romanhaendler.github.io/umriss-ui/table/rowactions/#more-than-two) and 7 more |
| `pin` | `boolean` | `true` | Whether the actions column sticks at the end of the row while the table scrolls sideways. `false` leaves it in the flow, for a place too narrow for a pinned block - the application decides, by its own measure. A column pinned to the end takes the actions with it regardless.<br>Shown in: [Find a late shipment (Scenarios)](https://romanhaendler.github.io/umriss-ui/table/#find-a-late-shipment) |

### `ActionProps<Z>`

| Prop | Type | Default | Description |
|---|---|---|---|
| `children` *required* | `string` | — | The label – text, because it is read out together with the row's name.<br>Shown in: [Two actions](https://romanhaendler.github.io/umriss-ui/table/rowactions/#two-actions), [Act on a selection](https://romanhaendler.github.io/umriss-ui/table/rowactions/#act-on-a-selection), [More than two actions](https://romanhaendler.github.io/umriss-ui/table/rowactions/#more-than-two) and 7 more |
| `tone` | `"default" \| "danger"` | — | `"danger"` for an action that destroys something – that is how it appears in the menu and in the table toolbar.<br>Shown in: [More than two actions](https://romanhaendler.github.io/umriss-ui/table/rowactions/#more-than-two), [Find a late shipment (Scenarios)](https://romanhaendler.github.io/umriss-ui/table/#find-a-late-shipment) |
| `bulk` | `false \| true` | — | Acts on a list: at the row on a list made from it, in the table toolbar on the selection within the filtered set.<br>Shown in: [Act on a selection](https://romanhaendler.github.io/umriss-ui/table/rowactions/#act-on-a-selection), [Select a whole group (Grouping)](https://romanhaendler.github.io/umriss-ui/table/grouping/#a-whole-group-selected), [Read a day's deliveries by depot (Grouping)](https://romanhaendler.github.io/umriss-ui/table/grouping/#the-dispatch-report) and 2 more |

#### Events

| Prop | Type | Default | Description |
|---|---|---|---|
| `onSelect` *required* | `((row: Z) => void) \| ((rows: readonly Z[]) => void)` | — | Gets the row the action was triggered on. With `bulk` a list: at the row one made from it, in the table toolbar the selection.<br>Shown in: [Two actions](https://romanhaendler.github.io/umriss-ui/table/rowactions/#two-actions), [Act on a selection](https://romanhaendler.github.io/umriss-ui/table/rowactions/#act-on-a-selection), [More than two actions](https://romanhaendler.github.io/umriss-ui/table/rowactions/#more-than-two) and 7 more |
