# Region traffic
> Traffic between regions as dotted curves whose rows and particle rate follow throughput; selecting a region isolates its flows and rolls its totals.
- React: `import { GeoFlows } from "@/components/lumesec/geo-flows"`
- Collection: Location (https://elements.lumesec.ai/components/location)
- Data: takes your data (`data`); shows demo data until you pass it
- Registry item: https://elements.lumesec.ai/r/geo-flows.json
- Page: https://elements.lumesec.ai/components/location/flows



Live preview: https://elements.lumesec.ai/view/geo-flows

Demo source:

```tsx
import { GeoFlows } from "@/components/lumesec/geo-flows";

export default function GeoFlowsDemo() {
  return <GeoFlows className="max-w-[660px]" />;
}
```

> **Your data:** Pass your regions as `regions` and your traffic as `flows`. Without either the component shows 12 demo cloud regions and 14 demo flows with invented throughput that drifts every 3 s; `regions` alone shows an empty map. See [Your data](/docs/data).

## Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

## Installation

```bash
npx shadcn@latest add @lumesec/geo-flows
```

First time with the @lumesec registry? Register it once, or install by URL:

```bash
npx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json
npx shadcn@latest add https://elements.lumesec.ai/r/geo-flows.json
```

## Usage

React:

```tsx
"use client";

import * as React from "react";

import { GeoFlows, type FlowRegion, type RegionFlow } from "@/components/lumesec/geo-flows";

const regions: FlowRegion[] = [
  { id: "fra", label: "Frankfurt", lat: 50.11, lon: 8.68 },
  { id: "iad", label: "Ashburn", lat: 39.04, lon: -77.49 },
  { id: "sin", label: "Singapore", lat: 1.35, lon: 103.82 },
];

export function Example({ flows }: { flows: RegionFlow[] }) {
  const [region, setRegion] = React.useState<string | null>(null);
  return <GeoFlows regions={regions} flows={flows} value={region} onValueChange={setRegion} unit="Gbps" />;
}
```

## Behaviour

Traffic between regions as dotted curves whose width and particle rate follow throughput. Downstream runs in the accent colour and upstream in the info colour, each bending to its own side of the line.

Select a region and its flows stay lit while all others collapse to hairlines and drain; the inbound and outbound totals roll to that region's numbers.

## API reference

### Props

Also accepts every prop of `<div>` (`React.ComponentProps<"div">`), spread onto the root element.

| Prop            | Type                                | Default           | Description                                                                                                                                                                                                                                            |
| --------------- | ----------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `regions`       | `readonly FlowRegion[]`             | `12 demo regions` | The regions on the map: `{ id, label, lat, lon }`. Regions with a repeated id or a non-finite coordinate are skipped.                                                                                                                                  |
| `flows`         | `readonly RegionFlow[]`             | `14 demo flows`   | Traffic between regions: `{ from, to, down, up }` with region ids and throughput in `unit`. `down` runs from `from` to `to`, `up` the other way. Flows that name an unknown region or carry no traffic are skipped; an empty array shows an empty map. |
| `value`         | `string \| null`                    | —                 | Selected region id (controlled), or null for none. Use with `onValueChange`.                                                                                                                                                                           |
| `defaultValue`  | `string \| null`                    | `null`            | Selected region when uncontrolled.                                                                                                                                                                                                                     |
| `onValueChange` | `(value: string \| null) => void`   | —                 | Called with the region id when a region is selected, and with null when the selection is cleared.                                                                                                                                                      |
| `unit`          | `string`                            | `"Gbps"`          | Unit of `down` and `up`, shown after the totals and used in the accessible names.                                                                                                                                                                      |
| `format`        | `(value: number) => string`         | `one decimal`     | Formats the totals and the throughput in the map's accessible name. The default gives one decimal with grouping, for example `1,204.5`.                                                                                                                |
| `maxParticles`  | `number`                            | `400`             | Most particles in flight at once. When the cap is reached the oldest particle is dropped. 0 turns particles off.                                                                                                                                       |
| `projection`    | `"equalEarth" \| "equirectangular"` | `"equalEarth"`    | Map projection. The map is cropped to 50° S to 72° N with its central meridian at 10° E.                                                                                                                                                               |
| `live`          | `boolean`                           | `true`            | Lets the demo flows drift by up to 12 % every 3 s while the map is on screen. Has no effect when `flows` or `regions` is given.                                                                                                                        |

### Ref

`ref` points at the root `HTMLDivElement`.

## Keyboard

| Keys                                         | Action                                                                                                 |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Tab                                          | Moves to the region markers, which share one tab stop, then to the clear button of the selection chip. |
| ArrowLeft / ArrowRight / ArrowUp / ArrowDown | Moves focus to the nearest marker in that direction on the map.                                        |
| Enter / Space                                | Selects the focused region, or clears the selection when it is already selected.                       |
| Escape                                       | Clears the selection.                                                                                  |

## Accessibility

* Each region marker is a `button` with `aria-pressed`, named by the region's `label`. The markers form one roving tab stop.
* The map has `role="img"` and an `aria-label` that gives the number of regions and the three largest flows with their downstream and upstream throughput.
* The totals are DOM text. A polite live region announces the selected region's inbound and outbound totals when the selection changes, not when the data changes.
* The chip's clear button is named "Clear selection: " plus the label. After clearing, focus moves to that region's marker.
* Where markers overlap on small maps, a tap on a dot goes to the marker whose centre is nearest; a tap on a label goes to its own region.
* The canvases are `aria-hidden`.
* Reduced motion: there are no particles, ping or halo: the number of dot rows carries the throughput, selection and data changes apply at once, and the totals swap without rolling.

## Theming

Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from `--lumesec`. See [Theming](/docs/theming).

This component reads `--lumesec`, `--lumesec-shine`, `--lumesec-info`, `--foreground`, `--muted-foreground`, `--card` and `--border`.

## Notes

* Downstream curves use `--lumesec` and bend to the left of their direction of travel; upstream curves use `--lumesec-info` and bend to the other side, so the two directions of a flow separate.
* A curve has 1 + round(2 × √(throughput / largest throughput)) rows of dots, so 1 to 3. Particles start at random along a Poisson process of 1.2 + 10 × throughput / largest particles per second and travel at constant speed.
* With nothing selected, In is the sum of every `down` and Out the sum of every `up`. With a region selected, In is the traffic arriving there (`down` of flows to it plus `up` of flows from it) and Out the traffic leaving it.
* Selecting a region lights its flows from the region outward behind an uneven front, collapses every other flow to a hairline and stops its particles, pings the land around the region and lights the land under it as its traffic arrives.
* When `flows` changes, rows and particle rates spring to the new values; added flows grow out of their origin and removed ones retract into it.
* Flows that cross the map's edge at 170° W continue from the other edge.
* The map's height follows from its width, so set only the width. Below 480 px only the selected region's label shows and the selection chip moves to its own row. Labels that do not fit are hidden; the markers keep their names.
* The animation runs only while the map is on screen. Colours follow the theme and are re-read when it changes.


