# Table

shadcn's table in Candor's theme: the plain table elements, for rows you lay out yourself. For records a person searches, sorts, filters and pages, use `DataTable`, which is built on these.

`Table` wraps the element in a box that scrolls sideways when the columns don't fit, and that box is a tab stop, so a keyboard user can scroll it. Give it a `label` and it is announced as a region by that name.

- Registry name: `table` (`@candor/table`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/table

## Install

```bash
npx candor-ui add table
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/table`.
It brings in: utils.

## Example

```tsx
"use client"

import * as React from "react"
import { Table, TableBody, TableCaption, TableCell, TableEmpty, TableFooter, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { useFormat } from "@/components/locale"

const invoices = [
  { id: "INV-1042", status: "Paid", method: "Card", amount: 250 },
  { id: "INV-1043", status: "Pending", method: "Bank transfer", amount: 1200 },
  { id: "INV-1044", status: "Unpaid", method: "Card", amount: 89.5 },
]

export default function Example() {
  const fmt = useFormat()
  const [selected, setSelected] = React.useState<string | null>("INV-1043")
  const total = invoices.reduce((sum, i) => sum + i.amount, 0)
  const money = { style: "currency", currency: "USD" } as const
  return (
    <Table label="Invoices">
      <TableCaption>Invoices for October. Select a row to mark it.</TableCaption>
      <TableHeader>
        <TableRow>
          <TableHead>Invoice</TableHead>
          <TableHead>Status</TableHead>
          <TableHead>Method</TableHead>
          <TableHead numeric>Amount</TableHead>
        </TableRow>
      </TableHeader>
      <TableBody>
        {invoices.length === 0 ? (
          <TableEmpty colSpan={4}>No invoices yet.</TableEmpty>
        ) : (
          invoices.map((i) => (
            <TableRow key={i.id} data-state={selected === i.id ? "selected" : undefined} onClick={() => setSelected(i.id)}>
              <TableCell className="font-medium">{i.id}</TableCell>
              <TableCell>{i.status}</TableCell>
              <TableCell>{i.method}</TableCell>
              <TableCell numeric>{fmt.number(i.amount, money)}</TableCell>
            </TableRow>
          ))
        )}
      </TableBody>
      <TableFooter>
        <TableRow>
          <TableCell colSpan={3}>Total</TableCell>
          <TableCell numeric>{fmt.number(total, money)}</TableCell>
        </TableRow>
      </TableFooter>
    </Table>
  )
}
```

## <Table>

Props:

- `label?: string | undefined`: A name for the scrolling box, announced as a region ("Migrations"). Without one the box is still a tab stop.
- `containerClassName?: string | undefined`: Classes for the scrolling box around the table.

Also takes the props of `React.ComponentProps<"table">`.

## <TableHeader>

No props.

Also takes the props of `React.ComponentProps<"thead">`.

## <TableBody>

No props.

Also takes the props of `React.ComponentProps<"tbody">`.

## <TableFooter>

No props.

Also takes the props of `React.ComponentProps<"tfoot">`.

## <TableHead>

Props:

- `numeric?: boolean | undefined` (default `false`): The column holds numbers: its heading lines up with them at the end. Use `aria-sort` for a sorted column.

Also takes the props of `React.ComponentProps<"th">`.

## <TableRow>

A row. Its background tints under the pointer and for the row marked `data-state="selected"`.

No props.

Also takes the props of `React.ComponentProps<"tr">`.

## <TableCell>

Props:

- `numeric?: boolean | undefined` (default `false`): The cell holds a number: it lines up at the end in tabular figures. Format the number with `useFormat`.

Also takes the props of `React.ComponentProps<"td">`.

## <TableCaption>

No props.

Also takes the props of `React.ComponentProps<"caption">`.

## <TableEmpty>

A row that says why there are no rows: "No migrations yet." or "Nothing matches that filter." You write the words, because only the app knows which it is. Put it in `TableBody` in place of the rows.

Props:

- `colSpan: number`: How many columns the table has, so the message spans them.

Also takes the props of `Omit<React.ComponentProps<"td">, "colSpan">`.

## Motion

- **tint** (tint): A row's background changes when the pointer is over it, and when the app marks it selected.

Set with `--cd-motion-table-<moment>-*` in your brand file; the families are in the design reference.

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
