@defra/interactive-map 0.0.30-alpha → 0.0.32-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 (247) hide show
  1. package/README.md +2 -2
  2. package/assets/images/bright-ofm-thumb.jpg +0 -0
  3. package/assets/images/dark-ofm-thumb.jpg +0 -0
  4. package/assets/images/road-ofm-thumb.jpg +0 -0
  5. package/assets/templates/add-polygons.njk +143 -0
  6. package/assets/templates/add-symbols.njk +119 -0
  7. package/assets/templates/{map.njk → basic-map.njk} +9 -17
  8. package/assets/templates/button-map.njk +56 -0
  9. package/assets/templates/draw-tools.njk +161 -0
  10. package/assets/templates/marker-panel.njk +91 -0
  11. package/assets/templates/place-marker.njk +104 -0
  12. package/assets/templates/search-control.njk +98 -0
  13. package/assets/templates/select-feature.njk +101 -0
  14. package/assets/templates/style-switcher.njk +87 -0
  15. package/assets/templates/toggle-marker-label.njk +74 -0
  16. package/dist/css/index.css +1 -1
  17. package/dist/esm/im-core.js +1 -1
  18. package/dist/esm/im-shell.js +1 -1
  19. package/dist/umd/im-core.js +1 -1
  20. package/dist/umd/index.js +1 -1
  21. package/docs/api/context.md +4 -4
  22. package/docs/api.md +166 -37
  23. package/docs/assets/css/docusaurus.scss +36 -4
  24. package/docs/assets/images/add-polygons.jpg +0 -0
  25. package/docs/assets/images/add-symbols.jpg +0 -0
  26. package/docs/assets/images/basic-map.jpg +0 -0
  27. package/docs/assets/images/button-first.jpg +0 -0
  28. package/docs/assets/images/deuteranopia-ozs-map-thumb.jpg +0 -0
  29. package/docs/assets/images/draw.jpg +0 -0
  30. package/docs/assets/images/marker-panel.jpg +0 -0
  31. package/docs/assets/images/night-ozs-map-thumb.jpg +0 -0
  32. package/docs/assets/images/outdoor-ozs-map-thumb.jpg +0 -0
  33. package/docs/assets/images/place-marker.jpg +0 -0
  34. package/docs/assets/images/screenshot.jpg +0 -0
  35. package/docs/assets/images/search.jpg +0 -0
  36. package/docs/assets/images/select-feature.jpg +0 -0
  37. package/docs/assets/images/style-switcher.jpg +0 -0
  38. package/docs/assets/images/toggle-marker-label.jpg +0 -0
  39. package/docs/assets/images/tritanopia-ozs-map-thumb.jpg +0 -0
  40. package/docs/examples/add-marker-with-panel.mdx +3 -3
  41. package/docs/examples/add-polygons.mdx +225 -0
  42. package/docs/examples/add-symbols.mdx +177 -0
  43. package/docs/examples/basic-map.mdx +2 -2
  44. package/docs/examples/draw-tools.mdx +271 -0
  45. package/docs/examples/index.mdx +95 -11
  46. package/docs/examples/place-marker.mdx +151 -0
  47. package/docs/examples/search-control.mdx +71 -0
  48. package/docs/examples/select-feature.mdx +146 -0
  49. package/docs/examples/style-switcher.mdx +135 -0
  50. package/docs/examples/toggle-marker-label.mdx +3 -3
  51. package/docs/plugins/datasets.md +157 -122
  52. package/docs/plugins/interact.md +1 -4
  53. package/docusaurus.config.cjs +9 -2
  54. package/govuk-prototype-kit.config.json +52 -2
  55. package/package.json +1 -1
  56. package/plugins/beta/datasets/dist/css/index.css +5 -2
  57. package/plugins/beta/datasets/dist/esm/im-datasets-ml-adapter.js +1 -0
  58. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  59. package/plugins/beta/datasets/dist/esm/index.js +1 -1
  60. package/plugins/beta/datasets/dist/umd/im-datasets-ml-adapter.js +2 -0
  61. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
  62. package/plugins/beta/datasets/dist/umd/index.js +2 -0
  63. package/plugins/beta/datasets/src/adapters/loadLayerAdapter.js +33 -0
  64. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +44 -120
  65. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +145 -245
  66. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +516 -0
  67. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +209 -0
  68. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.test.js +483 -0
  69. package/plugins/beta/datasets/src/api/addDataset.js +2 -5
  70. package/plugins/beta/datasets/src/api/addDataset.test.js +16 -0
  71. package/plugins/beta/datasets/src/api/getOpacity.js +14 -14
  72. package/plugins/beta/datasets/src/api/getOpacity.test.js +72 -0
  73. package/plugins/beta/datasets/src/api/getStyle.js +8 -9
  74. package/plugins/beta/datasets/src/api/getStyle.test.js +42 -0
  75. package/plugins/beta/datasets/src/api/removeDataset.js +7 -7
  76. package/plugins/beta/datasets/src/api/removeDataset.test.js +45 -0
  77. package/plugins/beta/datasets/src/api/setData.js +9 -3
  78. package/plugins/beta/datasets/src/api/setData.test.js +62 -0
  79. package/plugins/beta/datasets/src/api/setDatasetVisibility.js +5 -30
  80. package/plugins/beta/datasets/src/api/setDatasetVisibility.test.js +31 -0
  81. package/plugins/beta/datasets/src/api/setFeatureVisibility.js +9 -18
  82. package/plugins/beta/datasets/src/api/setFeatureVisibility.test.js +74 -0
  83. package/plugins/beta/datasets/src/api/setGlobals.js +20 -0
  84. package/plugins/beta/datasets/src/api/setGlobals.test.js +63 -0
  85. package/plugins/beta/datasets/src/api/setOpacity.js +8 -27
  86. package/plugins/beta/datasets/src/api/setOpacity.test.js +31 -0
  87. package/plugins/beta/datasets/src/api/setStyle.js +5 -21
  88. package/plugins/beta/datasets/src/api/setStyle.test.js +53 -0
  89. package/plugins/beta/datasets/src/components/Key/Key.jsx +51 -0
  90. package/plugins/beta/datasets/src/components/Key/KeyGroupItem.jsx +18 -0
  91. package/plugins/beta/datasets/src/components/Key/KeyItem.jsx +20 -0
  92. package/plugins/beta/datasets/src/components/Key/KeySvg.jsx +22 -0
  93. package/plugins/beta/datasets/src/components/Key/KeySvg.test.jsx +87 -0
  94. package/plugins/beta/datasets/src/components/Key/KeySvgLine.jsx +19 -0
  95. package/plugins/beta/datasets/src/components/Key/KeySvgLine.test.jsx +71 -0
  96. package/plugins/beta/datasets/src/components/{KeySvgPattern.jsx → Key/KeySvgPattern.jsx} +3 -4
  97. package/plugins/beta/datasets/src/components/Key/KeySvgPattern.test.jsx +55 -0
  98. package/plugins/beta/datasets/src/components/Key/KeySvgRect.jsx +22 -0
  99. package/plugins/beta/datasets/src/components/Key/KeySvgRect.test.jsx +85 -0
  100. package/plugins/beta/datasets/src/components/{KeySvgSymbol.jsx → Key/KeySvgSymbol.jsx} +5 -5
  101. package/plugins/beta/datasets/src/components/Key/KeySvgSymbol.test.jsx +80 -0
  102. package/plugins/beta/datasets/src/components/Key/svgProperties.test.js +61 -0
  103. package/plugins/beta/datasets/src/{panels → components/LayersMenu}/Layers.module.scss +15 -11
  104. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenu.jsx +33 -0
  105. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuCheckbox.jsx +31 -0
  106. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuGroupWrapper.jsx +19 -0
  107. package/plugins/beta/datasets/src/datasets.scss +2 -2
  108. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +8 -8
  109. package/plugins/beta/datasets/src/{DatasetsInit.jsx → initialise/DatasetsInit.jsx} +19 -21
  110. package/plugins/beta/datasets/src/{defaults.js → initialise/defaults.js} +17 -2
  111. package/plugins/beta/datasets/src/initialise/defaults.test.js +127 -0
  112. package/plugins/beta/datasets/src/{datasets.js → initialise/initialiseDatasets.js} +30 -26
  113. package/plugins/beta/datasets/src/initialise/initialiseDatasets.test.js +273 -0
  114. package/plugins/beta/datasets/src/manifest.js +8 -6
  115. package/plugins/beta/datasets/src/reducers/__data__/demoDatasets.js +253 -0
  116. package/plugins/beta/datasets/src/reducers/datasetsToMenu.js +83 -0
  117. package/plugins/beta/datasets/src/reducers/datasetsToMenu.test.js +84 -0
  118. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.js +41 -0
  119. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.test.js +117 -0
  120. package/plugins/beta/datasets/src/reducers/pluginState.js +206 -0
  121. package/plugins/beta/datasets/src/reducers/pluginState.test.js +386 -0
  122. package/plugins/beta/datasets/src/registry/__mocks__/datasetRegistry.js +29 -0
  123. package/plugins/beta/datasets/src/registry/createDataset.js +5 -0
  124. package/plugins/beta/datasets/src/registry/dataset.js +176 -0
  125. package/plugins/beta/datasets/src/registry/dataset.test.js +497 -0
  126. package/plugins/beta/datasets/src/registry/datasetDefinitionCache.js +35 -0
  127. package/plugins/beta/datasets/src/registry/datasetRegistry.js +116 -0
  128. package/plugins/beta/datasets/src/registry/datasetRegistry.test.js +335 -0
  129. package/plugins/beta/datasets/src/registry/dynamicGeoJson.js +17 -0
  130. package/plugins/beta/datasets/src/registry/globalDataset.js +36 -0
  131. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  132. package/plugins/beta/draw-es/src/api/addFeature.js +12 -3
  133. package/plugins/beta/draw-es/src/events.js +8 -3
  134. package/plugins/beta/draw-es/src/events.test.js +2 -0
  135. package/plugins/beta/draw-es/src/graphic.js +26 -2
  136. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  137. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  138. package/plugins/beta/draw-ml/src/DrawInit.jsx +8 -0
  139. package/plugins/beta/draw-ml/src/api/newLine.js +1 -0
  140. package/plugins/beta/draw-ml/src/api/newPolygon.js +1 -0
  141. package/plugins/beta/draw-ml/src/events.js +13 -1
  142. package/plugins/beta/draw-ml/src/index.js +1 -1
  143. package/plugins/beta/draw-ml/src/manifest.js +3 -0
  144. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +21 -12
  145. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +4 -0
  146. package/plugins/beta/draw-ol/dist/esm/EditMode.js +1 -1
  147. package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
  148. package/plugins/beta/draw-ol/src/edit/keyboardHandler.js +4 -2
  149. package/plugins/beta/map-styles/src/mapStyles.scss +41 -41
  150. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  151. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  152. package/plugins/interact/dist/umd/index.js +1 -1
  153. package/plugins/interact/src/InteractInit.jsx +3 -5
  154. package/plugins/interact/src/InteractInit.test.js +1 -3
  155. package/plugins/interact/src/events.js +2 -29
  156. package/plugins/interact/src/events.test.js +4 -102
  157. package/plugins/interact/src/hooks/useAttachEvents.js +3 -4
  158. package/plugins/interact/src/hooks/useAttachEvents.test.js +1 -3
  159. package/plugins/interact/src/hooks/useCrossHairVisibility.js +4 -10
  160. package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +5 -10
  161. package/plugins/interact/src/hooks/useHighlightSync.js +1 -10
  162. package/plugins/interact/src/hooks/useHighlightSync.test.js +2 -21
  163. package/plugins/interact/src/hooks/useInteractionHandlers.js +15 -12
  164. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +28 -29
  165. package/plugins/interact/src/manifest.js +0 -35
  166. package/plugins/interact/src/manifest.test.js +3 -43
  167. package/plugins/interact/src/reducer.js +4 -22
  168. package/plugins/interact/src/reducer.test.js +11 -39
  169. package/plugins/search/dist/css/index.css +5 -2
  170. package/plugins/search/src/components/Form/Form.module.scss +5 -2
  171. package/plugins/search/src/search.scss +4 -4
  172. package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
  173. package/providers/beta/esri/src/esriProvider.js +4 -0
  174. package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
  175. package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
  176. package/providers/beta/openlayers/src/openlayersProvider.js +4 -0
  177. package/providers/beta/openlayers/src/openlayersProvider.test.js +5 -0
  178. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  179. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  180. package/providers/maplibre/src/maplibreProvider.js +6 -4
  181. package/providers/maplibre/src/maplibreProvider.test.js +5 -0
  182. package/providers/maplibre/src/utils/highlightFeatures.js +74 -25
  183. package/providers/maplibre/src/utils/highlightFeatures.test.js +132 -0
  184. package/providers/maplibre/src/utils/patternImages.js +2 -4
  185. package/providers/maplibre/src/utils/symbolImages.js +1 -4
  186. package/rollup.esm.mjs +2 -8
  187. package/src/App/components/CrossHair/CrossHair.module.scss +8 -8
  188. package/src/App/components/MapButton/MapButton.jsx +16 -1
  189. package/src/App/components/MapButton/MapButton.module.scss +12 -1
  190. package/src/App/components/MapButton/MapButton.test.jsx +40 -1
  191. package/src/App/hooks/useContinueEnabledEvaluator.js +31 -0
  192. package/src/App/hooks/useContinueEnabledEvaluator.test.js +118 -0
  193. package/src/App/hooks/useEvaluateProp.js +15 -15
  194. package/src/App/hooks/useEvaluateProp.test.js +80 -76
  195. package/src/App/hooks/useLayoutMeasurements.js +11 -5
  196. package/src/App/hooks/useResizeObserver.js +15 -2
  197. package/src/App/hooks/useResizeObserver.test.js +1 -0
  198. package/src/App/renderer/PluginInits.jsx +2 -0
  199. package/src/App/renderer/mapButtons.js +21 -1
  200. package/src/App/renderer/mapButtons.test.js +46 -1
  201. package/src/App/store/appReducer.js +1 -1
  202. package/src/App/store/appReducer.test.js +37 -0
  203. package/src/InteractiveMap/InteractiveMap.js +14 -4
  204. package/src/InteractiveMap/InteractiveMap.test.js +11 -3
  205. package/src/InteractiveMap/behaviourController.js +3 -1
  206. package/src/InteractiveMap/behaviourController.test.js +6 -3
  207. package/src/InteractiveMap/domStateManager.js +2 -0
  208. package/src/config/appConfig.js +25 -0
  209. package/src/config/appConfig.test.js +50 -0
  210. package/src/config/defaults.js +1 -0
  211. package/src/config/events.js +21 -3
  212. package/src/scss/settings/_dimensions.scss +1 -0
  213. package/src/services/patternRegistry.js +8 -4
  214. package/src/types.js +7 -0
  215. package/src/utils/patternUtils.js +1 -2
  216. package/src/utils/patternUtils.test.js +9 -0
  217. package/webpack.dev.mjs +10 -0
  218. package/webpack.umd.mjs +1 -1
  219. package/docs/assets/images/screens-blue.jpg +0 -0
  220. package/docs/assets/images/screens-white.jpg +0 -0
  221. package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +0 -1
  222. package/plugins/beta/datasets/dist/adapters/maplibre/esm/index.js +0 -1
  223. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +0 -2
  224. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +0 -2
  225. package/plugins/beta/datasets/dist/umd/maplibre/index.js +0 -2
  226. package/plugins/beta/datasets/src/adapters/maplibre/index.js +0 -18
  227. package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +0 -75
  228. package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +0 -27
  229. package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +0 -31
  230. package/plugins/beta/datasets/src/components/KeySvg.jsx +0 -24
  231. package/plugins/beta/datasets/src/components/KeySvgLine.jsx +0 -19
  232. package/plugins/beta/datasets/src/components/KeySvgRect.jsx +0 -22
  233. package/plugins/beta/datasets/src/index.maplibre.umd.js +0 -23
  234. package/plugins/beta/datasets/src/panels/Key.jsx +0 -62
  235. package/plugins/beta/datasets/src/panels/Layers.jsx +0 -141
  236. package/plugins/beta/datasets/src/reducer.js +0 -219
  237. package/plugins/beta/datasets/src/reducers/keyReducer.js +0 -34
  238. package/plugins/beta/datasets/src/utils/filters.js +0 -34
  239. package/plugins/beta/datasets/src/utils/mergeSublayer.js +0 -86
  240. package/plugins/beta/draw-ml/dist/css/index.css +0 -21
  241. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-maplibre-adapter.js.LICENSE.txt → im-datasets-ml-adapter.js.LICENSE.txt} +0 -0
  242. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-plugin.js.LICENSE.txt → im-datasets-plugin.js.LICENSE.txt} +0 -0
  243. /package/plugins/beta/datasets/dist/umd/{maplibre/index.js.LICENSE.txt → index.js.LICENSE.txt} +0 -0
  244. /package/plugins/beta/datasets/src/components/{EmptyKey.jsx → Key/EmptyKey.jsx} +0 -0
  245. /package/plugins/beta/datasets/src/components/{EmptyKey.test.jsx → Key/EmptyKey.test.jsx} +0 -0
  246. /package/plugins/beta/datasets/src/{panels → components/Key}/Key.module.scss +0 -0
  247. /package/plugins/beta/datasets/src/components/{svgProperties.js → Key/svgProperties.js} +0 -0
@@ -0,0 +1,31 @@
1
+ import { datasetRegistry } from '../../registry/datasetRegistry.js'
2
+
3
+ export const LayersMenuCheckbox = ({ menuGroupItem, onChange }) => {
4
+ const registryDataset = datasetRegistry.getDataset(menuGroupItem.id)
5
+ if (!registryDataset) {
6
+ return null
7
+ }
8
+ const datasetId = registryDataset.isSublayer ? registryDataset.parentId : registryDataset.id
9
+ const sublayerId = registryDataset.isSublayer ? registryDataset.id : undefined
10
+ const itemClass = `im-c-datasets-layers__item govuk-checkboxes govuk-checkboxes--small${registryDataset.visible ? '' : ' im-c-datasets-layers__item--checked'}`
11
+ return (
12
+ <div key={registryDataset.id} className={itemClass} data-module='govuk-checkboxes'>
13
+ <div className='govuk-checkboxes__item'>
14
+ <input
15
+ className='govuk-checkboxes__input'
16
+ id={registryDataset.id}
17
+ data-dataset-id={datasetId}
18
+ data-sublayer-id={sublayerId}
19
+ name='layers'
20
+ type='checkbox'
21
+ value={registryDataset.id}
22
+ checked={registryDataset.isLocallyVisible}
23
+ onChange={onChange}
24
+ />
25
+ <label className='im-c-datasets-layers__item-label govuk-label govuk-checkboxes__label' htmlFor={registryDataset.id}>
26
+ {registryDataset.label}
27
+ </label>
28
+ </div>
29
+ </div>
30
+ )
31
+ }
@@ -0,0 +1,19 @@
1
+ export const LayersMenuGroupWrapper = ({ menuGroup, children }) => {
2
+ if (!menuGroup.groupLabel) {
3
+ return <>{children}</>
4
+ }
5
+ // TODO - is there any need for im-c-datasets-layers-group--items-checked - it isn't used anywhere
6
+ // const anyItemsChecked = false
7
+ // const wrapperClass = `govuk-form-group im-c-datasets-layers-group${anyItemsChecked ? ' im-c-datasets-layers-group--items-checked' : ''}`
8
+ const wrapperClass = 'govuk-form-group im-c-datasets-layers-group'
9
+ return (
10
+ <div key={menuGroup.id} className={wrapperClass}>
11
+ <fieldset className='im-c-datasets-layers-group__fieldset'>
12
+ <legend className='im-c-datasets-layers-group__legend'>
13
+ {menuGroup.groupLabel}
14
+ </legend>
15
+ {children}
16
+ </fieldset>
17
+ </div>
18
+ )
19
+ }
@@ -1,6 +1,6 @@
1
1
  // Components: specific UI components. Using 'CSS in Component' pattern
2
- @use './panels/Key.module';
3
- @use './panels/Layers.module';
2
+ @use './components/Key/Key.module';
3
+ @use './components/LayersMenu/Layers.module';
4
4
 
5
5
  .im-c-datasets {
6
6
  display: block;
@@ -8,14 +8,14 @@ const EVICTION_THRESHOLD = 1.2 // Trigger eviction at 120% of maxFeatures
8
8
  /**
9
9
  * Create a dynamic GeoJSON source that fetches data based on viewport
10
10
  * @param {Object} options
11
- * @param {Object} options.dataset - Dataset configuration
11
+ * @param {Object} options.registryDataset - registryDataset instance
12
12
  * @param {Object} options.map - Map instance
13
13
  * @param {string} options.sourceId - Source ID for the map
14
14
  * @param {Function} options.onUpdate - Callback when source data should be updated
15
15
  * @returns {Object} { destroy, clear, refresh }
16
16
  */
17
- export const createDynamicSource = ({ dataset, map, onUpdate }) => {
18
- const { geojson: baseUrl, idProperty, transformRequest, maxFeatures, minZoom = 0 } = dataset
17
+ export const createDynamicSource = ({ dynamicGeoJSON, map, onUpdate }) => {
18
+ const { url: baseUrl, idProperty, transformRequest, maxFeatures, minZoom = 0 } = dynamicGeoJSON
19
19
 
20
20
  // Feature cache: id → { feature, bbox, lastSeenAt }
21
21
  const features = new Map()
@@ -112,7 +112,7 @@ export const createDynamicSource = ({ dataset, map, onUpdate }) => {
112
112
  currentController = new AbortController()
113
113
 
114
114
  try {
115
- const context = { bbox: currentBbox, zoom, dataset }
115
+ const context = { bbox: currentBbox, zoom }
116
116
  const data = await fetchGeoJSON(baseUrl, context, transformRequest, currentController.signal)
117
117
 
118
118
  const now = Date.now()
@@ -144,12 +144,12 @@ export const createDynamicSource = ({ dataset, map, onUpdate }) => {
144
144
  }
145
145
 
146
146
  // Update map source
147
- onUpdate(dataset.id, toFeatureCollection())
147
+ onUpdate(dynamicGeoJSON.id, toFeatureCollection())
148
148
  } catch (error) {
149
149
  if (error.name === 'AbortError') {
150
150
  return
151
151
  }
152
- console.error(`Failed to fetch dynamic GeoJSON for ${dataset.id}:`, error)
152
+ console.error(`Failed to fetch dynamic GeoJSON for ${dynamicGeoJSON.id}:`, error)
153
153
  }
154
154
  }
155
155
 
@@ -184,7 +184,7 @@ export const createDynamicSource = ({ dataset, map, onUpdate }) => {
184
184
  clear () {
185
185
  features.clear()
186
186
  fetchedBbox = null
187
- onUpdate(dataset.id, { type: 'FeatureCollection', features: [] })
187
+ onUpdate(dynamicGeoJSON.id, { type: 'FeatureCollection', features: [] })
188
188
  },
189
189
 
190
190
  /**
@@ -208,7 +208,7 @@ export const createDynamicSource = ({ dataset, map, onUpdate }) => {
208
208
  */
209
209
  reapply () {
210
210
  if (features.size > 0) {
211
- onUpdate(dataset.id, toFeatureCollection())
211
+ onUpdate(dynamicGeoJSON.id, toFeatureCollection())
212
212
  }
213
213
  }
214
214
  }
@@ -1,13 +1,14 @@
1
1
  // src/plugins/datasets/datasetsInit.jsx
2
2
  import { useEffect, useRef } from 'react'
3
- import { EVENTS } from '../../../../src/config/events.js'
4
- import { datasetDefaults } from './defaults.js'
5
- import { createDatasets } from './datasets.js'
3
+ import { EVENTS } from '../../../../../src/config/events.js'
4
+ import { initialiseDatasets } from './initialiseDatasets.js'
5
+ import { datasetRegistry } from '../registry/datasetRegistry.js'
6
+ import { attachGlobalState } from '../registry/globalDataset.js'
7
+ import { loadLayerAdapter, layerAdapter } from '../adapters/loadLayerAdapter.js'
6
8
 
7
9
  export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, mapProvider, services }) {
8
10
  const { dispatch } = pluginState
9
11
  const { eventBus, symbolRegistry, patternRegistry } = services
10
-
11
12
  const isMapStyleReady = !!mapProvider.map?.getStyle()
12
13
 
13
14
  // Keep a ref to the latest pluginState so event handlers can access current data
@@ -30,28 +31,17 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m
30
31
  return
31
32
  }
32
33
 
33
- // Only initialise state if not already set
34
- if (!pluginState.datasets) {
35
- dispatch({ type: 'SET_DATASETS', payload: { datasets: pluginConfig.datasets, datasetDefaults } })
36
- }
37
-
38
34
  const initDatasets = async () => {
39
- if (!pluginConfig.layerAdapter) {
40
- throw new Error('datasets plugin: no layerAdapter provided. Import and pass maplibreLayerAdapter or a custom adapter.')
41
- }
35
+ const adapter = await loadLayerAdapter(mapProvider, symbolRegistry, patternRegistry)
42
36
 
43
- const { default: LayerAdapter } = await pluginConfig.layerAdapter.load()
44
- const adapter = new LayerAdapter(mapProvider, symbolRegistry, patternRegistry)
45
-
46
- dispatch({ type: 'SET_LAYER_ADAPTER', payload: adapter })
47
-
48
- datasetsInstanceRef.current = createDatasets({
37
+ datasetsInstanceRef.current = initialiseDatasets({
49
38
  adapter,
50
39
  pluginConfig,
51
40
  pluginStateRef,
52
41
  mapStyle: mapState.mapStyle,
53
42
  mapProvider,
54
43
  events: EVENTS,
44
+ dispatch,
55
45
  eventBus
56
46
  })
57
47
  }
@@ -59,9 +49,18 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m
59
49
  initDatasets()
60
50
  }, [isMapStyleReady, appState.mode])
61
51
 
52
+ useEffect(() => datasetRegistry.attach(pluginState.mappedDatasets, pluginState.orderedDatasets),
53
+ [pluginState.mappedDatasets, pluginState.orderedDatasets])
54
+
55
+ useEffect(() => attachGlobalState(pluginState.globals), [pluginState.globals])
56
+
57
+ // Call layerAdapter methods that are queued from state updates
62
58
  useEffect(() => {
63
- dispatch({ type: 'BUILD_KEY_GROUPS', payload: null })
64
- }, [pluginState.datasets])
59
+ const { actionsArray } = pluginState
60
+ if (!actionsArray.length) { return }
61
+ actionsArray.forEach(({ method, parameters }) => layerAdapter[method](...parameters))
62
+ dispatch({ type: 'REMOVE_ADAPTER_ACTIONS', payload: actionsArray })
63
+ }, [pluginState.actionsArray])
65
64
 
66
65
  // Cleanup only on unmount
67
66
  useEffect(() => {
@@ -70,7 +69,6 @@ export function DatasetsInit ({ pluginConfig, pluginState, appState, mapState, m
70
69
  datasetsInstanceRef.current.remove()
71
70
  datasetsInstanceRef.current = null
72
71
  }
73
- dispatch({ type: 'SET_LAYER_ADAPTER', payload: null })
74
72
  }
75
73
  }, [])
76
74
 
@@ -3,7 +3,7 @@ const datasetDefaults = {
3
3
  maxZoom: 24,
4
4
  showInKey: false,
5
5
  showInMenu: false,
6
- visibility: 'visible',
6
+ visible: true,
7
7
  style: {
8
8
  stroke: '#d4351c',
9
9
  strokeWidth: 2,
@@ -48,4 +48,19 @@ const applyDatasetDefaults = (dataset, defaults) => {
48
48
  return { ...topLevelDefaults, ...topLevel, ...mergedStyle }
49
49
  }
50
50
 
51
- export { datasetDefaults, hasCustomVisualStyle, applyDatasetDefaults }
51
+ const applyDatasetDefaultsWithoutFlattening = (dataset) => {
52
+ // Allow for existing configs that use visibility instead of visible, but default to visible if neither is set
53
+ if (dataset.visible !== true && dataset.visible !== false) {
54
+ dataset.visible = dataset.visibility !== 'hidden'
55
+ }
56
+ const datasetWithDefaults = { ...datasetDefaults, ...dataset, style: { ...datasetDefaults.style, ...dataset.style } }
57
+
58
+ const style = dataset.style || {}
59
+ if (!('symbolDescription' in style) && hasCustomVisualStyle(style)) {
60
+ delete datasetWithDefaults.style.symbolDescription
61
+ }
62
+ STYLE_PROPS.forEach(prop => delete datasetWithDefaults[prop])
63
+ return datasetWithDefaults
64
+ }
65
+
66
+ export { datasetDefaults, hasCustomVisualStyle, applyDatasetDefaults, applyDatasetDefaultsWithoutFlattening }
@@ -0,0 +1,127 @@
1
+ import { datasetDefaults, hasCustomVisualStyle, applyDatasetDefaults } from './defaults'
2
+
3
+ describe('datasetDefaults', () => {
4
+ it('has expected top-level defaults', () => {
5
+ expect(datasetDefaults).toMatchObject({
6
+ minZoom: 6,
7
+ maxZoom: 24,
8
+ showInKey: false,
9
+ showInMenu: false,
10
+ visible: true
11
+ })
12
+ })
13
+
14
+ it('has expected style defaults', () => {
15
+ expect(datasetDefaults.style).toMatchObject({
16
+ stroke: '#d4351c',
17
+ strokeWidth: 2,
18
+ fill: 'transparent',
19
+ symbolDescription: 'red outline'
20
+ })
21
+ })
22
+ })
23
+
24
+ describe('hasCustomVisualStyle', () => {
25
+ it('returns true when stroke is present', () => {
26
+ expect(hasCustomVisualStyle({ stroke: '#ff0000' })).toBe(true)
27
+ })
28
+
29
+ it('returns true when fill is present', () => {
30
+ expect(hasCustomVisualStyle({ fill: 'blue' })).toBe(true)
31
+ })
32
+
33
+ it('returns true when fillPattern is present', () => {
34
+ expect(hasCustomVisualStyle({ fillPattern: 'dots' })).toBe(true)
35
+ })
36
+
37
+ it('returns true when fillPatternSvgContent is present', () => {
38
+ expect(hasCustomVisualStyle({ fillPatternSvgContent: '<svg/>' })).toBe(true)
39
+ })
40
+
41
+ it('returns true when symbol is present', () => {
42
+ expect(hasCustomVisualStyle({ symbol: 'marker' })).toBe(true)
43
+ })
44
+
45
+ it('returns true when symbolSvgContent is present', () => {
46
+ expect(hasCustomVisualStyle({ symbolSvgContent: '<svg/>' })).toBe(true)
47
+ })
48
+
49
+ it('returns false when no visual style props are present', () => {
50
+ expect(hasCustomVisualStyle({ strokeWidth: 2, opacity: 0.5 })).toBe(false)
51
+ })
52
+
53
+ it('returns false for an empty style object', () => {
54
+ expect(hasCustomVisualStyle({})).toBe(false)
55
+ })
56
+ })
57
+
58
+ describe('applyDatasetDefaults', () => {
59
+ const defaults = {
60
+ minZoom: 6,
61
+ maxZoom: 24,
62
+ showInKey: false,
63
+ style: {
64
+ stroke: '#d4351c',
65
+ strokeWidth: 2,
66
+ symbolDescription: 'red outline'
67
+ }
68
+ }
69
+
70
+ it('merges top-level dataset properties over defaults', () => {
71
+ const dataset = { id: 'test', minZoom: 10 }
72
+ const result = applyDatasetDefaults(dataset, defaults)
73
+ expect(result.minZoom).toBe(10)
74
+ expect(result.maxZoom).toBe(24)
75
+ expect(result.showInKey).toBe(false)
76
+ })
77
+
78
+ it('flattens style properties into the result', () => {
79
+ const dataset = { id: 'test', style: { fill: 'transparent' } }
80
+ const result = applyDatasetDefaults(dataset, defaults)
81
+ expect(result.fill).toBe('transparent')
82
+ expect(result.stroke).toBe('#d4351c')
83
+ expect(result.style).toBeUndefined()
84
+ })
85
+
86
+ it('dataset style properties override default style properties', () => {
87
+ const dataset = { id: 'test', style: { stroke: '#0000ff' } }
88
+ const result = applyDatasetDefaults(dataset, defaults)
89
+ expect(result.stroke).toBe('#0000ff')
90
+ })
91
+
92
+ it('drops symbolDescription from defaults when custom visual style is present and no explicit symbolDescription', () => {
93
+ const dataset = { id: 'test', style: { stroke: '#0000ff' } }
94
+ const result = applyDatasetDefaults(dataset, defaults)
95
+ expect(result.symbolDescription).toBeUndefined()
96
+ })
97
+
98
+ it('keeps symbolDescription when dataset provides its own, even with custom visual style', () => {
99
+ const dataset = { id: 'test', style: { stroke: '#0000ff', symbolDescription: 'blue outline' } }
100
+ const result = applyDatasetDefaults(dataset, defaults)
101
+ expect(result.symbolDescription).toBe('blue outline')
102
+ })
103
+
104
+ it('keeps symbolDescription from defaults when no custom visual style is present', () => {
105
+ const dataset = { id: 'test', style: { strokeWidth: 4 } }
106
+ const result = applyDatasetDefaults(dataset, defaults)
107
+ expect(result.symbolDescription).toBe('red outline')
108
+ })
109
+
110
+ it('keeps symbolDescription from defaults when dataset has no style', () => {
111
+ const dataset = { id: 'test' }
112
+ const result = applyDatasetDefaults(dataset, defaults)
113
+ expect(result.symbolDescription).toBe('red outline')
114
+ })
115
+
116
+ it('ignores style properties set at top level of dataset', () => {
117
+ const dataset = { id: 'test', stroke: '#ignored' }
118
+ const result = applyDatasetDefaults(dataset, defaults)
119
+ expect(result.stroke).toBe('#d4351c')
120
+ })
121
+
122
+ it('does not include style key in the result', () => {
123
+ const dataset = { id: 'test', style: { fill: 'blue' } }
124
+ const result = applyDatasetDefaults(dataset, defaults)
125
+ expect(result).not.toHaveProperty('style')
126
+ })
127
+ })
@@ -1,71 +1,75 @@
1
- import { createDynamicSource } from './fetch/createDynamicSource.js'
1
+ import { createDynamicSource } from '../fetch/createDynamicSource.js'
2
2
  // NOSONAR: applyDatasetDefaults and datasetDefaults are used in processedDatasets.map
3
3
  import { applyDatasetDefaults, datasetDefaults } from './defaults.js'
4
+ import { mappedDatasetsReducer } from '../reducers/mappedDatasetsReducer.js'
5
+ import { datasetRegistry } from '../registry/datasetRegistry.js'
4
6
 
5
- const isDynamicSource = (dataset) =>
6
- typeof dataset.geojson === 'string' &&
7
- !!dataset.idProperty &&
8
- typeof dataset.transformRequest === 'function'
9
-
10
- export const createDatasets = ({
7
+ export const initialiseDatasets = ({
11
8
  adapter,
12
9
  pluginConfig,
13
10
  pluginStateRef,
14
11
  mapStyle,
15
12
  mapProvider,
16
13
  events,
14
+ dispatch,
17
15
  eventBus
18
16
  }) => {
19
17
  const { datasets } = pluginConfig
20
-
21
18
  const dynamicSources = new Map()
22
19
 
23
- const getDatasets = () => pluginStateRef.current.datasets || datasets
24
- const getHiddenFeatures = () => pluginStateRef.current.hiddenFeatures || {}
20
+ if (pluginConfig.globals) {
21
+ dispatch({ type: 'SET_GLOBAL_STATE', payload: pluginConfig.globals })
22
+ }
25
23
 
26
24
  // Initialise all datasets via the adapter, then set up dynamic sources
27
25
  const processedDatasets = datasets.map(d => applyDatasetDefaults(d, datasetDefaults))
28
- adapter.init(processedDatasets, mapStyle).then(() => {
29
- processedDatasets.forEach(dataset => {
30
- if (!isDynamicSource(dataset)) {
26
+ const { mappedDatasets, orderedDatasets } = mappedDatasetsReducer({ datasets })
27
+ if (adapter.createDataset) {
28
+ datasetRegistry.attachCreateDataset(adapter.createDataset)
29
+ }
30
+ datasetRegistry.attach(mappedDatasets)
31
+ adapter.init(mapStyle).then(() => {
32
+ datasetRegistry.forEachDataset(registryDataset => {
33
+ if (!registryDataset.hasDynamicGeoJSON) {
31
34
  return
32
35
  }
33
-
36
+ const { dynamicGeoJSON } = registryDataset
34
37
  const dynamicSource = createDynamicSource({
35
- dataset,
38
+ dynamicGeoJSON,
36
39
  map: mapProvider.map,
37
40
  onUpdate: (datasetId, geojson) => adapter.setData(datasetId, geojson)
38
41
  })
39
- dynamicSources.set(dataset.id, dynamicSource)
42
+ dynamicSources.set(registryDataset.id, dynamicSource)
40
43
  })
41
-
44
+ // TODO - apply dynamic source defaults here, and include in mappedDatasets
45
+ dispatch({ type: 'SET_DATASETS', payload: { datasets: processedDatasets, mappedDatasets, orderedDatasets } })
42
46
  eventBus.emit('datasets:ready')
43
47
  })
44
48
 
45
49
  let currentMapStyle = mapStyle
46
50
 
47
51
  // Handle basemap style changes — delegate entirely to the adapter
48
- const onSetStyle = (newMapStyle) => {
52
+ const onSetMapStyle = (newMapStyle) => {
49
53
  currentMapStyle = newMapStyle
50
- adapter.onStyleChange(getDatasets(), newMapStyle, getHiddenFeatures(), dynamicSources)
54
+ adapter.onMapStyleChange(newMapStyle, dynamicSources)
51
55
  }
52
56
 
53
- const onSizeChange = () => {
54
- adapter.onSizeChange(getDatasets(), currentMapStyle)
57
+ const onMapSizeChange = () => {
58
+ adapter.onMapSizeChange(currentMapStyle)
55
59
  }
56
60
 
57
- eventBus.on(events.MAP_SET_STYLE, onSetStyle)
58
- eventBus.on(events.MAP_SIZE_CHANGE, onSizeChange)
61
+ eventBus.on(events.MAP_SET_STYLE, onSetMapStyle)
62
+ eventBus.on(events.MAP_SIZE_CHANGE, onMapSizeChange)
59
63
 
60
64
  return {
61
65
  remove () {
62
- eventBus.off(events.MAP_SET_STYLE, onSetStyle)
63
- eventBus.off(events.MAP_SIZE_CHANGE, onSizeChange)
66
+ eventBus.off(events.MAP_SET_STYLE, onSetMapStyle)
67
+ eventBus.off(events.MAP_SIZE_CHANGE, onMapSizeChange)
64
68
 
65
69
  // Clean up dynamic sources
66
70
  dynamicSources.forEach(source => source.destroy())
67
71
  dynamicSources.clear()
68
- adapter.destroy(getDatasets())
72
+ adapter.destroy()
69
73
  },
70
74
 
71
75
  /**