@defra/interactive-map 0.0.3-alpha → 0.0.4-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 (268) hide show
  1. package/README.md +16 -57
  2. package/dist/esm/im-core.js +1 -1
  3. package/dist/esm/index.js +1 -1
  4. package/dist/umd/im-core.js +1 -1
  5. package/dist/umd/index.js +1 -1
  6. package/docs/api/button-definition.md +176 -0
  7. package/docs/api/context.md +124 -0
  8. package/docs/api/control-definition.md +72 -0
  9. package/docs/api/icon-definition.md +28 -0
  10. package/docs/api/map-style-config.md +76 -0
  11. package/docs/api/marker-config.md +51 -0
  12. package/docs/api/panel-definition.md +117 -0
  13. package/docs/api/slots.md +25 -0
  14. package/docs/api.md +713 -0
  15. package/docs/architecture.md +139 -0
  16. package/docs/building-a-plugin.md +149 -0
  17. package/docs/getting-started.md +46 -0
  18. package/docs/govuk-prototype.md +0 -0
  19. package/docs/plugins/interact.md +205 -0
  20. package/docs/plugins/map-styles.md +84 -0
  21. package/docs/plugins/plugin-context.md +67 -0
  22. package/docs/plugins/plugin-descriptor.md +71 -0
  23. package/docs/plugins/plugin-manifest.md +118 -0
  24. package/docs/plugins/scale-bar.md +62 -0
  25. package/docs/plugins/search.md +162 -0
  26. package/docs/plugins.md +116 -0
  27. package/govuk-prototype-kit.config.json +7 -7
  28. package/package.json +14 -1
  29. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -0
  30. package/plugins/beta/datasets/dist/esm/index.js +2 -0
  31. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -0
  32. package/plugins/beta/datasets/dist/umd/index.js +2 -0
  33. package/plugins/{datasets → beta/datasets}/src/manifest.js +1 -1
  34. package/plugins/{datasets → beta/datasets}/src/mapLayers.js +1 -1
  35. package/plugins/{datasets → beta/datasets}/src/panels/Key.jsx +1 -1
  36. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -0
  37. package/plugins/beta/draw-ml/dist/esm/index.js +2 -0
  38. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -0
  39. package/plugins/beta/draw-ml/dist/umd/index.js +2 -0
  40. package/plugins/{draw-ml → beta/draw-ml}/src/api/editFeature.js +1 -1
  41. package/plugins/beta/draw-ml/src/api/merge.js +9 -0
  42. package/plugins/beta/draw-ml/src/api/newLine.js +43 -0
  43. package/plugins/{draw-ml → beta/draw-ml}/src/api/newPolygon.js +5 -5
  44. package/plugins/beta/draw-ml/src/api/split.js +81 -0
  45. package/plugins/{draw-ml → beta/draw-ml}/src/defaults.js +2 -0
  46. package/plugins/{draw-ml → beta/draw-ml}/src/events.js +105 -3
  47. package/plugins/{draw-ml → beta/draw-ml}/src/manifest.js +17 -11
  48. package/plugins/{draw-ml → beta/draw-ml}/src/mapboxDraw.js +51 -2
  49. package/plugins/{draw-ml → beta/draw-ml}/src/mapboxSnap.js +2 -0
  50. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +496 -0
  51. package/plugins/beta/draw-ml/src/modes/drawLineMode.js +18 -0
  52. package/plugins/beta/draw-ml/src/modes/drawPolygonMode.js +16 -0
  53. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +792 -0
  54. package/plugins/{draw-ml → beta/draw-ml}/src/reducer.js +30 -2
  55. package/plugins/{draw-ml → beta/draw-ml}/src/styles.js +66 -36
  56. package/plugins/beta/draw-ml/src/undoStack.js +50 -0
  57. package/plugins/beta/draw-ml/src/utils/debounce.js +16 -0
  58. package/plugins/beta/draw-ml/src/utils/spatial.js +258 -0
  59. package/plugins/beta/frame/dist/esm/im-frame-plugin.js +1 -0
  60. package/plugins/beta/frame/dist/esm/index.js +2 -0
  61. package/plugins/beta/frame/dist/umd/im-frame-plugin.js +1 -0
  62. package/plugins/beta/frame/dist/umd/index.js +2 -0
  63. package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -0
  64. package/plugins/beta/map-styles/dist/esm/index.js +2 -0
  65. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -0
  66. package/plugins/beta/map-styles/dist/umd/index.js +2 -0
  67. package/plugins/{map-styles → beta/map-styles}/src/MapStyles.jsx +1 -1
  68. package/plugins/{map-styles → beta/map-styles}/src/manifest.js +8 -10
  69. package/plugins/beta/scale-bar/dist/esm/im-scale-bar-plugin.js +1 -0
  70. package/plugins/beta/scale-bar/dist/esm/index.js +2 -0
  71. package/plugins/beta/scale-bar/dist/umd/im-scale-bar-plugin.js +1 -0
  72. package/plugins/beta/scale-bar/dist/umd/index.js +2 -0
  73. package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -0
  74. package/plugins/beta/use-location/dist/esm/index.js +2 -0
  75. package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -0
  76. package/plugins/beta/use-location/dist/umd/index.js +2 -0
  77. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  78. package/plugins/interact/dist/esm/index.js +1 -1
  79. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  80. package/plugins/interact/dist/umd/index.js +1 -1
  81. package/plugins/interact/src/InteractInit.test.js +97 -0
  82. package/plugins/interact/src/api/clear.test.js +18 -0
  83. package/plugins/interact/src/api/disable.test.js +12 -0
  84. package/plugins/interact/src/api/enable.test.js +48 -0
  85. package/plugins/interact/src/api/selectFeature.test.js +33 -0
  86. package/plugins/interact/src/api/unselectFeature.test.js +33 -0
  87. package/plugins/interact/src/defaults.js +1 -0
  88. package/plugins/interact/src/events.test.js +151 -0
  89. package/plugins/interact/src/hooks/useHighlightSync.test.js +212 -0
  90. package/plugins/interact/src/hooks/useInteractionHandlers.js +14 -7
  91. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +260 -0
  92. package/plugins/interact/src/index.test.js +22 -0
  93. package/plugins/interact/src/manifest.js +1 -1
  94. package/plugins/interact/src/manifest.test.js +94 -0
  95. package/plugins/interact/src/reducer.js +10 -1
  96. package/plugins/interact/src/reducer.test.js +142 -0
  97. package/plugins/interact/src/utils/buildStylesMap.test.js +57 -0
  98. package/plugins/interact/src/utils/featureQueries.test.js +56 -0
  99. package/plugins/interact/src/utils/turfHelpers.js +30 -0
  100. package/plugins/interact/src/utils/turfHelpers.test.js +30 -0
  101. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  102. package/plugins/search/dist/esm/index.js +1 -1
  103. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  104. package/plugins/search/dist/umd/index.js +1 -1
  105. package/plugins/search/src/utils/parseOsNamesResults.js +3 -7
  106. package/providers/{esri → beta/esri}/src/mapEvents.js +2 -2
  107. package/providers/beta/open-names/dist/esm/im-reverse-geocode.js +2 -0
  108. package/providers/beta/open-names/dist/esm/index.js +2 -0
  109. package/providers/beta/open-names/dist/umd/im-reverse-geocode.js +2 -0
  110. package/providers/beta/open-names/dist/umd/index.js +2 -0
  111. package/providers/maplibre/dist/esm/im-maplibre-framework.js +1 -1
  112. package/providers/maplibre/dist/esm/im-maplibre-framework.js.LICENSE.txt +1 -1
  113. package/providers/maplibre/dist/esm/im-maplibre-legacy-framework.js +1 -1
  114. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  115. package/providers/maplibre/dist/esm/index.js +1 -1
  116. package/providers/maplibre/dist/umd/im-maplibre-framework.js +1 -1
  117. package/providers/maplibre/dist/umd/im-maplibre-framework.js.LICENSE.txt +1 -1
  118. package/providers/maplibre/dist/umd/im-maplibre-legacy-framework.js +1 -1
  119. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  120. package/providers/maplibre/dist/umd/index.js +1 -1
  121. package/providers/maplibre/src/index.js +14 -1
  122. package/providers/maplibre/src/maplibreProvider.js +127 -0
  123. package/sonar-project.properties +1 -1
  124. package/src/App/hooks/useInterfaceAPI.js +24 -1
  125. package/src/App/renderer/mapButtons.js +1 -1
  126. package/src/App/store/appActionsMap.js +22 -1
  127. package/src/InteractiveMap/InteractiveMap.js +114 -6
  128. package/src/config/defaults.js +29 -20
  129. package/src/config/events.js +110 -4
  130. package/src/index.js +5 -2
  131. package/src/scss/tools/_border-focus.scss +20 -4
  132. package/src/types.js +585 -0
  133. package/webpack.esm.mjs +8 -8
  134. package/webpack.umd.mjs +8 -8
  135. package/plugins/datasets/dist/esm/im-datasets-plugin.js +0 -1
  136. package/plugins/datasets/dist/esm/index.js +0 -2
  137. package/plugins/datasets/dist/umd/im-datasets-plugin.js +0 -1
  138. package/plugins/datasets/dist/umd/index.js +0 -2
  139. package/plugins/draw-ml/dist/esm/im-draw-ml-plugin.js +0 -1
  140. package/plugins/draw-ml/dist/esm/index.js +0 -2
  141. package/plugins/draw-ml/dist/umd/im-draw-ml-plugin.js +0 -1
  142. package/plugins/draw-ml/dist/umd/index.js +0 -2
  143. package/plugins/draw-ml/src/modes/drawVertexMode.js +0 -275
  144. package/plugins/draw-ml/src/modes/editVertexMode.js +0 -426
  145. package/plugins/draw-ml/src/utils.js +0 -102
  146. package/plugins/frame/dist/esm/im-frame-plugin.js +0 -1
  147. package/plugins/frame/dist/esm/index.js +0 -2
  148. package/plugins/frame/dist/umd/im-frame-plugin.js +0 -1
  149. package/plugins/frame/dist/umd/index.js +0 -2
  150. package/plugins/map-styles/dist/esm/im-map-styles-plugin.js +0 -1
  151. package/plugins/map-styles/dist/esm/index.js +0 -2
  152. package/plugins/map-styles/dist/umd/im-map-styles-plugin.js +0 -1
  153. package/plugins/map-styles/dist/umd/index.js +0 -2
  154. package/plugins/scale-bar/dist/esm/im-scale-bar-plugin.js +0 -1
  155. package/plugins/scale-bar/dist/esm/index.js +0 -2
  156. package/plugins/scale-bar/dist/umd/im-scale-bar-plugin.js +0 -1
  157. package/plugins/scale-bar/dist/umd/index.js +0 -2
  158. package/plugins/use-location/dist/esm/im-use-location-plugin.js +0 -1
  159. package/plugins/use-location/dist/esm/index.js +0 -2
  160. package/plugins/use-location/dist/umd/im-use-location-plugin.js +0 -1
  161. package/plugins/use-location/dist/umd/index.js +0 -2
  162. package/providers/open-names/dist/esm/im-reverse-geocode.js +0 -2
  163. package/providers/open-names/dist/esm/index.js +0 -2
  164. package/providers/open-names/dist/umd/im-reverse-geocode.js +0 -2
  165. package/providers/open-names/dist/umd/index.js +0 -2
  166. /package/docs/architecture/{ARCHITECTURE_DIAGRAMS.md → architecture-diagrams.md} +0 -0
  167. /package/plugins/{datasets → beta/datasets}/dist/css/index.css +0 -0
  168. /package/plugins/{datasets → beta/datasets}/dist/esm/index.js.LICENSE.txt +0 -0
  169. /package/plugins/{datasets → beta/datasets}/dist/umd/index.js.LICENSE.txt +0 -0
  170. /package/plugins/{datasets → beta/datasets}/src/DatasetsInit.jsx +0 -0
  171. /package/plugins/{datasets → beta/datasets}/src/api/addDataset.js +0 -0
  172. /package/plugins/{datasets → beta/datasets}/src/api/hideDataset.js +0 -0
  173. /package/plugins/{datasets → beta/datasets}/src/api/hideFeatures.js +0 -0
  174. /package/plugins/{datasets → beta/datasets}/src/api/removeDataset.js +0 -0
  175. /package/plugins/{datasets → beta/datasets}/src/api/showDataset.js +0 -0
  176. /package/plugins/{datasets → beta/datasets}/src/api/showFeatures.js +0 -0
  177. /package/plugins/{datasets → beta/datasets}/src/datasets.js +0 -0
  178. /package/plugins/{datasets → beta/datasets}/src/datasets.scss +0 -0
  179. /package/plugins/{datasets → beta/datasets}/src/defaults.js +0 -0
  180. /package/plugins/{datasets → beta/datasets}/src/handleSetMapStyle.js +0 -0
  181. /package/plugins/{datasets → beta/datasets}/src/index.js +0 -0
  182. /package/plugins/{datasets → beta/datasets}/src/panels/Key.module.scss +0 -0
  183. /package/plugins/{datasets → beta/datasets}/src/panels/Layers.jsx +0 -0
  184. /package/plugins/{datasets → beta/datasets}/src/panels/Layers.module.scss +0 -0
  185. /package/plugins/{datasets → beta/datasets}/src/reducer.js +0 -0
  186. /package/plugins/{datasets → beta/datasets}/src/utils/bbox.js +0 -0
  187. /package/plugins/{datasets → beta/datasets}/src/utils/debounce.js +0 -0
  188. /package/plugins/{datasets → beta/datasets}/src/utils/filters.js +0 -0
  189. /package/plugins/{draw-es → beta/draw-es}/src/DrawInit.jsx +0 -0
  190. /package/plugins/{draw-es → beta/draw-es}/src/api/addFeature.js +0 -0
  191. /package/plugins/{draw-es → beta/draw-es}/src/api/deleteFeature.js +0 -0
  192. /package/plugins/{draw-es → beta/draw-es}/src/api/editFeature.js +0 -0
  193. /package/plugins/{draw-es → beta/draw-es}/src/api/newPolygon.js +0 -0
  194. /package/plugins/{draw-es → beta/draw-es}/src/events.js +0 -0
  195. /package/plugins/{draw-es → beta/draw-es}/src/graphic.js +0 -0
  196. /package/plugins/{draw-es → beta/draw-es}/src/index.js +0 -0
  197. /package/plugins/{draw-es → beta/draw-es}/src/manifest.js +0 -0
  198. /package/plugins/{draw-es → beta/draw-es}/src/reducer.js +0 -0
  199. /package/plugins/{draw-es → beta/draw-es}/src/sketchViewModel.js +0 -0
  200. /package/plugins/{draw-ml → beta/draw-ml}/dist/css/index.css +0 -0
  201. /package/plugins/{draw-ml → beta/draw-ml}/dist/esm/index.js.LICENSE.txt +0 -0
  202. /package/plugins/{draw-ml → beta/draw-ml}/dist/umd/index.js.LICENSE.txt +0 -0
  203. /package/plugins/{draw-ml → beta/draw-ml}/src/DrawInit.jsx +0 -0
  204. /package/plugins/{draw-ml → beta/draw-ml}/src/api/addFeature.js +0 -0
  205. /package/plugins/{draw-ml → beta/draw-ml}/src/api/deleteFeature.js +0 -0
  206. /package/plugins/{draw-ml → beta/draw-ml}/src/draw.scss +0 -0
  207. /package/plugins/{draw-ml → beta/draw-ml}/src/index.js +0 -0
  208. /package/plugins/{draw-ml → beta/draw-ml}/src/modes/disabledMode.js +0 -0
  209. /package/plugins/{draw-ml/src → beta/draw-ml/src/utils}/snapHelpers.js +0 -0
  210. /package/plugins/{frame → beta/frame}/dist/css/index.css +0 -0
  211. /package/plugins/{frame → beta/frame}/dist/esm/index.js.LICENSE.txt +0 -0
  212. /package/plugins/{frame → beta/frame}/dist/umd/index.js.LICENSE.txt +0 -0
  213. /package/plugins/{frame → beta/frame}/package.json +0 -0
  214. /package/plugins/{frame → beta/frame}/src/Frame.jsx +0 -0
  215. /package/plugins/{frame → beta/frame}/src/FrameInit.jsx +0 -0
  216. /package/plugins/{frame → beta/frame}/src/api/addFrame.js +0 -0
  217. /package/plugins/{frame → beta/frame}/src/api/editFeature.js +0 -0
  218. /package/plugins/{frame → beta/frame}/src/config.js +0 -0
  219. /package/plugins/{frame → beta/frame}/src/frame.scss +0 -0
  220. /package/plugins/{frame → beta/frame}/src/index.js +0 -0
  221. /package/plugins/{frame → beta/frame}/src/manifest.js +0 -0
  222. /package/plugins/{frame → beta/frame}/src/reducer.js +0 -0
  223. /package/plugins/{frame → beta/frame}/src/utils.js +0 -0
  224. /package/plugins/{map-styles → beta/map-styles}/dist/css/index.css +0 -0
  225. /package/plugins/{map-styles → beta/map-styles}/dist/esm/index.js.LICENSE.txt +0 -0
  226. /package/plugins/{map-styles → beta/map-styles}/dist/umd/index.js.LICENSE.txt +0 -0
  227. /package/plugins/{map-styles → beta/map-styles}/src/MapStylesInit.jsx +0 -0
  228. /package/plugins/{map-styles → beta/map-styles}/src/config.js +0 -0
  229. /package/plugins/{map-styles → beta/map-styles}/src/index.js +0 -0
  230. /package/plugins/{map-styles → beta/map-styles}/src/mapStyles.scss +0 -0
  231. /package/plugins/{scale-bar → beta/scale-bar}/dist/css/index.css +0 -0
  232. /package/plugins/{scale-bar → beta/scale-bar}/dist/esm/index.js.LICENSE.txt +0 -0
  233. /package/plugins/{scale-bar → beta/scale-bar}/dist/umd/index.js.LICENSE.txt +0 -0
  234. /package/plugins/{scale-bar → beta/scale-bar}/package.json +0 -0
  235. /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.jsx +0 -0
  236. /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.test.jsx +0 -0
  237. /package/plugins/{scale-bar → beta/scale-bar}/src/index.js +0 -0
  238. /package/plugins/{scale-bar → beta/scale-bar}/src/index.test.js +0 -0
  239. /package/plugins/{scale-bar → beta/scale-bar}/src/manifest.js +0 -0
  240. /package/plugins/{scale-bar → beta/scale-bar}/src/scaleBar.scss +0 -0
  241. /package/plugins/{scale-bar → beta/scale-bar}/src/utils.js +0 -0
  242. /package/plugins/{scale-bar → beta/scale-bar}/src/utils.test.js +0 -0
  243. /package/plugins/{use-location → beta/use-location}/dist/esm/index.js.LICENSE.txt +0 -0
  244. /package/plugins/{use-location → beta/use-location}/dist/umd/index.js.LICENSE.txt +0 -0
  245. /package/plugins/{use-location → beta/use-location}/src/UseLocation.jsx +0 -0
  246. /package/plugins/{use-location → beta/use-location}/src/UseLocationInit.jsx +0 -0
  247. /package/plugins/{use-location → beta/use-location}/src/defaults.js +0 -0
  248. /package/plugins/{use-location → beta/use-location}/src/events.js +0 -0
  249. /package/plugins/{use-location → beta/use-location}/src/index.js +0 -0
  250. /package/plugins/{use-location → beta/use-location}/src/manifest.js +0 -0
  251. /package/plugins/{use-location → beta/use-location}/src/reducer.js +0 -0
  252. /package/providers/{esri → beta/esri}/src/appEvents.js +0 -0
  253. /package/providers/{esri → beta/esri}/src/defaults.js +0 -0
  254. /package/providers/{esri → beta/esri}/src/esriProvider.js +0 -0
  255. /package/providers/{esri → beta/esri}/src/esriProvider.scss +0 -0
  256. /package/providers/{esri → beta/esri}/src/index.js +0 -0
  257. /package/providers/{esri → beta/esri}/src/utils/coords.js +0 -0
  258. /package/providers/{esri → beta/esri}/src/utils/detectWebGL.js +0 -0
  259. /package/providers/{esri → beta/esri}/src/utils/esriFixes.js +0 -0
  260. /package/providers/{esri → beta/esri}/src/utils/query.js +0 -0
  261. /package/providers/{esri → beta/esri}/src/utils/spatial.js +0 -0
  262. /package/providers/{open-names → beta/open-names}/dist/esm/im-reverse-geocode.js.LICENSE.txt +0 -0
  263. /package/providers/{open-names → beta/open-names}/dist/esm/index.js.LICENSE.txt +0 -0
  264. /package/providers/{open-names → beta/open-names}/dist/umd/im-reverse-geocode.js.LICENSE.txt +0 -0
  265. /package/providers/{open-names → beta/open-names}/dist/umd/index.js.LICENSE.txt +0 -0
  266. /package/providers/{open-names → beta/open-names}/src/index.js +0 -0
  267. /package/providers/{open-names → beta/open-names}/src/reverseGeocode.js +0 -0
  268. /package/providers/{open-names → beta/open-names}/src/utils/mapToLocationModel.js +0 -0
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Programmatically merge multiple polygons ia single polygon
3
+ * @param {object} context - plugin context
4
+ * @param {string} polygon[] - an a rray of polygons to merge
5
+ */
6
+ export const merge = ({ services }, polygons) => {
7
+ console.log('merge')
8
+ console.log(polygons)
9
+ }
@@ -0,0 +1,43 @@
1
+ import { getSnapInstance } from '../utils/snapHelpers.js'
2
+
3
+ /**
4
+ * Programmatically create a new line
5
+ * @param {object} context - plugin context
6
+ * @param {string} featureId - ID for the new feature
7
+ * @param {object} options - Options including snapLayers.
8
+ */
9
+ export const newLine = ({ appState, appConfig, pluginState, mapProvider }, featureId, options = {}) => {
10
+ const { dispatch } = pluginState
11
+ const { draw, map } = mapProvider
12
+
13
+ if (!draw) {
14
+ return
15
+ }
16
+
17
+ // Set per-call snap layers if provided
18
+ const snap = getSnapInstance(map)
19
+ if (snap?.setSnapLayers) {
20
+ snap.setSnapLayers(options.snapLayers || null)
21
+ } else if (options.snapLayers) {
22
+ // Snap instance not ready yet - store for later
23
+ map._pendingSnapLayers = options.snapLayers
24
+ } else {
25
+ // No action
26
+ }
27
+
28
+ // Update state so UI can react to snap layer availability
29
+ dispatch({ type: 'SET_HAS_SNAP_LAYERS', payload: options.snapLayers?.length > 0 })
30
+
31
+ // Change mode to draw_line
32
+ draw.changeMode('draw_line', {
33
+ container: appState.layoutRefs.viewportRef.current,
34
+ vertexMarkerId: `${appConfig.id}-cross-hair`,
35
+ addVertexButtonId: `${appConfig.id}-draw-add-point`,
36
+ interfaceType: appState.interfaceType,
37
+ getSnapEnabled: () => mapProvider.snapEnabled === true,
38
+ featureId
39
+ })
40
+
41
+ // Set mode to draw_line
42
+ dispatch({ type: 'SET_MODE', payload: 'draw_line' })
43
+ }
@@ -1,4 +1,4 @@
1
- import { getSnapInstance } from '../snapHelpers.js'
1
+ import { getSnapInstance } from '../utils/snapHelpers.js'
2
2
 
3
3
  /**
4
4
  * Programmatically create a new polygon
@@ -28,8 +28,8 @@ export const newPolygon = ({ appState, appConfig, pluginState, mapProvider }, fe
28
28
  // Update state so UI can react to snap layer availability
29
29
  dispatch({ type: 'SET_HAS_SNAP_LAYERS', payload: options.snapLayers?.length > 0 })
30
30
 
31
- // Change mode to draw_vertex
32
- draw.changeMode('draw_vertex', {
31
+ // Change mode to draw_polygon
32
+ draw.changeMode('draw_polygon', {
33
33
  container: appState.layoutRefs.viewportRef.current,
34
34
  vertexMarkerId: `${appConfig.id}-cross-hair`,
35
35
  addVertexButtonId: `${appConfig.id}-draw-add-point`,
@@ -38,6 +38,6 @@ export const newPolygon = ({ appState, appConfig, pluginState, mapProvider }, fe
38
38
  featureId
39
39
  })
40
40
 
41
- // Set mode to draw_vertex
42
- dispatch({ type: 'SET_MODE', payload: 'draw_vertex' })
41
+ // Set mode to draw_polygon
42
+ dispatch({ type: 'SET_MODE', payload: 'draw_polygon' })
43
43
  }
@@ -0,0 +1,81 @@
1
+ import { getSnapInstance } from '../utils/snapHelpers.js'
2
+ import { splitPolygon } from '../utils/spatial.js'
3
+ import { debounce } from '../utils/debounce.js'
4
+
5
+ /**
6
+ * Programmatically split a polygon or line
7
+ * @param {object} context - plugin context
8
+ * @param {string} feature - the new feature to be split
9
+ * @param {object} options - Options including snapLayers.
10
+ */
11
+ export const split = ({ appState, appConfig, pluginState, mapProvider }, featureId, options = {}) => {
12
+ const { dispatch } = pluginState
13
+ const { draw, map } = mapProvider
14
+
15
+ if (!draw) {
16
+ return
17
+ }
18
+
19
+ // Get target polygon feature
20
+ const polygonFeature = draw.get(featureId)
21
+
22
+ // Always include 'stroke-inactive' in snap layers
23
+ const snapLayers = ['stroke-inactive.cold', ...(options.snapLayers || [])]
24
+
25
+ // Set per-call snap layers if provided
26
+ const snap = getSnapInstance(map)
27
+
28
+ if (snap?.setSnapLayers) {
29
+ snap.setSnapLayers(snapLayers)
30
+ } else if (options.snapLayers) {
31
+ // Snap instance not ready yet - store for later
32
+ map._pendingSnapLayers = snapLayers
33
+ } else {
34
+ // No action
35
+ }
36
+
37
+ // Update state so UI can react to snap layer availability
38
+ dispatch({ type: 'SET_HAS_SNAP_LAYERS', payload: true })
39
+
40
+ // Change mode to draw_line
41
+ draw.changeMode('draw_line', {
42
+ container: appState.layoutRefs.viewportRef.current,
43
+ vertexMarkerId: `${appConfig.id}-cross-hair`,
44
+ addVertexButtonId: `${appConfig.id}-draw-add-point`,
45
+ interfaceType: appState.interfaceType,
46
+ getSnapEnabled: () => mapProvider.snapEnabled === true,
47
+ featureId: '_splitter',
48
+ properties: { splitter: 'invalid' }
49
+ })
50
+
51
+ // Perform split
52
+ map.on('draw.create', (e) => {
53
+ const lineFeature = e.features[0]
54
+ const featureCollection = splitPolygon(polygonFeature, lineFeature)
55
+ draw.setFeatureProperty('_splitter', 'splitter', featureCollection ? 'valid' : 'invalid')
56
+ dispatch({ type: 'SET_ACTION', payload: { name: 'split', isValid: !!featureCollection }})
57
+ })
58
+
59
+ // Real time check for valid line
60
+ const DEBOUNCE_SPLIT_POLYGON = 50
61
+ const onGeometryChange = debounce((e) => {
62
+ if (e.coordinates.length < 2) {
63
+ return
64
+ }
65
+ const lineFeature = { id: '_splitter', geometry: { type: 'LineString', coordinates: e.coordinates }}
66
+ const featureCollection = splitPolygon(polygonFeature, lineFeature)
67
+ const isValid = !!featureCollection
68
+ e.properties.splitter = isValid ? 'valid' : 'invalid'
69
+ e.ctx.store.render()
70
+ if (draw.getMode() === 'edit_vertex') {
71
+ dispatch({ type: 'SET_ACTION', payload: { name: 'split', isValid }})
72
+ }
73
+ }, DEBOUNCE_SPLIT_POLYGON)
74
+ map.on('draw.geometrychange', onGeometryChange)
75
+
76
+ // Set mode to 'draw_line'
77
+ dispatch({ type: 'SET_MODE', payload: 'draw_line' })
78
+
79
+ // Set action to 'split'
80
+ dispatch({ type: 'SET_ACTION', payload: { name: 'split' }})
81
+ }
@@ -2,6 +2,8 @@ export const DEFAULTS = {
2
2
  editColorsForeground: { light: 'rgba(29,112,184,1)', dark: '#ffffff' },
3
3
  editColorsBackground: { light: '#ffffff', dark: 'rgba(11,12,12,1)' },
4
4
  editColorsHalo: { light: 'rgba(11,12,12,1)', dark: '#ffffff' },
5
+ splitInvalidColors: { light: 'rgba(29,112,184,1)', dark: 'rgba(29,112,184,1)' },
6
+ splitValidColors: { light: 'rgba(29,112,184,1)', dark: 'rgba(29,112,184,1)' },
5
7
  stroke: 'rgba(212,53,28,1)',
6
8
  strokeWidth: 2,
7
9
  fill: 'rgba(212,53,28,0.1)',
@@ -1,4 +1,4 @@
1
- import { clearSnapState, getSnapInstance } from './snapHelpers.js'
1
+ import { clearSnapState, getSnapInstance } from './utils/snapHelpers.js'
2
2
 
3
3
  export function attachEvents ({ appState, appConfig, mapState, pluginState, mapProvider, buttonConfig, eventBus }) {
4
4
  const {
@@ -14,8 +14,24 @@ export function attachEvents ({ appState, appConfig, mapState, pluginState, mapP
14
14
  const { map, draw } = mapProvider
15
15
  const { dispatch, feature, tempFeature } = pluginState
16
16
 
17
+ // Helper to disable snap and hide indicators
18
+ const disableSnap = () => {
19
+ mapProvider.snapEnabled = false
20
+ dispatch({ type: 'SET_SNAP', payload: false })
21
+ const snap = getSnapInstance(map)
22
+ if (snap) {
23
+ snap.setSnapStatus?.(false)
24
+ clearSnapState(snap)
25
+ }
26
+ if (map.getLayer('snap-helper-circle')) {
27
+ map.setLayoutProperty('snap-helper-circle', 'visibility', 'none')
28
+ }
29
+ }
30
+
17
31
  // --- Done
18
32
  const handleDone = () => {
33
+ disableSnap()
34
+ mapProvider.undoStack?.clear()
19
35
  draw.changeMode('disabled')
20
36
  dispatch({ type: 'SET_MODE', payload: null })
21
37
  dispatch({ type: 'SET_FEATURE', payload: { feature: null, tempFeature: null }})
@@ -25,12 +41,16 @@ export function attachEvents ({ appState, appConfig, mapState, pluginState, mapP
25
41
 
26
42
  // --- Cancel
27
43
  const handleCancel = () => {
28
- draw.delete(tempFeature.id)
44
+ if (tempFeature?.id) {
45
+ draw.delete(tempFeature.id)
46
+ }
29
47
 
30
48
  // Reinstate initial feature
31
49
  if (feature) {
32
50
  draw.add(feature)
33
51
  }
52
+ disableSnap()
53
+ mapProvider.undoStack?.clear()
34
54
  draw.changeMode('disabled')
35
55
 
36
56
  dispatch({ type: 'SET_MODE', payload: null })
@@ -39,10 +59,67 @@ export function attachEvents ({ appState, appConfig, mapState, pluginState, mapP
39
59
  }
40
60
  drawCancel.onClick = handleCancel
41
61
 
62
+ // --- Finish shape (programmatically complete the drawing)
63
+ const handleFinish = () => {
64
+ const mode = draw.getMode()
65
+ if (!['draw_polygon', 'draw_line'].includes(mode)) {
66
+ return
67
+ }
68
+
69
+ const features = draw.getAll().features
70
+ if (features.length === 0) {
71
+ return
72
+ }
73
+
74
+ const feature = features[0]
75
+ const geom = feature.geometry
76
+
77
+ // Remove the rubber band point (last coordinate that follows cursor)
78
+ if (geom.type === 'Polygon') {
79
+ // Polygon coords: [v1, v2, v3, ..., rubberBand, v1(closing)]
80
+ // Remove rubber band (second to last) and keep the ring closed
81
+ const ring = geom.coordinates[0]
82
+ geom.coordinates[0] = [...ring.slice(0, -2), ring[0]]
83
+ } else {
84
+ // Line coords: [v1, v2, v3, ..., rubberBand]
85
+ // Remove the rubber band (last point)
86
+ geom.coordinates = geom.coordinates.slice(0, -1)
87
+ }
88
+
89
+ // Fire draw.create to trigger the standard completion flow
90
+ map.fire('draw.create', { features: [feature] })
91
+
92
+ // Change mode to trigger cleanup (this calls onStop on the draw mode)
93
+ draw.changeMode('simple_select', { featureIds: [feature.id] })
94
+ }
95
+ drawFinish.onClick = handleFinish
96
+
97
+ // --- Undo last action (only for draw modes - edit_vertex handles its own undo)
98
+ const handleUndo = () => {
99
+ // edit_vertex mode handles its own undo via button click listener
100
+ if (draw.getMode() === 'edit_vertex') {
101
+ return
102
+ }
103
+
104
+ const undoStack = mapProvider.undoStack
105
+ if (!undoStack || undoStack.length === 0) {
106
+ return
107
+ }
108
+
109
+ const operation = undoStack.pop()
110
+
111
+ // Set flag to prevent button click from also adding a vertex
112
+ map._undoInProgress = true
113
+ setTimeout(() => { map._undoInProgress = false }, 100)
114
+
115
+ // Fire event for the current mode to handle
116
+ map.fire('draw.undo', { operation })
117
+ }
118
+ drawUndo.onClick = handleUndo
119
+
42
120
  // --- Snap toggle with throttle to prevent Safari slowdown
43
121
  let lastToggleTime = 0
44
122
  const THROTTLE_MS = 300
45
-
46
123
  const handleSnap = () => {
47
124
  // Strict throttle - ignore clicks within throttle window
48
125
  const now = Date.now()
@@ -88,6 +165,9 @@ export function attachEvents ({ appState, appConfig, mapState, pluginState, mapP
88
165
  dispatch({ type: 'SET_FEATURE', payload: { tempFeature: newFeature }})
89
166
  eventBus.emit('draw:create', e)
90
167
 
168
+ // Clear draw mode undo stack - editing starts fresh
169
+ mapProvider.undoStack?.clear()
170
+
91
171
  // Switch straight to edit vertex mode
92
172
  dispatch({ type: 'SET_MODE', payload: 'edit_vertex'})
93
173
 
@@ -95,6 +175,7 @@ export function attachEvents ({ appState, appConfig, mapState, pluginState, mapP
95
175
  draw.changeMode('edit_vertex', {
96
176
  container: appState.layoutRefs.viewportRef.current,
97
177
  deleteVertexButtonId: `${appConfig.id}-draw-delete-point`,
178
+ undoButtonId: `${appConfig.id}-draw-undo`,
98
179
  isPanEnabled: appState.interfaceType !== 'keyboard',
99
180
  interfaceType: appState.interfaceType,
100
181
  scale: { small: 1, medium: 1.5, large: 2 }[mapState.mapSize],
@@ -105,6 +186,12 @@ export function attachEvents ({ appState, appConfig, mapState, pluginState, mapP
105
186
  }
106
187
  map.on('draw.create', onCreate)
107
188
 
189
+ // --- A shape is updated
190
+ const onUpdate = (e) => {
191
+ eventBus.emit('draw:update', e)
192
+ }
193
+ map.on('draw.update', onUpdate)
194
+
108
195
  // --- A vertex is selected
109
196
  const onVertexSelection = (e) => {
110
197
  dispatch({ type: 'SET_SELECTED_VERTEX_INDEX', payload: e })
@@ -112,6 +199,18 @@ export function attachEvents ({ appState, appConfig, mapState, pluginState, mapP
112
199
  }
113
200
  map.on('draw.vertexselection', onVertexSelection)
114
201
 
202
+ // --- Vertex count changes during drawing
203
+ const onVertexChange = (e) => {
204
+ dispatch({ type: 'SET_SELECTED_VERTEX_INDEX', payload: { index: -1, numVertecies: e.numVertecies } })
205
+ }
206
+ map.on('draw.vertexchange', onVertexChange)
207
+
208
+ // --- Undo stack changes
209
+ const onUndoChange = (e) => {
210
+ dispatch({ type: 'SET_UNDO_STACK_LENGTH', payload: e.length })
211
+ }
212
+ map.on('draw.undochange', onUndoChange)
213
+
115
214
  return () => {
116
215
  drawDone.onClick = null
117
216
  drawAddPoint.onClick = null
@@ -122,6 +221,9 @@ export function attachEvents ({ appState, appConfig, mapState, pluginState, mapP
122
221
  drawCancel.onClick = null
123
222
  map.off('styledata', handleStyleData)
124
223
  map.off('draw.create', onCreate)
224
+ map.off('draw.update', onUpdate)
125
225
  map.off('draw.vertexselection', onVertexSelection)
226
+ map.off('draw.vertexchange', onVertexChange)
227
+ map.off('draw.undochange', onUndoChange)
126
228
  }
127
229
  }
@@ -2,9 +2,12 @@
2
2
  import { initialState, actions } from './reducer.js'
3
3
  import { DrawInit } from './DrawInit.jsx'
4
4
  import { newPolygon } from './api/newPolygon.js'
5
+ import { newLine } from './api/newLine.js'
5
6
  import { editFeature } from './api/editFeature.js'
6
7
  import { addFeature } from './api/addFeature.js'
7
8
  import { deleteFeature } from './api/deleteFeature.js'
9
+ import { split } from './api/split.js'
10
+ import { merge } from './api/merge.js'
8
11
 
9
12
  const createButtonSlots = (showLabel) => ({
10
13
  mobile: { slot: 'actions', showLabel },
@@ -22,39 +25,39 @@ export const manifest = {
22
25
 
23
26
  buttons: [{
24
27
  id: 'drawDone',
25
- label: 'Done',
26
- variant: 'primary',
28
+ label: ({ pluginState }) => pluginState.action ? pluginState.action.charAt(0).toUpperCase() + pluginState.action.slice(1) : 'Done',
27
29
  hiddenWhen: ({ appState, pluginState }) => !pluginState.mode || appState.interfaceType !== 'mouse' && pluginState.mode !== 'edit_vertex',
28
- enableWhen: ({ pluginState }) => !!pluginState.tempFeature,
29
- ...createButtonSlots(true)
30
+ enableWhen: ({ pluginState }) => pluginState.action ? pluginState.actionValid : !!pluginState.tempFeature,
31
+ ...createButtonSlots(true),
32
+ variant: 'primary',
30
33
  },{
31
34
  id: 'drawAddPoint',
32
35
  label: 'Add point',
33
36
  variant: 'primary',
34
- hiddenWhen: ({ appState, pluginState }) => pluginState.mode !== 'draw_vertex' || appState.interfaceType === 'mouse',
37
+ hiddenWhen: ({ appState, pluginState }) => !['draw_polygon', 'draw_line'].includes(pluginState.mode) || appState.interfaceType === 'mouse',
35
38
  ...createButtonSlots(true)
36
39
  },{
37
40
  id: 'drawUndo',
38
41
  label: 'Undo',
39
42
  iconId: 'undo',
40
43
  variant: 'tertiary',
41
- hiddenWhen: ({ pluginState }) => !pluginState.mode,
42
- enableWhen: ({ pluginState }) => false,
44
+ hiddenWhen: ({ pluginState }) => !['draw_polygon', 'draw_line', 'edit_vertex'].includes(pluginState.mode),
45
+ enableWhen: ({ pluginState }) => pluginState.undoStackLength > 0,
43
46
  ...createButtonSlots(false)
44
47
  },{
45
48
  id: 'drawFinish',
46
49
  label: 'Finish shape',
47
50
  iconId: 'check',
48
51
  variant: 'tertiary',
49
- hiddenWhen: ({ pluginState }) => pluginState.mode !== 'draw_vertex',
50
- enableWhen: ({ pluginState }) => pluginState.numVertecies > 3,
52
+ hiddenWhen: ({ pluginState }) => !['draw_polygon', 'draw_line'].includes(pluginState.mode),
53
+ enableWhen: ({ pluginState }) => pluginState.numVertecies >= (pluginState.mode === 'draw_polygon' ? 3 : 2),
51
54
  ...createButtonSlots(false)
52
55
  },{
53
56
  id: 'drawDeletePoint',
54
57
  label: 'Delete point',
55
58
  iconId: 'close',
56
59
  variant: 'tertiary',
57
- enableWhen: ({ pluginState }) => pluginState.selectedVertexIndex >= 0 && pluginState.numVertecies > 3,
60
+ enableWhen: ({ pluginState }) => pluginState.selectedVertexIndex >= 0 && pluginState.numVertecies > (pluginState.tempFeature?.geometry?.type === 'Polygon' ? 3 : 2),
58
61
  hiddenWhen: ({ pluginState }) => !(['simple_select', 'edit_vertex'].includes(pluginState.mode)),
59
62
  ...createButtonSlots(false)
60
63
  },{
@@ -93,8 +96,11 @@ export const manifest = {
93
96
 
94
97
  api: {
95
98
  newPolygon,
99
+ newLine,
96
100
  editFeature,
97
101
  addFeature,
98
- deleteFeature
102
+ deleteFeature,
103
+ split,
104
+ merge
99
105
  }
100
106
  }
@@ -1,9 +1,11 @@
1
1
  import MapboxDraw from '@mapbox/mapbox-gl-draw'
2
2
  import { DisabledMode } from './modes/disabledMode.js'
3
3
  import { EditVertexMode } from './modes/editVertexMode.js'
4
- import { DrawVertexMode } from './modes/drawVertexMode.js'
4
+ import { DrawPolygonMode } from './modes/drawPolygonMode.js'
5
+ import { DrawLineMode } from './modes/drawLineMode.js'
5
6
  import { createDrawStyles, updateDrawStyles } from './styles.js'
6
7
  import { initMapLibreSnap } from './mapboxSnap.js'
8
+ import { createUndoStack } from './undoStack.js'
7
9
 
8
10
  /**
9
11
  * Creates and manages a MapLibre/Mapbox Draw control instance configured for polygon editing.
@@ -33,7 +35,8 @@ export const createMapboxDraw = ({ mapStyle, mapProvider, events, eventBus, snap
33
35
  ...MapboxDraw.modes,
34
36
  disabled: DisabledMode,
35
37
  edit_vertex: EditVertexMode,
36
- draw_vertex: DrawVertexMode
38
+ draw_polygon: DrawPolygonMode,
39
+ draw_line: DrawLineMode
37
40
  }
38
41
 
39
42
  // --- Create MapLibre Draw instance ---
@@ -46,10 +49,53 @@ export const createMapboxDraw = ({ mapStyle, mapProvider, events, eventBus, snap
46
49
  })
47
50
  map.addControl(draw)
48
51
 
52
+ // Workaround: mapbox-gl-draw calls preventDefault() on touchend even in disabled mode,
53
+ // which prevents the browser from synthesizing a click event. We detect taps and
54
+ // manually dispatch a click event when in disabled mode.
55
+ const canvas = map.getCanvas()
56
+ let touchStart = null
57
+
58
+ const handleTouchStart = (e) => {
59
+ if (e.touches.length === 1) {
60
+ touchStart = { x: e.touches[0].clientX, y: e.touches[0].clientY, time: Date.now() }
61
+ }
62
+ }
63
+
64
+ const handleTouchEnd = (e) => {
65
+ if (draw.getMode() !== 'disabled' || !touchStart) {
66
+ touchStart = null
67
+ return
68
+ }
69
+
70
+ const touch = e.changedTouches[0]
71
+ const dx = touch.clientX - touchStart.x
72
+ const dy = touch.clientY - touchStart.y
73
+ const duration = Date.now() - touchStart.time
74
+
75
+ // Only synthesize click for quick taps with minimal movement
76
+ if (duration < 300 && Math.abs(dx) < 10 && Math.abs(dy) < 10) {
77
+ canvas.dispatchEvent(new MouseEvent('click', {
78
+ bubbles: true,
79
+ cancelable: true,
80
+ clientX: touch.clientX,
81
+ clientY: touch.clientY
82
+ }))
83
+ }
84
+
85
+ touchStart = null
86
+ }
87
+
88
+ canvas.addEventListener('touchstart', handleTouchStart, { passive: true })
89
+ canvas.addEventListener('touchend', handleTouchEnd, { passive: true })
90
+
49
91
  // We need a reference to this
50
92
  mapProvider.draw = draw
51
93
  // Initialize snap as disabled (matches initialState.snap = false)
52
94
  mapProvider.snapEnabled = false
95
+ // Initialize undo stack (also stored on map for mode access)
96
+ const undoStack = createUndoStack(map)
97
+ mapProvider.undoStack = undoStack
98
+ map._undoStack = undoStack
53
99
 
54
100
  // --- Initialize MapboxSnap using external module ---
55
101
  // Start with status: false to match initial snap disabled state
@@ -77,6 +123,9 @@ export const createMapboxDraw = ({ mapStyle, mapProvider, events, eventBus, snap
77
123
  return {
78
124
  draw,
79
125
  remove() {
126
+ // Remove touch workaround listeners
127
+ canvas.removeEventListener('touchstart', handleTouchStart)
128
+ canvas.removeEventListener('touchend', handleTouchEnd)
80
129
  // Remove event listeners
81
130
  eventBus.off(events.MAP_SET_STYLE, handleSetMapStyle)
82
131
  // Delete all features and disable draw
@@ -68,6 +68,7 @@ function applyMapboxSnapPatches(colors) {
68
68
  return orig.getLines.call(this, feature, mouse, radiusArg)
69
69
  } catch (e) {
70
70
  // Invalid geometry - skip this feature
71
+ console.log(e)
71
72
  return []
72
73
  }
73
74
  }
@@ -127,6 +128,7 @@ function applyMapboxSnapPatches(colors) {
127
128
  return result
128
129
  } catch (err) {
129
130
  // Invalid geometry encountered - clear state and continue
131
+ console.log(err)
130
132
  this.snapStatus = false
131
133
  this.snapCoords = null
132
134
  return