Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 176 additions & 55 deletions docs/plugins/interact.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
# Interact Plugin

Select features or place markers on the map. The interact plugin provides a unified way to handle user interactions for selecting map features or placing location markers.
The interact plugin provides a unified way to handle user interactions for selecting map features or placing location markers.

## Usage

```js
import createInteractPlugin from '@defra/interactive-map/plugins/interact'

const interactPlugin = createInteractPlugin()
const interactPlugin = createInteractPlugin({
interactionMode: 'auto',
multiSelect: true,
dataLayers: [
{ layerId: 'my-layer', idProperty: 'id' }
]
})

const interactiveMap = new InteractiveMap({
plugins: [interactPlugin]
Expand All @@ -34,20 +40,136 @@ Array of mode identifiers. When set, the plugin does not render when the app is

---

### `interactionMode`
**Type:** `'marker' | 'select' | 'auto'`
**Default:** `'marker'`

Controls how user clicks are interpreted.

- `'marker'` — clicking always places a location marker at the clicked coordinates
- `'select'` — clicking attempts to match a feature from `dataLayers`; click outside clears selection (unless `deselectOnClickOutside` is `false`)
- `'auto'` — attempts feature matching first, falls back to placing a marker if no feature is found

---

### `dataLayers`
**Type:** `Array<DataLayer>`
**Default:** `[]`

Array of map layer configurations that are selectable. Each entry specifies which layer to watch and how to identify features.

```js
dataLayers: [
{ layerId: 'my-polygons', idProperty: 'id' },
{ layerId: 'my-lines' }
]
```

#### `DataLayer` properties

| Property | Type | Description |
|----------|------|-------------|
| `layerId` | `string` | **Required.** The map layer identifier to enable selection on |
| `idProperty` | `string` | Property name used as the feature's unique identifier. If omitted, features are matched by index |
| `selectedStroke` | `string` | Overrides the global `selectedStroke` for this layer |
| `selectedFill` | `string` | Overrides the global `selectedFill` for this layer |
| `selectedStrokeWidth` | `number` | Overrides the global `selectedStrokeWidth` for this layer |

---

### `multiSelect`
**Type:** `boolean`
**Default:** `false`

When `true`, clicking additional features adds them to the selection rather than replacing it.

---

### `contiguous`
**Type:** `boolean`
**Default:** `false`

When `true`, only features that touch or overlap the existing selection can be added. Uses spatial intersection to determine contiguity. Works with polygons, lines, and points.

---

### `deselectOnClickOutside`
**Type:** `boolean`
**Default:** `false`

When `true`, clicking outside any selectable layer clears the current selection.

---

### `tolerance`
**Type:** `number`
**Default:** `10`

Click detection radius in pixels. Increases the hit area around the cursor when matching features, which is useful for lines and points.

---

### `closeOnAction`
**Type:** `boolean`
**Default:** `true`

When `true`, the app closes after the user clicks "Done" or "Cancel".

---

### `markerColor`
**Type:** `string`
**Default:** `'rgba(212,53,28,1)'`

Color of the location marker placed on the map.

---

### `selectedStroke`
**Type:** `string`
**Default:** `'rgba(212,53,28,1)'`

Stroke color used to highlight selected features. Can be overridden per layer via `dataLayers`.

---

### `selectedFill`
**Type:** `string`
**Default:** `'rgba(255, 0, 0, 0.1)'`

Fill color used to highlight selected features. Can be overridden per layer via `dataLayers`.

---

### `selectedStrokeWidth`
**Type:** `number`
**Default:** `2`

Stroke width used to highlight selected features. Can be overridden per layer via `dataLayers`.

---

## Methods

Methods are called on the plugin instance after the map is ready.

---

### `enable()`
### `enable(options?)`

Enable interaction mode. Shows action buttons and enables feature selection or marker placement. Accepts an optional options object to override any of the factory options at runtime.

Enable interaction mode. Shows action buttons and enables feature selection or marker placement.
| Parameter | Type | Description |
|-----------|------|-------------|
| `options` | `Object` | Optional. Any factory options to apply for this session |

```js
interactiveMap.on('map:ready', () => {
interactPlugin.enable()
})

// Override options at runtime
interactPlugin.enable({ multiSelect: true, interactionMode: 'select' })
```

---
Expand All @@ -72,26 +194,42 @@ interactPlugin.clear()

---

### `selectFeature(feature)`
### `selectFeature(featureInfo)`

Programmatically select a feature.

| Parameter | Type | Description |
|-----------|------|-------------|
| `feature` | `Object` | Feature object to select |
| `featureInfo.featureId` | `string` | The feature's identifier value |
| `featureInfo.layerId` | `string` | Optional. The layer the feature belongs to |
| `featureInfo.idProperty` | `string` | Optional. The property name used as the identifier |

```js
interactPlugin.selectFeature({ id: 'feature-1', bounds: [...] })
interactPlugin.selectFeature({
featureId: 'abc123',
layerId: 'my-layer',
idProperty: 'id'
})
```

Respects the current `multiSelect` setting — if `multiSelect` is `false`, the new feature replaces the existing selection.

---

### `unselectFeature()`
### `unselectFeature(featureInfo)`

Clear the currently selected feature.
Programmatically unselect a specific feature.

| Parameter | Type | Description |
|-----------|------|-------------|
| `featureInfo.featureId` | `string` | The feature's identifier value |
| `featureInfo.layerId` | `string` | Optional. The layer the feature belongs to |
| `featureInfo.idProperty` | `string` | Optional. The property name used as the identifier |

```js
interactPlugin.unselectFeature()
interactPlugin.unselectFeature({
featureId: 'abc123'
})
```

---
Expand All @@ -109,18 +247,22 @@ Emitted when the user confirms their selection (clicks "Done").
**Payload:**
```js
{
marker: { coords: [lng, lat] } | null,
selection: { bounds: [...], feature: {...} } | null
// If a marker was placed:
coords: [lng, lat],

// If features were selected:
selectedFeatures: [...],
selectionBounds: [west, south, east, north]
}
```

```js
interactiveMap.on('interact:done', ({ marker, selection }) => {
if (marker) {
console.log('Location selected:', marker.coords)
interactiveMap.on('interact:done', (e) => {
if (e.coords) {
console.log('Location selected:', e.coords)
}
if (selection) {
console.log('Feature selected:', selection.feature)
if (e.selectedFeatures) {
console.log('Features selected:', e.selectedFeatures)
}
})
```
Expand All @@ -129,7 +271,7 @@ interactiveMap.on('interact:done', ({ marker, selection }) => {

### `interact:cancel`

Emitted when the user cancels the interaction.
Emitted when the user cancels the interaction (clicks "Back").

**Payload:** None

Expand All @@ -141,65 +283,44 @@ interactiveMap.on('interact:cancel', () => {

---

### `interact:markerchange`

Emitted when the marker position changes.

**Payload:**
```js
{
coords: [lng, lat]
}
```

```js
interactiveMap.on('interact:markerchange', ({ coords }) => {
console.log('Marker moved to:', coords)
})
```

---

### `interact:selectionchange`

Emitted when the feature selection changes.
Emitted whenever the feature selection changes.

**Payload:**
```js
{
bounds: [west, south, east, north],
feature: { ... }
selectedFeatures: [
{ featureId: '...', layerId: '...', properties: {...}, geometry: {...} }
],
selectionBounds: [west, south, east, north] | null,
canMerge: boolean, // true when all selected features are contiguous
canSplit: boolean // true when exactly one Polygon or MultiPolygon is selected
}
```

```js
interactiveMap.on('interact:selectionchange', ({ bounds, feature }) => {
console.log('Selection changed:', feature)
interactiveMap.on('interact:selectionchange', (e) => {
console.log('Selected features:', e.selectedFeatures)
console.log('Bounds:', e.selectionBounds)
})
```

---

### `interact:selectFeature`
### `interact:markerchange`

Emitted when a feature is programmatically selected via the API.
Emitted when a location marker is placed or moved.

**Payload:**
```js
{
feature: { ... }
coords: [lng, lat]
}
```

---

### `interact:unselectFeature`

Emitted when a feature is programmatically unselected via the API.

**Payload:**
```js
{
feature: { ... }
}
interactiveMap.on('interact:markerchange', ({ coords }) => {
console.log('Marker moved to:', coords)
})
```
Loading
Loading