# MapFeatureList

## Overview

`MapFeatureList` renders the map's content as an operable list. A map is a spatial control, and a spatial control alone is not reachable by everyone. Pairing it with a list that shares `selectedId` in both directions gives the same information and the same actions through plain keyboard and screen-reader navigation.

---

## Import

```tsx
import { MapFeatureList, type MapFeatureListItem } from 'xertica-ui/ui';
```

---

## Prerequisites

- Import `xertica-ui/style.css` once at the app root.
- Hold `selectedId` in the parent and pass it to both `<Map>` and this list.

---

## Props

| Prop         | Type                               | Default                 | Description                                     |
| ------------ | ---------------------------------- | ----------------------- | ----------------------------------------------- |
| `features`   | `MapFeatureListItem[]`             | required                | Rows to render.                                 |
| `selectedId` | `string \| null`                   | -                       | The shared selection.                           |
| `onSelect`   | `(id: string \| null) => void`     | required                | Fires with `null` when the user presses Escape. |
| `renderItem` | `(feature, selected) => ReactNode` | -                       | Replaces the default row.                       |
| `title`      | `string`                           | -                       | Heading, also the accessible name.              |
| `ariaLabel`  | `string`                           | `"Map features"`        | Accessible name when no `title` is shown.       |
| `emptyLabel` | `string`                           | `"No features to list"` | Shown when `features` is empty.                 |
| `maxHeight`  | `string`                           | -                       | Caps the height and scrolls.                    |

### `MapFeatureListItem`

| Field            | Type           | Description                                                |
| ---------------- | -------------- | ---------------------------------------------------------- |
| `id`             | `string`       | Must match the marker's `id`.                              |
| `label`          | `string`       | Primary line.                                              |
| `description`    | `string`       | Secondary line — a value, a count, a status.               |
| `colorToken`     | `ColorToken`   | Swatch, ideally the same token the marker uses.            |
| `precision`      | `MapPrecision` | `'exact'`, `'centroid'` or `'jittered'`.                   |
| `precisionLabel` | `string`       | Human-readable explanation, shown for non-exact positions. |

---

## Keyboard

| Key               | Action                               |
| ----------------- | ------------------------------------ |
| `Tab`             | Enters the list (a single tab stop). |
| `↑` / `↓`         | Moves between rows.                  |
| `Home` / `End`    | Jumps to the first or last row.      |
| `Enter` / `Space` | Selects the focused row.             |
| `Escape`          | Clears the selection.                |

---

## Example

```tsx
const [selectedId, setSelectedId] = useState<string | null>(null);

<div className="grid gap-4 md:grid-cols-[1fr_320px]">
  <Map
    height="520px"
    ariaLabel="Mapa de unidades por município"
    selectedId={selectedId}
    onFeatureSelect={setSelectedId}
    markers={units.map(u => ({ id: u.id, position: u.position, title: u.name }))}
  />
  <MapFeatureList
    title="Municípios"
    selectedId={selectedId}
    onSelect={setSelectedId}
    features={units.map(u => ({ id: u.id, label: u.name, description: `${u.total} atendimentos` }))}
  />
</div>;
```

---

## AI Rules

- **Always** share one `selectedId` between the map and the list, in both directions.
- **Always** keep the ids identical to the markers' ids, or selecting in one will not highlight the other.
- **Always** set `precisionLabel` when positions are centroids or jittered.
- **Never** hide the list at small breakpoints — it is the accessible path to the same data, not a desktop extra.
- **Never** rebuild it from `<div>` rows: the `listbox` role, the roving `tabIndex`, arrow-key movement and Escape-to-clear all come from this component.
- Common mistake: rendering the list from the raw data while the map renders clustered markers. Feed both from the same post-clustering set, or the two will disagree about what exists.
