@defra/interactive-map 0.0.17-alpha → 0.0.18-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/css/index.css +1 -1
  2. package/dist/esm/im-core.js +1 -1
  3. package/dist/esm/im-shell.js +1 -1
  4. package/dist/umd/im-core.js +1 -1
  5. package/dist/umd/index.js +1 -1
  6. package/docs/api/context.md +53 -7
  7. package/docs/api/map-style-config.md +41 -2
  8. package/docs/api/marker-config.md +53 -11
  9. package/docs/api/symbol-config.md +160 -0
  10. package/docs/api/symbol-registry.md +115 -0
  11. package/docs/api.md +22 -19
  12. package/docs/plugins/datasets.md +105 -9
  13. package/docs/plugins/interact.md +68 -43
  14. package/docs/plugins/search.md +15 -3
  15. package/package.json +1 -1
  16. package/plugins/beta/datasets/dist/css/index.css +32 -14
  17. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  18. package/plugins/beta/datasets/dist/esm/index.js +1 -1
  19. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
  20. package/plugins/beta/datasets/dist/umd/index.js +1 -1
  21. package/plugins/beta/datasets/src/DatasetsInit.jsx +9 -4
  22. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +57 -11
  23. package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +14 -8
  24. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +155 -53
  25. package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +27 -0
  26. package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +31 -0
  27. package/plugins/beta/datasets/src/api/addDataset.js +1 -1
  28. package/plugins/beta/datasets/src/api/setData.js +4 -2
  29. package/plugins/beta/datasets/src/api/setStyle.js +2 -2
  30. package/plugins/beta/datasets/src/components/EmptyKey.jsx +7 -0
  31. package/plugins/beta/datasets/src/components/EmptyKey.test.jsx +21 -0
  32. package/plugins/beta/datasets/src/components/KeySvg.jsx +24 -0
  33. package/plugins/beta/datasets/src/components/KeySvgLine.jsx +19 -0
  34. package/plugins/beta/datasets/src/components/KeySvgPattern.jsx +15 -0
  35. package/plugins/beta/datasets/src/components/KeySvgRect.jsx +22 -0
  36. package/plugins/beta/datasets/src/components/KeySvgSymbol.jsx +16 -0
  37. package/plugins/beta/datasets/src/components/svgProperties.js +20 -0
  38. package/plugins/beta/datasets/src/datasets.js +13 -4
  39. package/plugins/beta/datasets/src/defaults.js +4 -2
  40. package/plugins/beta/datasets/src/index.js +2 -1
  41. package/plugins/beta/datasets/src/manifest.js +1 -1
  42. package/plugins/beta/datasets/src/panels/Key.jsx +11 -89
  43. package/plugins/beta/datasets/src/panels/Key.module.scss +24 -13
  44. package/plugins/beta/datasets/src/panels/Layers.module.scss +13 -7
  45. package/plugins/beta/datasets/src/reducer.js +6 -0
  46. package/plugins/beta/datasets/src/reducers/keyReducer.js +34 -0
  47. package/plugins/beta/datasets/src/utils/mergeSublayer.js +8 -0
  48. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  49. package/plugins/beta/draw-es/src/DrawInit.jsx +3 -2
  50. package/plugins/beta/draw-ml/dist/css/index.css +21 -1
  51. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  52. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  53. package/plugins/beta/draw-ml/dist/umd/index.js +1 -1
  54. package/plugins/beta/draw-ml/src/DrawInit.jsx +4 -3
  55. package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -1
  56. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
  57. package/plugins/beta/map-styles/dist/umd/index.js +1 -1
  58. package/plugins/beta/map-styles/src/MapStyles.jsx +5 -4
  59. package/plugins/beta/map-styles/src/MapStylesInit.jsx +5 -4
  60. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  61. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  62. package/plugins/interact/dist/umd/index.js +1 -1
  63. package/plugins/interact/src/InteractInit.jsx +14 -5
  64. package/plugins/interact/src/InteractInit.test.js +26 -6
  65. package/plugins/interact/src/api/enable.test.js +7 -7
  66. package/plugins/interact/src/defaults.js +4 -6
  67. package/plugins/interact/src/events.js +9 -6
  68. package/plugins/interact/src/events.test.js +28 -4
  69. package/plugins/interact/src/hooks/useHighlightSync.js +3 -3
  70. package/plugins/interact/src/hooks/useHighlightSync.test.js +6 -6
  71. package/plugins/interact/src/hooks/useHoverCursor.js +10 -0
  72. package/plugins/interact/src/hooks/useHoverCursor.test.js +44 -0
  73. package/plugins/interact/src/hooks/useInteractionHandlers.js +111 -69
  74. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +147 -32
  75. package/plugins/interact/src/reducer.js +23 -4
  76. package/plugins/interact/src/reducer.test.js +60 -11
  77. package/plugins/interact/src/utils/buildStylesMap.js +17 -4
  78. package/plugins/interact/src/utils/buildStylesMap.test.js +16 -2
  79. package/plugins/interact/src/utils/featureQueries.js +11 -6
  80. package/plugins/interact/src/utils/featureQueries.test.js +8 -1
  81. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  82. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  83. package/plugins/search/src/Search.jsx +3 -1
  84. package/plugins/search/src/events/fetchSuggestions.js +6 -4
  85. package/plugins/search/src/events/fetchSuggestions.test.js +26 -4
  86. package/plugins/search/src/events/formHandlers.js +3 -3
  87. package/plugins/search/src/events/formHandlers.test.js +1 -1
  88. package/plugins/search/src/events/suggestionHandlers.js +2 -2
  89. package/plugins/search/src/events/suggestionHandlers.test.js +1 -1
  90. package/plugins/search/src/utils/updateMap.js +3 -3
  91. package/plugins/search/src/utils/updateMap.test.js +3 -3
  92. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  93. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  94. package/providers/maplibre/dist/umd/index.js +1 -1
  95. package/providers/maplibre/src/appEvents.js +7 -0
  96. package/providers/maplibre/src/appEvents.test.js +18 -4
  97. package/providers/maplibre/src/maplibreProvider.js +52 -0
  98. package/providers/maplibre/src/maplibreProvider.test.js +105 -1
  99. package/providers/maplibre/src/utils/highlightFeatures.js +36 -7
  100. package/providers/maplibre/src/utils/highlightFeatures.test.js +153 -96
  101. package/providers/maplibre/src/utils/hoverCursor.js +61 -0
  102. package/providers/maplibre/src/utils/hoverCursor.test.js +130 -0
  103. package/providers/maplibre/src/utils/patternImages.js +70 -0
  104. package/providers/maplibre/src/utils/patternImages.test.js +180 -0
  105. package/providers/maplibre/src/utils/queryFeatures.js +38 -16
  106. package/providers/maplibre/src/utils/queryFeatures.test.js +20 -3
  107. package/providers/maplibre/src/utils/rasteriseToImageData.js +30 -0
  108. package/providers/maplibre/src/utils/rasteriseToImageData.test.js +69 -0
  109. package/providers/maplibre/src/utils/symbolImages.js +147 -0
  110. package/providers/maplibre/src/utils/symbolImages.test.js +248 -0
  111. package/src/App/components/Markers/Markers.jsx +122 -27
  112. package/src/App/components/Markers/Markers.module.scss +0 -10
  113. package/src/App/components/Markers/Markers.test.jsx +246 -0
  114. package/src/App/hooks/useInterfaceAPI.test.js +156 -0
  115. package/src/App/hooks/useMarkersAPI.js +2 -5
  116. package/src/App/hooks/useMarkersAPI.test.js +4 -4
  117. package/src/App/layout/Layout.jsx +2 -2
  118. package/src/App/layout/Layout.test.jsx +4 -2
  119. package/src/App/store/ServiceProvider.jsx +7 -5
  120. package/src/App/store/mapActionsMap.js +4 -6
  121. package/src/App/store/mapActionsMap.test.js +3 -2
  122. package/src/App/store/mapReducer.js +2 -1
  123. package/src/config/appConfig.js +0 -6
  124. package/src/config/appConfig.test.js +1 -2
  125. package/src/config/defaults.js +0 -2
  126. package/src/config/mapTheme.js +56 -0
  127. package/src/config/patternConfig.js +16 -0
  128. package/src/config/symbolConfig.js +80 -0
  129. package/src/scss/settings/_colors.scss +0 -9
  130. package/src/services/patternRegistry.js +40 -0
  131. package/src/services/patternRegistry.test.js +48 -0
  132. package/src/services/symbolRegistry.js +113 -0
  133. package/src/services/symbolRegistry.test.js +262 -0
  134. package/src/types.js +93 -11
  135. package/src/utils/patternUtils.js +94 -0
  136. package/src/utils/patternUtils.test.js +160 -0
  137. package/src/utils/symbolUtils.js +85 -0
  138. package/src/utils/symbolUtils.test.js +156 -0
  139. package/plugins/beta/datasets/src/adapters/maplibre/patternRegistry.js +0 -48
  140. package/plugins/beta/datasets/src/styles/patterns.js +0 -157
@@ -0,0 +1,115 @@
1
+ # Symbol Registry
2
+
3
+ The symbol registry is a service that manages reusable named symbols for map markers. It is available to plugin authors via `services.symbolRegistry`.
4
+
5
+ > **Application code** that needs a one-off custom marker should pass [`symbolSvgContent`](./symbol-config.md#symbolsvgcontent) directly to `addMarker()` via `MarkerOptions` instead — no registration required.
6
+
7
+ ## Built-in symbols
8
+
9
+ Two symbols are registered by default:
10
+
11
+ | ID | Anchor | Description |
12
+ |----|--------|-------------|
13
+ | `'pin'` | `[0.5, 1]` | Teardrop pin — tip aligns with the coordinate |
14
+ | `'circle'` | `[0.5, 0.5]` | Filled circle — centre aligns with the coordinate |
15
+
16
+ Both use the standard `{{token}}` placeholders and respect the resolution order described in [Symbol Config](./symbol-config.md#how-values-are-resolved).
17
+
18
+ ## Methods
19
+
20
+ Available on `services.symbolRegistry` inside a plugin.
21
+
22
+ ---
23
+
24
+ ### `setDefaults(defaults)`
25
+
26
+ Set constructor-level defaults. Called automatically during app initialisation with the `symbolDefaults` constructor config. Plugin authors do not normally need to call this.
27
+
28
+ ---
29
+
30
+ ### `getDefaults()`
31
+
32
+ Returns the merged app-wide defaults (hardcoded `symbolDefaults.js` + constructor overrides).
33
+
34
+ ```js
35
+ const defaults = services.symbolRegistry.getDefaults()
36
+ // { symbol: 'pin', backgroundColor: '#ca3535', selectedColor: { outdoor: '#0b0c0c', dark: '#ffffff' }, ... }
37
+ ```
38
+
39
+ ---
40
+
41
+ ### `register(symbolDef)`
42
+
43
+ Register a custom symbol. Once registered it can be referenced by ID via `MarkerOptions.symbol` or a dataset `style.symbol`.
44
+
45
+ | Property | Type | Required | Description |
46
+ |----------|------|----------|-------------|
47
+ | `id` | `string` | Yes | Unique symbol identifier |
48
+ | `svg` | `string` | Yes | Inner SVG path content with `{{token}}` placeholders — see [SVG structure](./symbol-config.md#svg-structure) |
49
+ | `viewBox` | `string` | Yes | SVG viewBox, e.g. `'0 0 38 38'` |
50
+ | `anchor` | `[number, number]` | Yes | Normalised [x, y] anchor point |
51
+ | *(token)* | `string \| Record<string, string>` | No | Default token value for this symbol, e.g. `backgroundColor: '#1d70b8'`. `selectedColor` and `selectedWidth` are ignored here — set them via constructor `symbolDefaults`. |
52
+
53
+ ```js
54
+ services.symbolRegistry.register({
55
+ id: 'star',
56
+ viewBox: '0 0 38 38',
57
+ anchor: [0.5, 0.5],
58
+ backgroundColor: '#1d70b8',
59
+ svg: `
60
+ <path d="..." fill="none" stroke="{{selectedColor}}" stroke-width="{{selectedWidth}}"/>
61
+ <path d="..." fill="{{backgroundColor}}" stroke="{{haloColor}}" stroke-width="{{haloWidth}}"/>
62
+ <path d="..." fill="{{foregroundColor}}"/>
63
+ `
64
+ })
65
+ ```
66
+
67
+ See [Symbol Config](./symbol-config.md) for the full list of token properties and the SVG structure convention.
68
+
69
+ ---
70
+
71
+ ### `get(id)`
72
+
73
+ Returns the symbol definition for the given ID, or `undefined` if not registered.
74
+
75
+ ```js
76
+ const symbolDef = services.symbolRegistry.get('pin')
77
+ ```
78
+
79
+ ---
80
+
81
+ ### `list()`
82
+
83
+ Returns an array of all registered symbol definitions.
84
+
85
+ ```js
86
+ const symbols = services.symbolRegistry.list()
87
+ ```
88
+
89
+ ---
90
+
91
+ ### `resolve(symbolDef, styleColors, mapStyleId)`
92
+
93
+ Resolves a symbol's SVG for **normal (unselected) rendering**. The `{{selectedColor}}` token is always replaced with an empty string — the selection ring is structurally present but invisible.
94
+
95
+ ```js
96
+ const svg = services.symbolRegistry.resolve(
97
+ services.symbolRegistry.get('pin'),
98
+ { backgroundColor: '#d4351c' },
99
+ 'outdoor'
100
+ )
101
+ ```
102
+
103
+ ---
104
+
105
+ ### `resolveSelected(symbolDef, styleColors, mapStyleId)`
106
+
107
+ Resolves a symbol's SVG for **selected rendering**. The `{{selectedColor}}` token uses the cascade value. Use this when rendering the highlight layer for an interact or datasets selection.
108
+
109
+ ```js
110
+ const svg = services.symbolRegistry.resolveSelected(
111
+ services.symbolRegistry.get('pin'),
112
+ { backgroundColor: '#d4351c' },
113
+ 'outdoor'
114
+ )
115
+ ```
package/docs/api.md CHANGED
@@ -264,18 +264,6 @@ URL query parameter key used to persist map visibility state.
264
264
 
265
265
  ---
266
266
 
267
- ### `markerColor`
268
- **Type:** `string | Object<string, string>`
269
- **Default:** `'#ff0000'`
270
-
271
- Default colour for map markers. Can be overridden per marker when calling `addMarker()`.
272
-
273
- May be provided as:
274
- - A single colour value applied to all map styles
275
- - An object keyed by map style ID, allowing different colours per style
276
-
277
- ---
278
-
279
267
  ### `markers`
280
268
  **Type:** `MarkerConfig[]`
281
269
 
@@ -285,14 +273,29 @@ See [MarkerConfig](./api/marker-config.md) for full details.
285
273
 
286
274
  ---
287
275
 
288
- ### `markerShape`
289
- **Type:** `string`
290
- **Default:** `'pin'`
276
+ ### `symbolDefaults`
277
+ **Type:** `Partial<SymbolDefaults>`
291
278
 
292
- Default shape for map markers. Can be overridden per marker when calling `addMarker()`.
279
+ App-wide defaults for symbol and marker appearance. These values apply across all markers and datasets unless a more specific value is set at the symbol or per-marker level. Any property omitted here falls back to the built-in default:
293
280
 
294
- > [!NOTE]
295
- > Currently only `'pin'` is available. Additional shapes are planned for a future release.
281
+ | Property | Built-in default |
282
+ |---|---|
283
+ | `symbol` | `'pin'` |
284
+ | `backgroundColor` | `'#ca3535'` |
285
+ | `foregroundColor` | `'#ffffff'` |
286
+ | `haloWidth` | `'1'` |
287
+ | `selectedWidth` | `'6'` |
288
+
289
+ ```js
290
+ new InteractiveMap('map', {
291
+ symbolDefaults: {
292
+ symbol: 'circle',
293
+ backgroundColor: { outdoor: '#1d70b8', dark: '#4c9ed9' }
294
+ }
295
+ })
296
+ ```
297
+
298
+ See [Symbol Config](./api/symbol-config.md) for the full property list.
296
299
 
297
300
  ---
298
301
 
@@ -488,7 +491,7 @@ Add a marker to the map.
488
491
  | `options` | [`MarkerOptions`](./api/marker-config.md#markeroptions) | Optional marker appearance options |
489
492
 
490
493
  ```js
491
- interactiveMap.addMarker('home', [-0.1276, 51.5074], { color: '#0000ff' })
494
+ interactiveMap.addMarker('home', [-0.1276, 51.5074], { backgroundColor: '#1d70b8' })
492
495
  ```
493
496
 
494
497
  ---
@@ -1,6 +1,6 @@
1
1
  # Datasets Plugin
2
2
 
3
- The datasets plugin renders GeoJSON and vector tile datasets on the map, with support for sublayer style rules, layer visibility toggling, a key panel, and runtime style and data updates.
3
+ The datasets plugin renders GeoJSON and vector tile datasets on the map, with support for polygon, line, and symbol (point) layer types, sublayer style rules, layer visibility toggling, a key panel, and runtime style and data updates.
4
4
 
5
5
  ## Usage
6
6
 
@@ -263,6 +263,15 @@ Overrides the shape used to render the key symbol for this dataset. Defaults to
263
263
 
264
264
  Visual style for the dataset. All style properties must be nested within this object.
265
265
 
266
+ **Common properties:**
267
+
268
+ | Property | Type | Description |
269
+ |----------|------|-------------|
270
+ | `opacity` | `number` | Layer opacity from `0` to `1` |
271
+ | `symbolDescription` | `string \| Record<string, string>` | Accessible description of the symbol shown in the key |
272
+
273
+ **Polygon/line properties:**
274
+
266
275
  | Property | Type | Description |
267
276
  |----------|------|-------------|
268
277
  | `stroke` | `string \| Record<string, string>` | Stroke (outline) colour. Accepts a plain colour string or a map-style-keyed object e.g. `{ outdoor: '#ff0000', dark: '#ffffff' }` |
@@ -273,17 +282,56 @@ Visual style for the dataset. All style properties must be nested within this ob
273
282
  | `fillPatternSvgContent` | `string` | Raw SVG content for a custom fill pattern |
274
283
  | `fillPatternForegroundColor` | `string \| Record<string, string>` | Foreground colour for the fill pattern |
275
284
  | `fillPatternBackgroundColor` | `string \| Record<string, string>` | Background colour for the fill pattern |
276
- | `opacity` | `number` | Layer opacity from `0` to `1` |
277
- | `symbolDescription` | `string \| Record<string, string>` | Accessible description of the symbol shown in the key |
278
285
  | `keySymbolShape` | `'polygon' \| 'line'` | Shape used for the key symbol |
279
286
 
287
+ **Symbol (point) properties:**
288
+
289
+ Setting `symbol` or `symbolSvgContent` renders the dataset as a point layer instead of a polygon/line layer.
290
+
291
+ | Property | Type | Description |
292
+ |----------|------|-------------|
293
+ | `symbol` | `string` | Registered symbol ID e.g. `'pin'`, `'circle'`, `'square'` |
294
+ | `symbolSvgContent` | `string` | Inline SVG content for a fully custom symbol (no `<svg>` wrapper). Takes precedence over `symbol` |
295
+ | `symbolViewBox` | `string` | SVG viewBox for the symbol e.g. `'0 0 38 38'`. Defaults to the registered symbol's viewBox |
296
+ | `symbolAnchor` | `[number, number]` | Anchor point as a normalised `[x, y]` pair. Defaults to the registered symbol's anchor |
297
+ | `symbolBackgroundColor` | `string \| Record<string, string>` | Background fill colour of the symbol |
298
+ | `symbolForegroundColor` | `string \| Record<string, string>` | Foreground fill colour of the symbol (e.g. the inner dot) |
299
+ | `symbolHaloWidth` | `string` | Stroke width of the halo in SVG units |
300
+ | `symbolGraphic` | `string` | SVG `d` attribute for the foreground graphic path. Use named values (`'dot'`, `'cross'`, `'diamond'`, `'triangle'`, `'square'`) or supply your own path data |
301
+
302
+ Symbol colour properties use the `symbol` prefix to distinguish them from polygon/line properties in the same style object. They follow the same resolution order and support style-keyed colour objects in the same way as markers — see [Symbol Config](../api/symbol-config.md) for details.
303
+
304
+ `haloColor` and `selectedColor` are not settable here — they are basemap-level properties set on [`MapStyleConfig`](../api/map-style-config.md).
305
+
280
306
  ```js
307
+ // Polygon/line dataset
281
308
  style: {
282
309
  stroke: { outdoor: '#d4351c', dark: '#ffffff' },
283
310
  strokeWidth: 2,
284
311
  fill: 'rgba(212,53,28,0.1)',
285
312
  symbolDescription: { outdoor: 'Red outline' }
286
313
  }
314
+
315
+ // Point dataset — registered symbol with colour overrides
316
+ style: {
317
+ symbol: 'pin',
318
+ symbolBackgroundColor: '#1d70b8',
319
+ symbolForegroundColor: '#ffffff'
320
+ }
321
+
322
+ // Point dataset — style-keyed colours for multi-basemap support
323
+ style: {
324
+ symbol: 'pin',
325
+ symbolBackgroundColor: { outdoor: '#1d70b8', dark: '#5694ca' }
326
+ }
327
+
328
+ // Point dataset — custom inline SVG
329
+ style: {
330
+ symbolSvgContent: '<circle cx="19" cy="19" r="12" fill="{{backgroundColor}}"/>',
331
+ symbolViewBox: '0 0 38 38',
332
+ symbolAnchor: [0.5, 0.5],
333
+ symbolBackgroundColor: '#1d70b8'
334
+ }
287
335
  ```
288
336
 
289
337
  ---
@@ -292,9 +340,9 @@ style: {
292
340
 
293
341
  **Type:** `Sublayer[]`
294
342
 
295
- Array of sublayer rules that partition the dataset into visually distinct groups based on feature filters. Each sublayer is rendered as a separate pair of map layers.
343
+ Array of sublayer rules that partition the dataset into visually distinct groups based on feature filters. Each sublayer is rendered as a separate map layer.
296
344
 
297
- Sublayers inherit the parent dataset's style and only override what they specify. Fill precedence (highest to lowest): sublayer's own `fillPattern` → sublayer's own `fill` → parent's `fillPattern` → parent's `fill`.
345
+ Sublayers inherit the parent dataset's style and only override what they specify in their own `style` object. For polygon/line datasets, fill precedence is (highest to lowest): sublayer `fillPattern` → sublayer `fill` → parent `fillPattern` → parent `fill`. For symbol datasets, each symbol property is inherited individually from the parent unless the sublayer sets it explicitly.
298
346
 
299
347
  #### `Sublayer` properties
300
348
 
@@ -303,10 +351,12 @@ Sublayers inherit the parent dataset's style and only override what they specify
303
351
  | `id` | `string` | **Required.** Unique identifier within the dataset |
304
352
  | `label` | `string` | Human-readable name shown in the Layers and Key panels |
305
353
  | `filter` | `FilterExpression` | MapLibre filter expression to match features for this sublayer |
306
- | `style` | `Object` | Style overrides for this sublayer. Accepts the same properties as the dataset `style` object |
354
+ | `style` | `Object` | Style overrides. Accepts the same properties as the dataset `style` object |
307
355
  | `showInKey` | `boolean` | Shows this sublayer in the Key panel. Inherits from dataset if not set |
308
356
  | `toggleVisibility` | `boolean` | Shows this sublayer in the Layers panel. **Default:** `false` |
309
357
 
358
+ **Polygon/line example:**
359
+
310
360
  ```js
311
361
  sublayers: [
312
362
  {
@@ -334,6 +384,44 @@ sublayers: [
334
384
  ]
335
385
  ```
336
386
 
387
+ **Symbol (point) example — scheduled monuments by type:**
388
+
389
+ When the parent dataset has `symbol` set, each sublayer can override individual symbol properties to represent different categories. Properties not set on the sublayer are inherited from the parent.
390
+
391
+ ```js
392
+ {
393
+ id: 'scheduled-monuments',
394
+ geojson: scheduledMonumentsData,
395
+ style: { symbol: 'square' },
396
+ sublayers: [
397
+ {
398
+ id: 'prehistoric',
399
+ label: 'Prehistoric sites',
400
+ filter: ['==', ['get', 'type'], 'prehistoric'],
401
+ showInKey: true,
402
+ toggleVisibility: true,
403
+ style: { symbolBackgroundColor: '#0f7a52' }
404
+ },
405
+ {
406
+ id: 'roman',
407
+ label: 'Roman sites',
408
+ filter: ['==', ['get', 'type'], 'roman'],
409
+ showInKey: true,
410
+ toggleVisibility: true,
411
+ style: { symbolBackgroundColor: '#54319f' }
412
+ },
413
+ {
414
+ id: 'medieval',
415
+ label: 'Medieval sites',
416
+ filter: ['==', ['get', 'type'], 'medieval'],
417
+ showInKey: true,
418
+ toggleVisibility: true,
419
+ style: { symbolBackgroundColor: '#ca357c' }
420
+ }
421
+ ]
422
+ }
423
+ ```
424
+
337
425
  ---
338
426
 
339
427
  ## Methods
@@ -436,24 +524,32 @@ datasetsPlugin.setFeatureVisibility(true, [123, 456], {
436
524
 
437
525
  Update the visual style of a dataset or sublayer at runtime. When targeting a sublayer, only the properties specified are overridden — the sublayer inherits all other styles from the parent dataset.
438
526
 
527
+ For symbol datasets, pass `symbol` as the style property to change the symbol config.
528
+
439
529
  | Argument | Type | Description |
440
530
  |----------|------|-------------|
441
- | `style` | `Object` | Style properties to apply. Accepts the same properties as `dataset.style` |
531
+ | `style` | `Object` | Style properties to apply. Accepts the same properties as `dataset.style`, plus `symbol` |
442
532
  | `scope.datasetId` | `string` | ID of the dataset |
443
533
  | `scope.sublayerId` | `string` | Optional. When provided, targets a single sublayer |
444
534
 
445
535
  ```js
446
- // Dataset level
536
+ // Polygon/line dataset
447
537
  datasetsPlugin.setStyle(
448
538
  { stroke: '#0000ff', strokeWidth: 3 },
449
539
  { datasetId: 'my-parcels' }
450
540
  )
451
541
 
452
- // Sublayer level
542
+ // Sublayer — polygon
453
543
  datasetsPlugin.setStyle(
454
544
  { stroke: '#00703c', fillPattern: 'diagonal-cross-hatch', fillPatternForegroundColor: '#00703c' },
455
545
  { datasetId: 'my-parcels', sublayerId: 'active' }
456
546
  )
547
+
548
+ // Sublayer — symbol colour override
549
+ datasetsPlugin.setStyle(
550
+ { symbolBackgroundColor: '#912b88' },
551
+ { datasetId: 'flood-warnings', sublayerId: 'severe' }
552
+ )
457
553
  ```
458
554
 
459
555
  ---
@@ -8,9 +8,9 @@ The interact plugin provides a unified way to handle user interactions for selec
8
8
  import createInteractPlugin from '@defra/interactive-map/plugins/interact'
9
9
 
10
10
  const interactPlugin = createInteractPlugin({
11
- interactionMode: 'auto',
11
+ interactionModes: ['selectMarker', 'selectFeature'],
12
12
  multiSelect: true,
13
- dataLayers: [
13
+ layers: [
14
14
  { layerId: 'my-layer', idProperty: 'id' }
15
15
  ]
16
16
  })
@@ -40,40 +40,61 @@ Array of mode identifiers. When set, the plugin does not render when the app is
40
40
 
41
41
  ---
42
42
 
43
- ### `interactionMode`
44
- **Type:** `'marker' | 'select' | 'auto'`
45
- **Default:** `'marker'`
43
+ ### `interactionModes`
44
+ **Type:** `Array<'selectMarker' | 'selectFeature' | 'placeMarker'>`
45
+ **Default:** `['selectMarker']`
46
46
 
47
- Controls how user clicks are interpreted.
47
+ Controls which interactions are active when the user clicks the map. Values can be combined freely — the plugin always processes them in a fixed priority order: marker selection → feature selection → place marker.
48
48
 
49
- - `'marker'` — clicking always places a location marker at the clicked coordinates
50
- - `'select'` — clicking attempts to match a feature from `dataLayers`; click outside clears selection (unless `deselectOnClickOutside` is `false`)
51
- - `'auto'` — attempts feature matching first, falls back to placing a marker if no feature is found
49
+ - `'selectMarker'` — clicking a placed marker toggles its selection state
50
+ - `'selectFeature'` — clicking the map attempts to match a feature from `layers`
51
+ - `'placeMarker'` — if no feature is matched (or `selectFeature` is not active), places a location marker at the clicked coordinates
52
+
53
+ **Common combinations:**
54
+
55
+ ```js
56
+ interactionModes: ['selectMarker'] // marker selection only (default)
57
+ interactionModes: ['selectFeature'] // feature selection only
58
+ interactionModes: ['placeMarker'] // always place a marker on click
59
+ interactionModes: ['selectMarker', 'selectFeature'] // select markers or features
60
+ interactionModes: ['selectFeature', 'placeMarker'] // select features, fall back to placing a marker
61
+ interactionModes: ['selectMarker', 'selectFeature', 'placeMarker'] // all interactions active
62
+ ```
52
63
 
53
64
  ---
54
65
 
55
- ### `dataLayers`
56
- **Type:** `Array<DataLayer>`
66
+ ### `layers`
67
+ **Type:** `Array<LayerConfig>`
57
68
  **Default:** `[]`
58
69
 
59
70
  Array of map layer configurations that are selectable. Each entry specifies which layer to watch and how to identify features.
60
71
 
61
72
  ```js
62
- dataLayers: [
63
- { layerId: 'my-polygons', idProperty: 'id' },
73
+ layers: [
74
+ { layerId: 'my-polygons', idProperty: 'guid' },
64
75
  { layerId: 'my-lines' }
65
76
  ]
66
77
  ```
67
78
 
68
- #### `DataLayer` properties
79
+ #### `LayerConfig` properties
69
80
 
70
81
  | Property | Type | Description |
71
82
  |----------|------|-------------|
72
83
  | `layerId` | `string` | **Required.** The map layer identifier to enable selection on |
73
84
  | `idProperty` | `string` | Property name used as the feature's unique identifier. If omitted, features are matched by index |
74
- | `selectedStroke` | `string` | Overrides the global `selectedStroke` for this layer |
75
- | `selectedFill` | `string` | Overrides the global `selectedFill` for this layer |
76
- | `selectedStrokeWidth` | `number` | Overrides the global `selectedStrokeWidth` for this layer |
85
+ | `selectedStroke` | `string` | Overrides the selection stroke colour for this layer. Defaults to `MapStyleConfig.selectedColor` |
86
+ | `selectedFill` | `string` | Overrides the selection fill colour for this layer. Defaults to `transparent` |
87
+ | `selectedStrokeWidth` | `number` | Overrides the selection stroke width for this layer. Defaults to `3` |
88
+
89
+ #### Finding layer IDs
90
+
91
+ What to use as `layerId` depends on how your data is added to the map — these are the layers the plugin will enable for feature selection:
92
+
93
+ - **Map provider directly** — use the layer IDs defined in your style or added via your map provider's layer API
94
+ - **Datasets plugin** — use the dataset ID, or the sublayer ID for datasets with sublayers
95
+ - **Draw plugin** — uses generated layer IDs; inspect the map at runtime to find them (e.g. `fill-inactive.cold`, `stroke-inactive.cold` when using MapLibre)
96
+
97
+ If you're unsure of the layer IDs available at runtime, set `debug: true` in the interact plugin options — this lets you query the map and inspect layer names in the browser console.
77
98
 
78
99
  ---
79
100
 
@@ -97,7 +118,7 @@ When `true`, only features that touch or overlap the existing selection can be a
97
118
  **Type:** `boolean`
98
119
  **Default:** `false`
99
120
 
100
- When `true`, clicking outside any selectable layer clears the current selection.
121
+ When `true`, clicking outside any selectable feature clears the current selection.
101
122
 
102
123
  ---
103
124
 
@@ -105,7 +126,7 @@ When `true`, clicking outside any selectable layer clears the current selection.
105
126
  **Type:** `number`
106
127
  **Default:** `10`
107
128
 
108
- Click detection radius in pixels. Increases the hit area around the cursor when matching features, which is useful for lines and points.
129
+ Click detection radius in pixels applied to line features. Lines have a 1px rendered width so a buffer is required for reliable selection. Polygon and symbol/icon features use exact hit detection and are unaffected by this value.
109
130
 
110
131
  ---
111
132
 
@@ -117,35 +138,32 @@ When `true`, the app closes after the user clicks "Done" or "Cancel".
117
138
 
118
139
  ---
119
140
 
120
- ### `markerColor`
121
- **Type:** `string`
122
- **Default:** `'rgba(212,53,28,1)'`
123
-
124
- Color of the location marker placed on the map.
125
-
126
- ---
141
+ ### `marker`
142
+ **Type:** `MarkerOptions`
127
143
 
128
- ### `selectedStroke`
129
- **Type:** `string`
130
- **Default:** `'rgba(212,53,28,1)'`
144
+ Appearance of the location marker placed on the map. Accepts the same properties as [`MarkerOptions`](../api/marker-config.md#markeroptions). See [Symbol Config](../api/symbol-config.md) for the full property reference.
131
145
 
132
- Stroke color used to highlight selected features. Can be overridden per layer via `dataLayers`.
133
-
134
- ---
135
-
136
- ### `selectedFill`
137
- **Type:** `string`
138
- **Default:** `'rgba(255, 0, 0, 0.1)'`
146
+ ```js
147
+ createInteractPlugin({
148
+ marker: {
149
+ symbol: 'pin',
150
+ backgroundColor: { outdoor: '#d4351c', dark: '#ff6b6b' },
151
+ foregroundColor: '#ffffff'
152
+ }
153
+ })
154
+ ```
139
155
 
140
- Fill color used to highlight selected features. Can be overridden per layer via `dataLayers`.
156
+ When not set, the marker inherits from the constructor `symbolDefaults` cascade.
141
157
 
142
158
  ---
143
159
 
144
160
  ### `selectedStrokeWidth`
145
161
  **Type:** `number`
146
- **Default:** `2`
162
+ **Default:** `3`
163
+
164
+ Stroke width used to highlight selected features. Can be overridden per layer via `layers[].selectedStrokeWidth`.
147
165
 
148
- Stroke width used to highlight selected features. Can be overridden per layer via `dataLayers`.
166
+ > **Selection colours** stroke and fill colours for selected features are not configured here. Stroke colour comes from `MapStyleConfig.selectedColor` (falling back to the `mapColorScheme` scheme default), ensuring the selection colour stays consistent with the rest of the map theme. Fill defaults to `transparent`. Both can be overridden per layer via `layers[].selectedStroke` and `layers[].selectedFill`.
149
167
 
150
168
  ---
151
169
 
@@ -169,7 +187,7 @@ interactiveMap.on('map:ready', () => {
169
187
  })
170
188
 
171
189
  // Override options at runtime
172
- interactPlugin.enable({ multiSelect: true, interactionMode: 'select' })
190
+ interactPlugin.enable({ multiSelect: true, interactionModes: ['selectFeature'] })
173
191
  ```
174
192
 
175
193
  ---
@@ -247,12 +265,15 @@ Emitted when the user confirms their selection (clicks "Done").
247
265
  **Payload:**
248
266
  ```js
249
267
  {
250
- // If a marker was placed:
268
+ // If a location marker was placed:
251
269
  coords: [lng, lat],
252
270
 
253
271
  // If features were selected:
254
272
  selectedFeatures: [...],
255
- selectionBounds: [west, south, east, north]
273
+ selectionBounds: [west, south, east, north],
274
+
275
+ // If markers were selected:
276
+ selectedMarkers: ['...']
256
277
  }
257
278
  ```
258
279
 
@@ -264,6 +285,9 @@ interactiveMap.on('interact:done', (e) => {
264
285
  if (e.selectedFeatures) {
265
286
  console.log('Features selected:', e.selectedFeatures)
266
287
  }
288
+ if (e.selectedMarkers) {
289
+ console.log('Markers selected:', e.selectedMarkers)
290
+ }
267
291
  })
268
292
  ```
269
293
 
@@ -285,7 +309,7 @@ interactiveMap.on('interact:cancel', () => {
285
309
 
286
310
  ### `interact:selectionchange`
287
311
 
288
- Emitted whenever the feature selection changes.
312
+ Emitted whenever the selected features or selected markers change.
289
313
 
290
314
  **Payload:**
291
315
  ```js
@@ -293,6 +317,7 @@ Emitted whenever the feature selection changes.
293
317
  selectedFeatures: [
294
318
  { featureId: '...', layerId: '...', properties: {...}, geometry: {...} }
295
319
  ],
320
+ selectedMarkers: ['...'], // array of selected marker IDs
296
321
  selectionBounds: [west, south, east, north] | null,
297
322
  canMerge: boolean, // true when all selected features are contiguous
298
323
  canSplit: boolean // true when exactly one Polygon or MultiPolygon is selected
@@ -90,10 +90,22 @@ Whether to place a marker on the map when a search result is selected.
90
90
 
91
91
  ---
92
92
 
93
- ### `markerColor`
94
- **Type:** `string`
93
+ ### `marker`
94
+ **Type:** `MarkerOptions`
95
+
96
+ Appearance of the marker placed when a search result is selected. Accepts the same properties as [`MarkerOptions`](../api/marker-config.md#markeroptions). See [Symbol Config](../api/symbol-config.md) for the full property reference.
97
+
98
+ ```js
99
+ searchPlugin({
100
+ showMarker: true,
101
+ marker: {
102
+ symbol: 'circle',
103
+ backgroundColor: { outdoor: '#1d70b8', dark: '#5694ca' }
104
+ }
105
+ })
106
+ ```
95
107
 
96
- Colour of the search result marker.
108
+ When not set, the marker inherits from the constructor `symbolDefaults` cascade.
97
109
 
98
110
  ---
99
111
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/interactive-map",
3
- "version": "0.0.17-alpha",
3
+ "version": "0.0.18-alpha",
4
4
  "description": "An accessible map component",
5
5
  "repository": {
6
6
  "type": "git",