@defra/interactive-map 0.0.29-alpha → 0.0.31-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 (195) 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 +4 -4
  7. package/docs/api.md +111 -37
  8. package/docs/assets/css/docusaurus.scss +20 -4
  9. package/docs/assets/images/basic-map.jpg +0 -0
  10. package/docs/assets/images/button-first.jpg +0 -0
  11. package/docs/assets/images/deuteranopia-ozs-map-thumb.jpg +0 -0
  12. package/docs/assets/images/draw.jpg +0 -0
  13. package/docs/assets/images/marker-panel.jpg +0 -0
  14. package/docs/assets/images/night-ozs-map-thumb.jpg +0 -0
  15. package/docs/assets/images/outdoor-ozs-map-thumb.jpg +0 -0
  16. package/docs/assets/images/search.jpg +0 -0
  17. package/docs/assets/images/select-feature.jpg +0 -0
  18. package/docs/assets/images/style-switcher.jpg +0 -0
  19. package/docs/assets/images/toggle-marker-label.jpg +0 -0
  20. package/docs/assets/images/tritanopia-ozs-map-thumb.jpg +0 -0
  21. package/docs/examples/add-marker-with-panel.mdx +2 -2
  22. package/docs/examples/basic-map.mdx +2 -2
  23. package/docs/examples/draw.mdx +271 -0
  24. package/docs/examples/index.mdx +58 -10
  25. package/docs/examples/search-control.mdx +71 -0
  26. package/docs/examples/select-feature.mdx +146 -0
  27. package/docs/examples/style-switcher.mdx +135 -0
  28. package/docs/examples/toggle-marker-label.mdx +3 -3
  29. package/docs/plugins/datasets.md +15 -28
  30. package/docusaurus.config.cjs +5 -1
  31. package/package.json +1 -1
  32. package/plugins/beta/datasets/dist/css/index.css +4 -0
  33. package/plugins/beta/datasets/dist/esm/im-datasets-ml-adapter.js +1 -0
  34. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  35. package/plugins/beta/datasets/dist/esm/index.js +1 -1
  36. package/plugins/beta/datasets/dist/umd/im-datasets-ml-adapter.js +2 -0
  37. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
  38. package/plugins/beta/datasets/dist/umd/index.js +2 -0
  39. package/plugins/beta/datasets/src/adapters/loadLayerAdapter.js +33 -0
  40. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +44 -120
  41. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +145 -245
  42. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +516 -0
  43. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +191 -0
  44. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.test.js +441 -0
  45. package/plugins/beta/datasets/src/api/addDataset.js +2 -5
  46. package/plugins/beta/datasets/src/api/addDataset.test.js +16 -0
  47. package/plugins/beta/datasets/src/api/getOpacity.js +14 -14
  48. package/plugins/beta/datasets/src/api/getOpacity.test.js +72 -0
  49. package/plugins/beta/datasets/src/api/getStyle.js +8 -9
  50. package/plugins/beta/datasets/src/api/getStyle.test.js +42 -0
  51. package/plugins/beta/datasets/src/api/removeDataset.js +7 -7
  52. package/plugins/beta/datasets/src/api/removeDataset.test.js +45 -0
  53. package/plugins/beta/datasets/src/api/setData.js +9 -3
  54. package/plugins/beta/datasets/src/api/setData.test.js +62 -0
  55. package/plugins/beta/datasets/src/api/setDatasetVisibility.js +5 -30
  56. package/plugins/beta/datasets/src/api/setDatasetVisibility.test.js +31 -0
  57. package/plugins/beta/datasets/src/api/setFeatureVisibility.js +3 -21
  58. package/plugins/beta/datasets/src/api/setFeatureVisibility.test.js +32 -0
  59. package/plugins/beta/datasets/src/api/setGlobals.js +20 -0
  60. package/plugins/beta/datasets/src/api/setGlobals.test.js +63 -0
  61. package/plugins/beta/datasets/src/api/setOpacity.js +8 -27
  62. package/plugins/beta/datasets/src/api/setOpacity.test.js +31 -0
  63. package/plugins/beta/datasets/src/api/setStyle.js +5 -21
  64. package/plugins/beta/datasets/src/api/setStyle.test.js +53 -0
  65. package/plugins/beta/datasets/src/components/Key/Key.jsx +51 -0
  66. package/plugins/beta/datasets/src/components/Key/KeyGroupItem.jsx +18 -0
  67. package/plugins/beta/datasets/src/components/Key/KeyItem.jsx +20 -0
  68. package/plugins/beta/datasets/src/components/Key/KeySvg.jsx +22 -0
  69. package/plugins/beta/datasets/src/components/Key/KeySvg.test.jsx +87 -0
  70. package/plugins/beta/datasets/src/components/Key/KeySvgLine.jsx +19 -0
  71. package/plugins/beta/datasets/src/components/Key/KeySvgLine.test.jsx +71 -0
  72. package/plugins/beta/datasets/src/components/{KeySvgPattern.jsx → Key/KeySvgPattern.jsx} +3 -4
  73. package/plugins/beta/datasets/src/components/Key/KeySvgPattern.test.jsx +55 -0
  74. package/plugins/beta/datasets/src/components/Key/KeySvgRect.jsx +22 -0
  75. package/plugins/beta/datasets/src/components/Key/KeySvgRect.test.jsx +85 -0
  76. package/plugins/beta/datasets/src/components/{KeySvgSymbol.jsx → Key/KeySvgSymbol.jsx} +5 -5
  77. package/plugins/beta/datasets/src/components/Key/KeySvgSymbol.test.jsx +80 -0
  78. package/plugins/beta/datasets/src/components/Key/svgProperties.test.js +61 -0
  79. package/plugins/beta/datasets/src/{panels → components/LayersMenu}/Layers.module.scss +4 -0
  80. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenu.jsx +33 -0
  81. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuCheckbox.jsx +31 -0
  82. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuGroupWrapper.jsx +19 -0
  83. package/plugins/beta/datasets/src/datasets.scss +2 -2
  84. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +8 -8
  85. package/plugins/beta/datasets/src/{DatasetsInit.jsx → initialise/DatasetsInit.jsx} +19 -21
  86. package/plugins/beta/datasets/src/{defaults.js → initialise/defaults.js} +17 -2
  87. package/plugins/beta/datasets/src/initialise/defaults.test.js +127 -0
  88. package/plugins/beta/datasets/src/{datasets.js → initialise/initialiseDatasets.js} +30 -26
  89. package/plugins/beta/datasets/src/initialise/initialiseDatasets.test.js +273 -0
  90. package/plugins/beta/datasets/src/manifest.js +8 -6
  91. package/plugins/beta/datasets/src/reducers/__data__/demoDatasets.js +253 -0
  92. package/plugins/beta/datasets/src/reducers/datasetsToMenu.js +78 -0
  93. package/plugins/beta/datasets/src/reducers/datasetsToMenu.test.js +35 -0
  94. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.js +34 -0
  95. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.test.js +117 -0
  96. package/plugins/beta/datasets/src/reducers/pluginState.js +206 -0
  97. package/plugins/beta/datasets/src/reducers/pluginState.test.js +386 -0
  98. package/plugins/beta/datasets/src/registry/__mocks__/datasetRegistry.js +29 -0
  99. package/plugins/beta/datasets/src/registry/createDataset.js +5 -0
  100. package/plugins/beta/datasets/src/registry/dataset.js +172 -0
  101. package/plugins/beta/datasets/src/registry/dataset.test.js +483 -0
  102. package/plugins/beta/datasets/src/registry/datasetDefinitionCache.js +35 -0
  103. package/plugins/beta/datasets/src/registry/datasetRegistry.js +116 -0
  104. package/plugins/beta/datasets/src/registry/datasetRegistry.test.js +335 -0
  105. package/plugins/beta/datasets/src/registry/dynamicGeoJson.js +17 -0
  106. package/plugins/beta/datasets/src/registry/globalDataset.js +36 -0
  107. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  108. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  109. package/plugins/beta/draw-ml/src/events.js +13 -1
  110. package/plugins/beta/draw-ml/src/index.js +1 -1
  111. package/plugins/beta/draw-ml/src/manifest.js +3 -0
  112. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  113. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  114. package/plugins/interact/src/InteractInit.jsx +2 -2
  115. package/plugins/interact/src/InteractInit.test.js +1 -1
  116. package/plugins/interact/src/events.js +2 -29
  117. package/plugins/interact/src/events.test.js +4 -102
  118. package/plugins/interact/src/hooks/useAttachEvents.js +3 -4
  119. package/plugins/interact/src/hooks/useAttachEvents.test.js +1 -3
  120. package/plugins/interact/src/hooks/useInteractionHandlers.js +10 -1
  121. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +23 -4
  122. package/plugins/interact/src/manifest.js +0 -35
  123. package/plugins/interact/src/manifest.test.js +3 -43
  124. package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
  125. package/providers/beta/esri/src/esriProvider.js +4 -0
  126. package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
  127. package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
  128. package/providers/beta/openlayers/src/openlayersProvider.js +4 -0
  129. package/providers/beta/openlayers/src/openlayersProvider.test.js +5 -0
  130. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  131. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  132. package/providers/maplibre/src/maplibreProvider.js +6 -4
  133. package/providers/maplibre/src/maplibreProvider.test.js +5 -0
  134. package/providers/maplibre/src/utils/highlightFeatures.js +74 -25
  135. package/providers/maplibre/src/utils/highlightFeatures.test.js +132 -0
  136. package/providers/maplibre/src/utils/patternImages.js +2 -4
  137. package/providers/maplibre/src/utils/symbolImages.js +1 -4
  138. package/rollup.esm.mjs +2 -8
  139. package/src/App/components/MapButton/MapButton.jsx +16 -1
  140. package/src/App/components/MapButton/MapButton.module.scss +12 -1
  141. package/src/App/components/MapButton/MapButton.test.jsx +40 -1
  142. package/src/App/components/Tooltip/getTooltipPosition.js +2 -2
  143. package/src/App/hooks/useContinueEnabledEvaluator.js +31 -0
  144. package/src/App/hooks/useContinueEnabledEvaluator.test.js +118 -0
  145. package/src/App/hooks/useEvaluateProp.js +15 -15
  146. package/src/App/hooks/useEvaluateProp.test.js +80 -76
  147. package/src/App/hooks/useLayoutMeasurements.js +1 -3
  148. package/src/App/hooks/useResizeObserver.js +12 -1
  149. package/src/App/hooks/useResizeObserver.test.js +1 -0
  150. package/src/App/renderer/PluginInits.jsx +2 -0
  151. package/src/App/renderer/mapButtons.js +21 -1
  152. package/src/App/renderer/mapButtons.test.js +46 -1
  153. package/src/App/store/appReducer.js +1 -1
  154. package/src/App/store/appReducer.test.js +37 -0
  155. package/src/InteractiveMap/InteractiveMap.js +10 -0
  156. package/src/InteractiveMap/InteractiveMap.test.js +8 -0
  157. package/src/InteractiveMap/polyfills.js +37 -6
  158. package/src/InteractiveMap/polyfills.test.js +132 -101
  159. package/src/config/appConfig.js +25 -0
  160. package/src/config/appConfig.test.js +50 -0
  161. package/src/config/defaults.js +1 -0
  162. package/src/scss/settings/_dimensions.scss +1 -0
  163. package/src/services/patternRegistry.js +8 -4
  164. package/src/types.js +7 -0
  165. package/src/utils/patternUtils.js +1 -2
  166. package/src/utils/patternUtils.test.js +9 -0
  167. package/webpack.dev.mjs +10 -0
  168. package/webpack.umd.mjs +1 -1
  169. package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +0 -1
  170. package/plugins/beta/datasets/dist/adapters/maplibre/esm/index.js +0 -1
  171. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +0 -2
  172. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +0 -2
  173. package/plugins/beta/datasets/dist/umd/maplibre/index.js +0 -2
  174. package/plugins/beta/datasets/src/adapters/maplibre/index.js +0 -18
  175. package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +0 -75
  176. package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +0 -27
  177. package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +0 -31
  178. package/plugins/beta/datasets/src/components/KeySvg.jsx +0 -24
  179. package/plugins/beta/datasets/src/components/KeySvgLine.jsx +0 -19
  180. package/plugins/beta/datasets/src/components/KeySvgRect.jsx +0 -22
  181. package/plugins/beta/datasets/src/index.maplibre.umd.js +0 -23
  182. package/plugins/beta/datasets/src/panels/Key.jsx +0 -62
  183. package/plugins/beta/datasets/src/panels/Layers.jsx +0 -141
  184. package/plugins/beta/datasets/src/reducer.js +0 -219
  185. package/plugins/beta/datasets/src/reducers/keyReducer.js +0 -34
  186. package/plugins/beta/datasets/src/utils/filters.js +0 -34
  187. package/plugins/beta/datasets/src/utils/mergeSublayer.js +0 -86
  188. package/plugins/beta/draw-ml/dist/css/index.css +0 -21
  189. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-maplibre-adapter.js.LICENSE.txt → im-datasets-ml-adapter.js.LICENSE.txt} +0 -0
  190. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-plugin.js.LICENSE.txt → im-datasets-plugin.js.LICENSE.txt} +0 -0
  191. /package/plugins/beta/datasets/dist/umd/{maplibre/index.js.LICENSE.txt → index.js.LICENSE.txt} +0 -0
  192. /package/plugins/beta/datasets/src/components/{EmptyKey.jsx → Key/EmptyKey.jsx} +0 -0
  193. /package/plugins/beta/datasets/src/components/{EmptyKey.test.jsx → Key/EmptyKey.test.jsx} +0 -0
  194. /package/plugins/beta/datasets/src/{panels → components/Key}/Key.module.scss +0 -0
  195. /package/plugins/beta/datasets/src/components/{svgProperties.js → Key/svgProperties.js} +0 -0
@@ -0,0 +1,271 @@
1
+ import DemoMapDraw from '../../demo/DemoMapDraw.js'
2
+ import CodeTabs from '../../demo/js/codeTabs.js'
3
+
4
+ # Draw tools
5
+
6
+ Draw, edit, select and delete polygons and lines using the draw and interact plugins. A single "Draw tools" menu button groups all drawing actions together. The menu items update their enabled state based on whether a drawn feature is selected.
7
+
8
+ :::note
9
+ The draw plugin (`draw-ml`) is currently in beta.
10
+ :::
11
+
12
+ <DemoMapDraw />
13
+
14
+ <CodeTabs tabs={[
15
+ {
16
+ label: 'ESM',
17
+ language: 'js',
18
+ code: `
19
+ import InteractiveMap from '@defra/interactive-map'
20
+ import maplibreProvider from '@defra/interactive-map/providers/maplibre'
21
+ import createInteractPlugin from '@defra/interactive-map/plugins/interact'
22
+ import createDrawPlugin from '@defra/interactive-map/plugins/draw-ml'
23
+
24
+ const DRAW_LAYERS = ['fill-inactive.cold', 'stroke-inactive.cold']
25
+
26
+ const interactPlugin = createInteractPlugin({
27
+ layers: [
28
+ { layerId: 'fill-inactive.cold', idProperty: 'id' },
29
+ { layerId: 'stroke-inactive.cold', idProperty: 'id' }
30
+ ],
31
+ interactionModes: ['selectFeature'],
32
+ multiSelect: true,
33
+ deselectOnClickOutside: true
34
+ })
35
+
36
+ const drawPlugin = createDrawPlugin()
37
+
38
+ const interactiveMap = new InteractiveMap('my-map', {
39
+ behaviour: 'inline',
40
+ mapProvider: maplibreProvider(),
41
+ mapStyle: {
42
+ url: 'https://your-tile-url/style.json',
43
+ attribution: 'Your tile attribution'
44
+ },
45
+ center: [-0.1276, 51.5074],
46
+ zoom: 12,
47
+ containerHeight: '516px',
48
+ plugins: [interactPlugin, drawPlugin]
49
+ })
50
+
51
+ let selectedFeatureIds = []
52
+
53
+ interactiveMap.on('map:ready', () => {
54
+ interactPlugin.enable()
55
+
56
+ interactiveMap.addButton('drawTools', {
57
+ label: 'Draw tools',
58
+ mobile: { slot: 'bottom-right' },
59
+ tablet: { slot: 'top-middle' },
60
+ desktop: { slot: 'top-middle' },
61
+ menuItems: [
62
+ {
63
+ id: 'drawPolygon',
64
+ label: 'Draw polygon',
65
+ iconSvgContent: '<path d="M19.5 7v10M4.5 7v10M7 19.5h10M7 4.5h10"/><path d="M22 18v3a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm0-15v3a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zM7 18v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zM7 3v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1z"/>',
66
+ onClick: () => {
67
+ interactiveMap.toggleButtonState('drawTools', 'hidden', true)
68
+ drawPlugin.newPolygon(crypto.randomUUID())
69
+ }
70
+ },
71
+ {
72
+ id: 'drawLine',
73
+ label: 'Draw line',
74
+ iconSvgContent: '<path d="M5.706 16.294L16.294 5.706"/><path d="M21 2v3c0 .549-.451 1-1 1h-3c-.549 0-1-.451-1-1V2c0-.549.451-1 1-1h3c.549 0 1 .451 1 1zM6 17v3c0 .549-.451 1-1 1H2c-.549 0-1-.451-1-1v-3c0-.549.451-1 1-1h3c.549 0 1 .451 1 1z"/>',
75
+ onClick: () => {
76
+ interactiveMap.toggleButtonState('drawTools', 'hidden', true)
77
+ drawPlugin.newLine(crypto.randomUUID())
78
+ }
79
+ },
80
+ {
81
+ id: 'editFeature',
82
+ label: 'Edit feature',
83
+ iconSvgContent: '<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/>',
84
+ isDisabled: true,
85
+ onClick: () => {
86
+ if (!drawPlugin.editFeature(selectedFeatureIds[0])) return
87
+ interactiveMap.toggleButtonState('drawTools', 'hidden', true)
88
+ interactPlugin.disable()
89
+ }
90
+ },
91
+ {
92
+ id: 'deleteFeature',
93
+ label: 'Delete feature',
94
+ iconSvgContent: '<path d="M10 11v6"/><path d="M14 11v6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>',
95
+ isDisabled: true,
96
+ onClick: () => {
97
+ drawPlugin.deleteFeature(selectedFeatureIds)
98
+ interactPlugin.clear()
99
+ interactiveMap.toggleButtonState('drawTools', 'hidden', false)
100
+ interactiveMap.toggleButtonState('drawPolygon', 'disabled', false)
101
+ interactiveMap.toggleButtonState('drawLine', 'disabled', false)
102
+ interactiveMap.toggleButtonState('editFeature', 'disabled', true)
103
+ interactiveMap.toggleButtonState('deleteFeature', 'disabled', true)
104
+ }
105
+ }
106
+ ]
107
+ })
108
+ })
109
+
110
+ interactiveMap.on('draw:started', () => {
111
+ interactPlugin.disable()
112
+ })
113
+
114
+ interactiveMap.on('draw:created', () => {
115
+ interactiveMap.toggleButtonState('drawTools', 'hidden', false)
116
+ interactPlugin.enable()
117
+ })
118
+
119
+ interactiveMap.on('draw:edited', () => {
120
+ interactiveMap.toggleButtonState('drawTools', 'hidden', false)
121
+ interactPlugin.enable()
122
+ })
123
+
124
+ interactiveMap.on('draw:cancelled', () => {
125
+ interactiveMap.toggleButtonState('drawTools', 'hidden', false)
126
+ interactPlugin.enable()
127
+ })
128
+
129
+ interactiveMap.on('interact:selectionchange', (e) => {
130
+ const singleFeature = e.selectedFeatures.length === 1
131
+ const anyFeature = e.selectedFeatures.length > 0
132
+ const isDrawFeature = singleFeature && DRAW_LAYERS.includes(e.selectedFeatures[0].layerId)
133
+ const allDrawFeatures = anyFeature && e.selectedFeatures.every(f => DRAW_LAYERS.includes(f.layerId))
134
+ selectedFeatureIds = e.selectedFeatures.map(f => f.featureId)
135
+ interactiveMap.toggleButtonState('drawPolygon', 'disabled', singleFeature)
136
+ interactiveMap.toggleButtonState('drawLine', 'disabled', singleFeature)
137
+ interactiveMap.toggleButtonState('editFeature', 'disabled', !isDrawFeature)
138
+ interactiveMap.toggleButtonState('deleteFeature', 'disabled', !allDrawFeatures)
139
+ })
140
+ `
141
+ },
142
+ {
143
+ label: 'UMD',
144
+ language: 'html',
145
+ code: `
146
+ <script src="/your-assets-path/interactive-map/index.js"></script>
147
+ <script src="/your-assets-path/maplibre-provider/index.js"></script>
148
+ <script src="/your-assets-path/interact-plugin/index.js"></script>
149
+ <script src="/your-assets-path/draw-ml-plugin/index.js"></script>
150
+
151
+ <script>
152
+ const DRAW_LAYERS = ['fill-inactive.cold', 'stroke-inactive.cold']
153
+
154
+ const interactPlugin = defra.interactPlugin({
155
+ layers: [
156
+ { layerId: 'fill-inactive.cold', idProperty: 'id' },
157
+ { layerId: 'stroke-inactive.cold', idProperty: 'id' }
158
+ ],
159
+ interactionModes: ['selectFeature'],
160
+ multiSelect: true,
161
+ deselectOnClickOutside: true
162
+ })
163
+
164
+ const drawPlugin = defra.drawMLPlugin()
165
+
166
+ const interactiveMap = new defra.InteractiveMap('my-map', {
167
+ behaviour: 'inline',
168
+ mapProvider: defra.maplibreProvider(),
169
+ mapStyle: {
170
+ url: 'https://your-tile-url/style.json',
171
+ attribution: 'Your tile attribution'
172
+ },
173
+ center: [-0.1276, 51.5074],
174
+ zoom: 12,
175
+ containerHeight: '516px',
176
+ plugins: [interactPlugin, drawPlugin]
177
+ })
178
+
179
+ let selectedFeatureIds = []
180
+
181
+ interactiveMap.on('map:ready', function () {
182
+ interactPlugin.enable()
183
+
184
+ interactiveMap.addButton('drawTools', {
185
+ label: 'Draw tools',
186
+ mobile: { slot: 'bottom-right' },
187
+ tablet: { slot: 'top-middle' },
188
+ desktop: { slot: 'top-middle' },
189
+ menuItems: [
190
+ {
191
+ id: 'drawPolygon',
192
+ label: 'Draw polygon',
193
+ iconSvgContent: '<path d="M19.5 7v10M4.5 7v10M7 19.5h10M7 4.5h10"/><path d="M22 18v3a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm0-15v3a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zM7 18v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zM7 3v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1z"/>',
194
+ onClick: function () {
195
+ interactiveMap.toggleButtonState('drawTools', 'hidden', true)
196
+ drawPlugin.newPolygon(crypto.randomUUID())
197
+ }
198
+ },
199
+ {
200
+ id: 'drawLine',
201
+ label: 'Draw line',
202
+ iconSvgContent: '<path d="M5.706 16.294L16.294 5.706"/><path d="M21 2v3c0 .549-.451 1-1 1h-3c-.549 0-1-.451-1-1V2c0-.549.451-1 1-1h3c.549 0 1 .451 1 1zM6 17v3c0 .549-.451 1-1 1H2c-.549 0-1-.451-1-1v-3c0-.549.451-1 1-1h3c.549 0 1 .451 1 1z"/>',
203
+ onClick: function () {
204
+ interactiveMap.toggleButtonState('drawTools', 'hidden', true)
205
+ drawPlugin.newLine(crypto.randomUUID())
206
+ }
207
+ },
208
+ {
209
+ id: 'editFeature',
210
+ label: 'Edit feature',
211
+ iconSvgContent: '<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/>',
212
+ isDisabled: true,
213
+ onClick: function () {
214
+ if (!drawPlugin.editFeature(selectedFeatureIds[0])) return
215
+ interactiveMap.toggleButtonState('drawTools', 'hidden', true)
216
+ interactPlugin.disable()
217
+ }
218
+ },
219
+ {
220
+ id: 'deleteFeature',
221
+ label: 'Delete feature',
222
+ iconSvgContent: '<path d="M10 11v6"/><path d="M14 11v6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>',
223
+ isDisabled: true,
224
+ onClick: function () {
225
+ drawPlugin.deleteFeature(selectedFeatureIds)
226
+ interactPlugin.clear()
227
+ interactiveMap.toggleButtonState('drawTools', 'hidden', false)
228
+ interactiveMap.toggleButtonState('drawPolygon', 'disabled', false)
229
+ interactiveMap.toggleButtonState('drawLine', 'disabled', false)
230
+ interactiveMap.toggleButtonState('editFeature', 'disabled', true)
231
+ interactiveMap.toggleButtonState('deleteFeature', 'disabled', true)
232
+ }
233
+ }
234
+ ]
235
+ })
236
+ })
237
+
238
+ interactiveMap.on('draw:started', function () {
239
+ interactPlugin.disable()
240
+ })
241
+
242
+ interactiveMap.on('draw:created', function () {
243
+ interactiveMap.toggleButtonState('drawTools', 'hidden', false)
244
+ interactPlugin.enable()
245
+ })
246
+
247
+ interactiveMap.on('draw:edited', function () {
248
+ interactiveMap.toggleButtonState('drawTools', 'hidden', false)
249
+ interactPlugin.enable()
250
+ })
251
+
252
+ interactiveMap.on('draw:cancelled', function () {
253
+ interactiveMap.toggleButtonState('drawTools', 'hidden', false)
254
+ interactPlugin.enable()
255
+ })
256
+
257
+ interactiveMap.on('interact:selectionchange', function (e) {
258
+ var singleFeature = e.selectedFeatures.length === 1
259
+ var anyFeature = e.selectedFeatures.length > 0
260
+ var isDrawFeature = singleFeature && DRAW_LAYERS.includes(e.selectedFeatures[0].layerId)
261
+ var allDrawFeatures = anyFeature && e.selectedFeatures.every(function (f) { return DRAW_LAYERS.includes(f.layerId) })
262
+ selectedFeatureIds = e.selectedFeatures.map(function (f) { return f.featureId })
263
+ interactiveMap.toggleButtonState('drawPolygon', 'disabled', singleFeature)
264
+ interactiveMap.toggleButtonState('drawLine', 'disabled', singleFeature)
265
+ interactiveMap.toggleButtonState('editFeature', 'disabled', !isDrawFeature)
266
+ interactiveMap.toggleButtonState('deleteFeature', 'disabled', !allDrawFeatures)
267
+ })
268
+ </script>
269
+ `
270
+ }
271
+ ]} />
@@ -3,51 +3,99 @@ import basicMapImg from '../assets/images/basic-map.jpg'
3
3
  import buttonFirstImg from '../assets/images/button-first.jpg'
4
4
  import markerPanelImg from '../assets/images/marker-panel.jpg'
5
5
  import toggleMarkerLabelImg from '../assets/images/toggle-marker-label.jpg'
6
+ import searchImg from '../assets/images/search.jpg'
7
+ import selectFeatureImg from '../assets/images/select-feature.jpg'
8
+ import styleSwitcherImg from '../assets/images/style-switcher.jpg'
9
+ import drawImg from '../assets/images/draw.jpg'
6
10
 
7
11
  export function ExampleCards() {
8
12
  const basicHref = useBaseUrl('/examples/basic-map')
9
13
  const buttonHref = useBaseUrl('/examples/button-map')
10
14
  const interactHref = useBaseUrl('/examples/add-marker-with-panel')
11
15
  const toggleMarkerLabelHref = useBaseUrl('/examples/toggle-marker-label')
16
+ const searchControlHref = useBaseUrl('/examples/search-control')
17
+ const selectFeatureHref = useBaseUrl('/examples/select-feature')
18
+ const styleSwitcherHref = useBaseUrl('/examples/style-switcher')
19
+ const drawHref = useBaseUrl('/examples/draw')
12
20
 
13
21
  return (
14
- <div className='govuk-grid-row'>
15
- <div className='govuk-grid-column-one-half'>
22
+ <div className='govuk-grid-row app-example-cards'>
23
+ <div className='govuk-grid-column-one-third'>
16
24
  <div className='app-example-card'>
17
25
  <img src={basicMapImg} alt='' />
18
26
  <div className='app-example-card__body'>
19
- <h2 className='govuk-heading-m'>
27
+ <h2 className='govuk-heading-s'>
20
28
  <a href={basicHref}>Basic map</a>
21
29
  </h2>
22
30
  </div>
23
31
  </div>
24
32
  </div>
25
- <div className='govuk-grid-column-one-half'>
33
+ <div className='govuk-grid-column-one-third'>
26
34
  <div className='app-example-card'>
27
35
  <img src={buttonFirstImg} alt='' />
28
36
  <div className='app-example-card__body'>
29
- <h2 className='govuk-heading-m'>
37
+ <h2 className='govuk-heading-s'>
30
38
  <a href={buttonHref}>Button-triggered map</a>
31
39
  </h2>
32
40
  </div>
33
41
  </div>
34
42
  </div>
35
- <div className='govuk-grid-column-one-half'>
43
+ <div className='govuk-grid-column-one-third'>
36
44
  <div className='app-example-card'>
37
45
  <img src={markerPanelImg} alt='' />
38
46
  <div className='app-example-card__body'>
39
- <h2 className='govuk-heading-m'>
47
+ <h2 className='govuk-heading-s'>
40
48
  <a href={interactHref}>Add a marker with a panel</a>
41
49
  </h2>
42
50
  </div>
43
51
  </div>
44
52
  </div>
45
- <div className='govuk-grid-column-one-half'>
53
+ <div className='govuk-grid-column-one-third'>
46
54
  <div className='app-example-card'>
47
55
  <img src={toggleMarkerLabelImg} alt='' />
48
56
  <div className='app-example-card__body'>
49
- <h2 className='govuk-heading-m'>
50
- <a href={toggleMarkerLabelHref}>Toggle marker label on click</a>
57
+ <h2 className='govuk-heading-s'>
58
+ <a href={toggleMarkerLabelHref}>Toggle marker label</a>
59
+ </h2>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ <div className='govuk-grid-column-one-third'>
64
+ <div className='app-example-card'>
65
+ <img src={searchImg} alt='' />
66
+ <div className='app-example-card__body'>
67
+ <h2 className='govuk-heading-s'>
68
+ <a href={searchControlHref}>Add a search control</a>
69
+ </h2>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ <div className='govuk-grid-column-one-third'>
74
+ <div className='app-example-card'>
75
+ <img src={selectFeatureImg} alt='' />
76
+ <div className='app-example-card__body'>
77
+ <h2 className='govuk-heading-s'>
78
+ <a href={selectFeatureHref}>Select a feature</a>
79
+ </h2>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ <div className='govuk-grid-column-one-third'>
84
+ <div className='app-example-card'>
85
+ <img src={styleSwitcherImg} alt='' />
86
+ <div className='app-example-card__body'>
87
+ <h2 className='govuk-heading-s'>
88
+ <a href={styleSwitcherHref}>Change map style</a>
89
+ </h2>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ <div className='govuk-grid-column-one-third'>
94
+ <div className='app-example-card'>
95
+ <img src={drawImg} alt='' />
96
+ <div className='app-example-card__body'>
97
+ <h2 className='govuk-heading-s'>
98
+ <a href={drawHref}>Draw tools</a>
51
99
  </h2>
52
100
  </div>
53
101
  </div>
@@ -0,0 +1,71 @@
1
+ import DemoMapSearchNominatim from '../../demo/DemoMapSearchNominatim.js'
2
+ import CodeTabs from '../../demo/js/codeTabs.js'
3
+
4
+ # Add a search control
5
+
6
+ Add a search control using the built-in OS Names API integration.
7
+
8
+ :::note
9
+ The live demo below uses Nominatim (no API key required) — the code example uses OS Names, so results may differ slightly. Embedding the key in the URL is the simplest approach for prototyping; for production, `transformRequest` can be used to inject auth headers, or you may need to route requests through your own server-side proxy.
10
+ :::
11
+
12
+ <DemoMapSearchNominatim />
13
+
14
+ <CodeTabs tabs={[
15
+ {
16
+ label: 'ESM',
17
+ language: 'js',
18
+ code: `
19
+ import InteractiveMap from '@defra/interactive-map'
20
+ import maplibreProvider from '@defra/interactive-map/providers/maplibre'
21
+ import createSearchPlugin from '@defra/interactive-map/plugins/search'
22
+
23
+ const searchPlugin = createSearchPlugin({
24
+ osNamesURL: 'https://api.os.uk/search/names/v1/find?query={query}&key=YOUR_API_KEY',
25
+ width: '300px'
26
+ })
27
+
28
+ new InteractiveMap('my-map', {
29
+ behaviour: 'inline',
30
+ mapProvider: maplibreProvider(),
31
+ mapStyle: {
32
+ url: 'https://your-tile-url/style.json',
33
+ attribution: 'Your tile attribution'
34
+ },
35
+ center: [-1.6, 53.1],
36
+ zoom: 6,
37
+ containerHeight: '516px',
38
+ plugins: [searchPlugin]
39
+ })
40
+ `
41
+ },
42
+ {
43
+ label: 'UMD',
44
+ language: 'html',
45
+ code: `
46
+ <script src="/your-assets-path/interactive-map/index.js"></script>
47
+ <script src="/your-assets-path/maplibre-provider/index.js"></script>
48
+ <script src="/your-assets-path/search-plugin/index.js"></script>
49
+
50
+ <script>
51
+ const searchPlugin = defra.searchPlugin({
52
+ osNamesURL: 'https://api.os.uk/search/names/v1/find?query={query}&key=YOUR_API_KEY',
53
+ width: '300px'
54
+ })
55
+
56
+ new defra.InteractiveMap('my-map', {
57
+ behaviour: 'inline',
58
+ mapProvider: defra.maplibreProvider(),
59
+ mapStyle: {
60
+ url: 'https://your-tile-url/style.json',
61
+ attribution: 'Your tile attribution'
62
+ },
63
+ center: [-1.6, 53.1],
64
+ zoom: 6,
65
+ containerHeight: '516px',
66
+ plugins: [searchPlugin]
67
+ })
68
+ </script>
69
+ `
70
+ }
71
+ ]} />
@@ -0,0 +1,146 @@
1
+ import DemoMapSelectBuilding from '../../demo/DemoMapSelectBuilding.js'
2
+ import CodeTabs from '../../demo/js/codeTabs.js'
3
+
4
+ # Select a feature
5
+
6
+ Use the interact plugin to add feature selection to your map. This example adds the ability to select a building from the OS Open Zoomstack `Buildings 3D` layer and display its properties in a panel. Selecting another building replaces the current selection; clicking outside clears it.
7
+
8
+ :::note
9
+ The layer ID used here (`buildings 3D`) is specific to the OS Open Zoomstack outdoor style. For other tile styles, set `debug: true` in the plugin options, open the browser console, and click the map to see which layer IDs are available at that point.
10
+ :::
11
+
12
+ <DemoMapSelectBuilding />
13
+
14
+ <CodeTabs tabs={[
15
+ {
16
+ label: 'ESM',
17
+ language: 'js',
18
+ code: `
19
+ import InteractiveMap from '@defra/interactive-map'
20
+ import maplibreProvider from '@defra/interactive-map/providers/maplibre'
21
+ import createInteractPlugin from '@defra/interactive-map/plugins/interact'
22
+
23
+ const PANEL_ID = 'building-info'
24
+
25
+ const interactPlugin = createInteractPlugin({
26
+ interactionModes: ['selectFeature'],
27
+ deselectOnClickOutside: true,
28
+ layers: [
29
+ { layerId: 'buildings 3D', idProperty: 'uuid' }
30
+ ]
31
+ })
32
+
33
+ const map = new InteractiveMap('my-map', {
34
+ behaviour: 'inline',
35
+ mapProvider: maplibreProvider(),
36
+ mapStyle: {
37
+ url: 'https://your-tile-url/style.json',
38
+ attribution: 'Your tile attribution'
39
+ },
40
+ center: [-1.55, 53.80],
41
+ zoom: 14.5,
42
+ maxZoom: 14.5,
43
+ containerHeight: '516px',
44
+ plugins: [interactPlugin]
45
+ })
46
+
47
+ map.on('map:ready', () => {
48
+ interactPlugin.enable()
49
+
50
+ map.addPanel(PANEL_ID, {
51
+ focus: false,
52
+ label: 'Selected building',
53
+ html: '<div id="building-info-content"></div>',
54
+ mobile: { slot: 'drawer', dismissible: true },
55
+ tablet: { slot: 'left-top', dismissible: true, width: '300px' },
56
+ desktop: { slot: 'left-top', dismissible: true, width: '300px' }
57
+ })
58
+ })
59
+
60
+ map.on('interact:selectionchange', ({ selectedFeatures }) => {
61
+ if (selectedFeatures.length > 0) {
62
+ let html = ''
63
+ for (const [k, v] of Object.entries(selectedFeatures[0].properties)) {
64
+ html += '<p class="govuk-body govuk-!-margin-bottom-1"><strong>' + k + ':</strong> ' + v + '</p>'
65
+ }
66
+ document.getElementById('building-info-content').innerHTML = html
67
+ map.showPanel(PANEL_ID)
68
+ } else {
69
+ map.hidePanel(PANEL_ID)
70
+ }
71
+ })
72
+
73
+ map.on('app:panelclosed', ({ panelId }) => {
74
+ if (panelId === PANEL_ID) {
75
+ interactPlugin.clear()
76
+ }
77
+ })
78
+ `
79
+ },
80
+ {
81
+ label: 'UMD',
82
+ language: 'html',
83
+ code: `
84
+ <script src="/your-assets-path/interactive-map/index.js"></script>
85
+ <script src="/your-assets-path/maplibre-provider/index.js"></script>
86
+ <script src="/your-assets-path/interact-plugin/index.js"></script>
87
+
88
+ <script>
89
+ const PANEL_ID = 'building-info'
90
+
91
+ const interactPlugin = defra.interactPlugin({
92
+ interactionModes: ['selectFeature'],
93
+ deselectOnClickOutside: true,
94
+ layers: [
95
+ { layerId: 'buildings 3D', idProperty: 'uuid' }
96
+ ]
97
+ })
98
+
99
+ const map = new defra.InteractiveMap('my-map', {
100
+ behaviour: 'inline',
101
+ mapProvider: defra.maplibreProvider(),
102
+ mapStyle: {
103
+ url: 'https://your-tile-url/style.json',
104
+ attribution: 'Your tile attribution'
105
+ },
106
+ center: [-1.55, 53.80],
107
+ zoom: 17,
108
+ containerHeight: '516px',
109
+ plugins: [interactPlugin]
110
+ })
111
+
112
+ map.on('map:ready', () => {
113
+ interactPlugin.enable()
114
+
115
+ map.addPanel(PANEL_ID, {
116
+ focus: false,
117
+ label: 'Selected building',
118
+ html: '<div id="building-info-content"></div>',
119
+ mobile: { slot: 'drawer', dismissible: true },
120
+ tablet: { slot: 'left-top', dismissible: true, width: '300px' },
121
+ desktop: { slot: 'left-top', dismissible: true, width: '300px' }
122
+ })
123
+ })
124
+
125
+ map.on('interact:selectionchange', ({ selectedFeatures }) => {
126
+ if (selectedFeatures.length > 0) {
127
+ let html = ''
128
+ for (const [k, v] of Object.entries(selectedFeatures[0].properties)) {
129
+ html += '<p class="govuk-body govuk-!-margin-bottom-1"><strong>' + k + ':</strong> ' + v + '</p>'
130
+ }
131
+ document.getElementById('building-info-content').innerHTML = html
132
+ map.showPanel(PANEL_ID)
133
+ } else {
134
+ map.hidePanel(PANEL_ID)
135
+ }
136
+ })
137
+
138
+ map.on('app:panelclosed', ({ panelId }) => {
139
+ if (panelId === PANEL_ID) {
140
+ interactPlugin.clear()
141
+ }
142
+ })
143
+ </script>
144
+ `
145
+ }
146
+ ]} />