Action cell
The trailing action column of a Yuno table, pinned right so the action stays in reach when the table scrolls sideways. The Eye when the click goes straight to the detail; the dots when it opens a menu first, even a menu of one. ActionHead is its header. They are the table cells themselves: put them in the row, not inside a <TableCell>.
Anatomy
A <td> pinned to the right edge (sticky right-0 z-10, w-10, pl-4 pr-4) holding one 32px icon button with a tooltip. While columns are hidden to the right it gets a divider and its header a CaretRight; its fill is the row's own, at rest, on hover and when selected.
| Rule | Status | Actions |
|---|---|---|
| Block high-risk BINs | Active |
- 1Pinned cell
sticky right-0 z-10 w-10 pl-4 pr-4. The fill follows the row: bg-background at rest (like the Table frame), the row's hover tint on hover, bg-muted when the row is selected, so nothing scrolling underneath shows through and the cell never reads as a separate block.
- 2Divider and caret
While columns are hidden to the right (Table marks the frame data-more-right), the column draws a 1px divider (an inset shadow: a collapsed border would scroll away from a pinned cell) and ActionHead shows a CaretRight, centred over the column like the eye or dots below it: there is more to the right, passing under this column. On a table that fits, or one scrolled to its end, neither: the last column then sits right beside it.
- 3Icon button
32px, transparent until hover (border, bg-background, shadow-xs), muted icon going to foreground. Eye (light) when the click goes straight to the detail, DotsThreeOutline (fill) when a menu comes first. Its tooltip is the label; its click never reaches the row.
Recipes
Click a row, an eye, or a menu item: this line says which fired.
The click goes straight to the detail, a Sheet or a page, with no menu in between. The row can open the same detail on its own click; the eye is the visible, keyboard-reachable way in. The eye's click does not also fire the row's (try both).
| Rule | Status | Actions |
|---|---|---|
| Block high-risk BINs | Active | |
| 3DS above 500 USD | Active | |
| Retry soft declines | Inactive |
import { ActionCell, ActionHead } from "@/components/ui/action-cell";
import { StatusBadge } from "@/components/ui/badge";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
const rules = [
{ name: "Block high-risk BINs", status: "succeeded", label: "Active" },
{ name: "Retry soft declines", status: "inactive", label: "Inactive" },
] as const;
const openRule = (rule: (typeof rules)[number]) => { /* open its Sheet */ };
<Table>
<TableHeader>
<TableRow>
<TableHead>Rule</TableHead>
<TableHead>Status</TableHead>
<ActionHead />
</TableRow>
</TableHeader>
<TableBody>
{rules.map((r) => (
<TableRow key={r.name} className="cursor-pointer" onClick={() => openRule(r)}>
<TableCell>{r.name}</TableCell>
<TableCell><StatusBadge status={r.status}>{r.label}</StatusBadge></TableCell>
<ActionCell label="View details" onClick={() => openRule(r)} />
</TableRow>
))}
</TableBody>
</Table>The click opens a menu first: edit, duplicate, delete. A menu of one item is still the dots. Pass the DropdownMenuItems as children and the cell becomes the dots opening them, aligned to the end. Choosing an item never opens the row.
| Rule | Status | Actions |
|---|---|---|
| Block high-risk BINs | Active | |
| 3DS above 500 USD | Active | |
| Retry soft declines | Inactive |
import { ActionCell } from "@/components/ui/action-cell";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
// Inside a row, as its last cell; r is the row's data.
<ActionCell label="Actions">
<DropdownMenuItem onSelect={() => edit(r)}>Edit</DropdownMenuItem>
<DropdownMenuItem onSelect={() => duplicate(r)}>Duplicate</DropdownMenuItem>
<DropdownMenuItem onSelect={() => remove(r)}>Delete</DropdownMenuItem>
</ActionCell>More columns than the container is wide. The action column stays pinned, with its divider and a CaretRight in its header, because there are columns hidden to the right, passing under it. Scroll to the end: the last column lands beside it and both go. In the tables above, which fit, neither shows.
| Order | Merchant | Method | Country | Provider | Created | Actions |
|---|---|---|---|---|---|---|
| ORD-88213 | Rappi | Visa | Colombia | Adyen | Feb 8, 2023, 10:00 a.m. | |
| ORD-88214 | Rappi | PIX | Brazil | dLocal | Feb 8, 2023, 10:04 a.m. | |
| ORD-88215 | Rappi | Mastercard | Mexico | Stripe | Feb 8, 2023, 10:09 a.m. |
import { ActionCell, ActionHead } from "@/components/ui/action-cell";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
// Nothing to add: the column pins itself, and its divider and the
// header's CaretRight appear only while columns are hidden to the right.
<Table>
<TableHeader>
<TableRow>
<TableHead className="min-w-40">Order</TableHead>
{/* …more columns than the container is wide… */}
<ActionHead />
</TableRow>
</TableHeader>
<TableBody>
{rows.map((r) => (
<TableRow key={r.id}>
<TableCell>{r.id}</TableCell>
{/* … */}
<ActionCell label="View details" onClick={() => openPayment(r)} />
</TableRow>
))}
</TableBody>
</Table>Import
Copy this import line at the top of the file where you compose this atom.
import { ActionCell, ActionHead } from "@/components/ui/action-cell";Props
Everything else from the underlying HTML or Radix primitive is forwarded via ...props.
| Prop | Type | Default | Description |
|---|---|---|---|
| label | string (required) | — | ActionCell: the button's accessible name and tooltip. "View details" for the eye, "Actions" for the menu. |
| onClick | () => void | — | ActionCell: where the eye goes, straight to the detail. Ignored when children are given. |
| children | ReactNode | — | ActionCell: DropdownMenuItems. When present the cell shows the dots and opens this menu, even with one item. |
| label (ActionHead) | string | "Actions" | ActionHead: the column's name for screen readers. It is not shown. |
| className | string | — | Extra classes on the <td> / <th>. Rarely needed: the pinning, divider and fill are the point of the component. |
Related
Cross-links to atoms and patterns you may reach for next.
- TableThe host, and the Cell types list this cell came from (Eye action, Dots action).
- Dropdown menuWhat the dots open; its items go in as children.