# ScanGrid

`ScanGrid` is the grid a hunt happens on, and the scan that finds things in it. On its own it draws cells in five levels of a theme's ramp. Through its handle, `seek` hunts one cell down and hands back what a `CardOpening` needs to send a card out of it.

> Live preview: see it play at https://bettertyped.github.io/react-cards-opening/docs/components/scan-grid/
>
> The grid alone. Press Scan to hunt a cell down.

## Usage

```tsx
import { ScanGrid, type ScanGridHandle } from "@react-cards-opening/ui";

const scan = useRef<ScanGridHandle>(null);
const [veiled, setVeiled] = useState(true);

<ScanGrid handle={scan} levels={levels} veiled={veiled} onUnveil={() => setVeiled(false)} />;
```

## With CardOpening

`seek` returns `{ element, lead, release }`, which is exactly what `launch` on `CardOpening` takes. The card waits for the scan, then lifts out of the cell.

```tsx
<CardOpening
  opening={hunt.opening}
  launch={(card, show) => scan.current?.seek(cellOf(card), show) ?? null}
/>
```

## Props

| Prop | Type | What it does |
| --- | --- | --- |
| `levels` | `readonly number[]` | One number per cell, 0 (unlit) to 4. Cells fill top to bottom, then left to right. |
| `lead?` | `number` | Empty slots before the first cell. |
| `rows?` | `number` | Cells in a column. 7 by default. |
| `theme?` | `CardTheme` | The colours. |
| `tone?` | `string` | The colour of the lock and the crack: the hunted card's tier. |
| `veiled?` | `boolean` | Blank until the beam has passed. |
| `onUnveil?` | `() => void` | The beam finished its first pass. Stop veiling. |
| `idle?` | `boolean` | Nothing is being hunted: the grid may twitch and sparkle. |
| `markers?` | `ReadonlyMap<number, ScanMarker>` | Found cells by index: `{ id, name, colour }`. |
| `onMarkerPress?` | `(marker, element) => void` | A found cell was pressed. |
| `markersDisabled?` | `boolean` | Found cells cannot be pressed for now. |
| `markerLabel?` | `(name: string) => string` | What a screen reader calls a found cell. |
| `handle?` | `Ref<ScanGridHandle>` | Where `seek`, `reset` and `cell` are handed out. |
| `className?` | `string` | Added to the grid's root. |
| `classNames?` | `ScanGridClassNames` | A class of your own for each part of the grid. |
| `style?` | `CSSProperties` | Added to the grid's root. |

## The handle

| Method | What it does |
| --- | --- |
| `seek(cell, show, options?)` | Hunts the cell down and returns `{ element, lead, release }`, or null when the grid has no such cell. `options.onLock` fires when the beam settles; `options.flash` is an element to flash on a loud find. |
| `reset()` | Forgets which cells the beam has lit, so a veiled grid is blank again. |
| `cell(index)` | The element of a cell. |

## Blank until scanned

A `veiled` grid shows every cell as unlit until the beam has crossed it. The first `seek` on a veiled grid sweeps the whole width, and `onUnveil` fires when it is done. While veiled and idle, a few cells shift by a pixel now and then: something is in there.

## Other shapes

```tsx
// A board: five rows, twelve columns.
<ScanGrid levels={board} rows={5} />

// A calendar whose first day is a Wednesday.
<ScanGrid levels={year} lead={3} />
```

## Related

- [Beam and lock](https://bettertyped.github.io/react-cards-opening/docs/effects/beam-and-lock/)
- [Crack](https://bettertyped.github.io/react-cards-opening/docs/effects/crack/)
- [CardHunt](https://bettertyped.github.io/react-cards-opening/docs/components/card-hunt/)
- [Hiding places](https://bettertyped.github.io/react-cards-opening/docs/core/spots/)

Source: https://bettertyped.github.io/react-cards-opening/docs/components/scan-grid/
