# Table scrubber map

Where a paginated table's flagged rows are: a strip of the whole result, with a tick for each flagged row and the page shown outlined, and a toolbar of the pages that hold flags ("Page 4: 3 flagged"), each a button to that page, one tab stop with the arrow keys between them, starting on the page shown when it has flags. It sits beside a `DataTable` in server mode, driven by its controlled `page` and your `onQueryChange`; DataTable in the browser (no `onQueryChange`) keeps its page to itself, so it can't be driven from here.

Positions are drawn only against a known total: with `total={null}` it says the total isn't known and lists the pages with flags without placing them.

- Registry name: `table-scrubber-map` (`@candor/table-scrubber-map`)
- 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-scrubber-map

## Install

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

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

## Example

```tsx
"use client"

import * as React from "react"
import { TableScrubberMap } from "@/components/candor/table-scrubber-map"

// Beside a DataTable in server mode: the table's controlled `page` and this map's `onPageChange` share one state.
const marks = [
  { index: 4, kind: "failed" },
  { index: 37, kind: "slow" },
  { index: 38, kind: "failed" },
  { index: 41, kind: "failed" },
  { index: 186, kind: "slow" },
]

export default function Example() {
  const [page, setPage] = React.useState(0)
  return (
    <div className="flex max-w-md gap-4 rounded-xl border bg-card p-4">
      <TableScrubberMap
        label="Flagged runs"
        total={248}
        pageSize={20}
        page={page}
        onPageChange={setPage}
        marks={marks}
        kinds={{ failed: { label: "failed", tone: "destructive" }, slow: { label: "slow", tone: "warning" } }}
      />
      <p className="text-sm text-muted-foreground">Showing page {page + 1} of 13.</p>
    </div>
  )
}
```

## <TableScrubberMap>

Props:

- `label: string`: What the flags are, naming the list: "Failed rows".
- `total: number | null`: How many rows the whole result holds; `null` when the server doesn't count them.
- `pageSize: number`: Rows per page, as the table's.
- `page: number`: The page shown, from 0, as DataTable's `page`.
- `onPageChange: (page: number) => void`: Called with the page, from 0, the person picks.
- `marks: ScrubberMark[]`: The flagged rows, by their position in the whole result.
- `kinds?: Record<string, ScrubberKind> | undefined`: The kinds of flag `marks` name, with their words and tones. Kind labels are drawn as isolated text.
- `orientation?: "vertical" | "horizontal" | undefined` (default `"vertical"`): A tall strip beside the table (the default) or a wide one above it.
- `className?: string | undefined`: Classes for the root, merged with Candor's.

## Strings (`tableScrubberMap`)

The fixed words, English by default. Override any of them under `tableScrubberMap` in `<LocaleProvider strings={{ tableScrubberMap: { … } }}>`; the table is exported as `tableScrubberMapStrings`.

- `page`: { one: "Page {page}: {count} flagged", other: "Page {page}: {count} flagged" }
- `kindCount`: {count} {kind}
- `withKinds`: {page} ({kinds})
- `kindSeparator`: , 
- `unknownTotal`: The total isn't known, so where these pages fall isn't shown.
- `none`: No flagged rows.
- `kinds`: Kinds of flag

## Types

### ScrubberMark

A flagged row: its position in the whole result, from 0, and its kind (a key of `kinds`).

```ts
{ index: number; kind?: string | undefined }
```

### ScrubberKind

What a kind of flag is called and its tone.

```ts
{ label: string; tone?: "default" | "warning" | "destructive" | undefined }
```

## Motion

- **tint** (tint): A page's background changes when the pointer is over it, and a ring fades in when it takes keyboard focus.

Set with `--cd-motion-table-scrubber-map-<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`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
