# MapTimeline

## Overview

`MapTimeline` is a time ruler bound to the map: a sliding window, playback at several speeds, and filtering of features by their timestamp. It is what turns plotted points into a space-time reconstruction, and the same mechanism serves an inspection-date filter or a day's schedule.

---

## Import

```tsx
import { Map, MapTimeline } from 'xertica-ui/ui';
```

---

## Props

| Prop              | Type                         | Default     | Description                                   |
| ----------------- | ---------------------------- | ----------- | --------------------------------------------- |
| `range`           | `[number, number]`           | required    | Inclusive start and end.                      |
| `value`           | `number`                     | required    | Current position.                             |
| `onChange`        | `(value: number) => void`    | required    | Reports scrubbing and playback.               |
| `speeds`          | `number[]`                   | `[1, 2, 4]` | Playback multipliers offered.                 |
| `speed`           | `number`                     | -           | Controlled speed.                             |
| `onSpeedChange`   | `(speed: number) => void`    | -           | Reports the chosen speed.                     |
| `playing`         | `boolean`                    | `false`     | Controlled playback state.                    |
| `onPlayingChange` | `(playing: boolean) => void` | -           | Reports play, pause and auto-stop at the end. |
| `step`            | `number`                     | `1`         | Units advanced per tick at 1×.                |
| `tickMs`          | `number`                     | `250`       | Milliseconds between ticks.                   |
| `formatValue`     | `(value: number) => string`  | -           | Renders the value as a label.                 |

On `<Map>`: `timeField` (the key on `markers[].data` holding the timestamp) and `timeWindow` (`[from, to]`).

---

## Example

```tsx
const [t, setT] = useState(1020);
const [playing, setPlaying] = useState(false);

<>
  <Map
    height="480px"
    markers={pings.map(p => ({ id: p.id, position: p.position, data: { timestamp: p.minute } }))}
    timeField="timestamp"
    timeWindow={[1020, t]}
  />
  <MapTimeline
    range={[1020, 1439]}
    value={t}
    onChange={setT}
    playing={playing}
    onPlayingChange={setPlaying}
    formatValue={v => `${Math.floor(v / 60)}:${String(v % 60).padStart(2, '0')}`}
  />
</>;
```

---

## AI Rules

- **Always** pass `formatValue`. A raw minute count means nothing to the reader.
- **Always** keep `playing` and `value` in the parent — the component is fully controlled.
- **Always** give the markers a `timeField` on their `data`, or `timeWindow` has nothing to filter on.
- **Never** filter the marker array yourself before passing it in when you also use `cluster`. `<Map>` applies the time window _before_ clustering, so a badge counts only what is inside the window; filtering afterwards would report totals the user cannot see.
- Markers whose timestamp is missing or unparseable stay visible rather than disappearing for a reason the user cannot see.
- Under `prefers-reduced-motion` the ruler does not auto-advance on its own and says so. Do not override that.
