@defra/interactive-map 0.0.3-alpha → 0.0.5-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 (407) hide show
  1. package/README.md +16 -57
  2. package/dist/css/index.css +1 -1
  3. package/dist/esm/im-core.js +1 -1
  4. package/dist/esm/index.js +1 -1
  5. package/dist/umd/im-core.js +1 -1
  6. package/dist/umd/index.js +1 -1
  7. package/docs/api/button-definition.md +176 -0
  8. package/docs/api/context.md +124 -0
  9. package/docs/api/control-definition.md +72 -0
  10. package/docs/api/icon-definition.md +28 -0
  11. package/docs/api/map-style-config.md +76 -0
  12. package/docs/api/marker-config.md +51 -0
  13. package/docs/api/panel-definition.md +117 -0
  14. package/docs/api/slots.md +25 -0
  15. package/docs/api.md +721 -0
  16. package/docs/architecture.md +139 -0
  17. package/docs/building-a-plugin.md +149 -0
  18. package/docs/getting-started.md +46 -0
  19. package/docs/govuk-prototype.md +0 -0
  20. package/docs/plugins/interact.md +205 -0
  21. package/docs/plugins/map-styles.md +84 -0
  22. package/docs/plugins/plugin-context.md +67 -0
  23. package/docs/plugins/plugin-descriptor.md +71 -0
  24. package/docs/plugins/plugin-manifest.md +118 -0
  25. package/docs/plugins/scale-bar.md +62 -0
  26. package/docs/plugins/search.md +162 -0
  27. package/docs/plugins.md +116 -0
  28. package/govuk-prototype-kit.config.json +7 -7
  29. package/package.json +16 -2
  30. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +2 -0
  31. package/plugins/beta/datasets/dist/esm/index.js +2 -0
  32. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
  33. package/plugins/beta/datasets/dist/umd/index.js +2 -0
  34. package/plugins/beta/datasets/src/datasets.js +113 -0
  35. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +206 -0
  36. package/plugins/beta/datasets/src/fetch/fetchGeoJSON.js +38 -0
  37. package/plugins/{datasets → beta/datasets}/src/handleSetMapStyle.js +8 -1
  38. package/plugins/{datasets → beta/datasets}/src/manifest.js +1 -1
  39. package/plugins/{datasets → beta/datasets}/src/mapLayers.js +42 -4
  40. package/plugins/{datasets → beta/datasets}/src/panels/Key.jsx +1 -1
  41. package/plugins/beta/datasets/src/utils/bbox.js +115 -0
  42. package/plugins/beta/draw-ml/dist/css/index.css +1 -0
  43. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -0
  44. package/plugins/beta/draw-ml/dist/esm/index.js +2 -0
  45. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -0
  46. package/plugins/beta/draw-ml/dist/umd/index.js +2 -0
  47. package/plugins/beta/draw-ml/src/api/addFeature.js +27 -0
  48. package/plugins/{draw-ml → beta/draw-ml}/src/api/editFeature.js +17 -6
  49. package/plugins/beta/draw-ml/src/api/merge.js +9 -0
  50. package/plugins/beta/draw-ml/src/api/newLine.js +68 -0
  51. package/plugins/beta/draw-ml/src/api/newPolygon.js +68 -0
  52. package/plugins/beta/draw-ml/src/api/split.js +81 -0
  53. package/plugins/{draw-ml → beta/draw-ml}/src/defaults.js +3 -0
  54. package/plugins/beta/draw-ml/src/draw.scss +32 -0
  55. package/plugins/beta/draw-ml/src/events.js +171 -0
  56. package/plugins/beta/draw-ml/src/manifest.js +132 -0
  57. package/plugins/{draw-ml → beta/draw-ml}/src/mapboxDraw.js +51 -2
  58. package/plugins/{draw-ml → beta/draw-ml}/src/mapboxSnap.js +18 -12
  59. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +497 -0
  60. package/plugins/beta/draw-ml/src/modes/drawLineMode.js +18 -0
  61. package/plugins/beta/draw-ml/src/modes/drawPolygonMode.js +16 -0
  62. package/plugins/beta/draw-ml/src/modes/editVertex/geometryHelpers.js +135 -0
  63. package/plugins/beta/draw-ml/src/modes/editVertex/helpers.js +2 -0
  64. package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +134 -0
  65. package/plugins/beta/draw-ml/src/modes/editVertex/undoHandlers.js +133 -0
  66. package/plugins/beta/draw-ml/src/modes/editVertex/vertexOperations.js +141 -0
  67. package/plugins/beta/draw-ml/src/modes/editVertex/vertexQueries.js +121 -0
  68. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +437 -0
  69. package/plugins/{draw-ml → beta/draw-ml}/src/reducer.js +30 -2
  70. package/plugins/{draw-ml → beta/draw-ml}/src/styles.js +66 -36
  71. package/plugins/beta/draw-ml/src/undoStack.js +50 -0
  72. package/plugins/beta/draw-ml/src/utils/debounce.js +16 -0
  73. package/plugins/beta/draw-ml/src/utils/flattenStyleProperties.js +49 -0
  74. package/plugins/beta/draw-ml/src/utils/spatial.js +258 -0
  75. package/plugins/beta/frame/dist/esm/im-frame-plugin.js +1 -0
  76. package/plugins/beta/frame/dist/esm/index.js +2 -0
  77. package/plugins/beta/frame/dist/umd/im-frame-plugin.js +1 -0
  78. package/plugins/beta/frame/dist/umd/index.js +2 -0
  79. package/plugins/{frame → beta/frame}/src/Frame.jsx +1 -1
  80. package/plugins/{map-styles → beta/map-styles}/dist/css/index.css +1 -1
  81. package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -0
  82. package/plugins/beta/map-styles/dist/esm/index.js +2 -0
  83. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -0
  84. package/plugins/beta/map-styles/dist/umd/index.js +2 -0
  85. package/plugins/{map-styles → beta/map-styles}/src/MapStyles.jsx +1 -1
  86. package/plugins/{map-styles → beta/map-styles}/src/manifest.js +8 -10
  87. package/plugins/{map-styles → beta/map-styles}/src/mapStyles.scss +4 -1
  88. package/plugins/beta/scale-bar/dist/esm/im-scale-bar-plugin.js +1 -0
  89. package/plugins/beta/scale-bar/dist/esm/index.js +2 -0
  90. package/plugins/beta/scale-bar/dist/umd/im-scale-bar-plugin.js +1 -0
  91. package/plugins/beta/scale-bar/dist/umd/index.js +2 -0
  92. package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -0
  93. package/plugins/beta/use-location/dist/esm/index.js +2 -0
  94. package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -0
  95. package/plugins/beta/use-location/dist/umd/index.js +2 -0
  96. package/plugins/interact/dist/css/index.css +1 -1
  97. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  98. package/plugins/interact/dist/esm/index.js +1 -1
  99. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  100. package/plugins/interact/dist/umd/index.js +1 -1
  101. package/plugins/interact/src/InteractInit.jsx +1 -1
  102. package/plugins/interact/src/InteractInit.test.js +97 -0
  103. package/plugins/interact/src/api/clear.test.js +18 -0
  104. package/plugins/interact/src/api/disable.test.js +12 -0
  105. package/plugins/interact/src/api/enable.test.js +48 -0
  106. package/plugins/interact/src/api/selectFeature.test.js +33 -0
  107. package/plugins/interact/src/api/unselectFeature.test.js +33 -0
  108. package/plugins/interact/src/defaults.js +2 -0
  109. package/plugins/interact/src/events.js +37 -52
  110. package/plugins/interact/src/events.test.js +186 -0
  111. package/plugins/interact/src/hooks/useHighlightSync.js +1 -1
  112. package/plugins/interact/src/hooks/useHighlightSync.test.js +212 -0
  113. package/plugins/interact/src/hooks/useInteractionHandlers.js +80 -41
  114. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +316 -0
  115. package/plugins/interact/src/index.test.js +22 -0
  116. package/plugins/interact/src/interact.scss +6 -0
  117. package/plugins/interact/src/manifest.js +1 -1
  118. package/plugins/interact/src/manifest.test.js +94 -0
  119. package/plugins/interact/src/reducer.js +28 -21
  120. package/plugins/interact/src/reducer.test.js +155 -0
  121. package/plugins/interact/src/utils/buildStylesMap.test.js +57 -0
  122. package/plugins/interact/src/utils/featureQueries.js +2 -2
  123. package/plugins/interact/src/utils/featureQueries.test.js +56 -0
  124. package/plugins/interact/src/utils/spatial.js +95 -0
  125. package/plugins/interact/src/utils/spatial.test.js +93 -0
  126. package/plugins/search/dist/css/index.css +1 -1
  127. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  128. package/plugins/search/dist/esm/index.js +1 -1
  129. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  130. package/plugins/search/dist/umd/index.js +1 -1
  131. package/plugins/search/src/Search.jsx +2 -2
  132. package/plugins/search/src/components/Form/Form.jsx +1 -1
  133. package/plugins/search/src/components/Suggestions/Suggestions.jsx +5 -5
  134. package/plugins/search/src/datasets.js +1 -1
  135. package/plugins/search/src/defaults.js +3 -0
  136. package/plugins/search/src/events/fetchSuggestions.js +48 -53
  137. package/plugins/search/src/events/formHandlers.js +3 -2
  138. package/plugins/search/src/events/index.js +1 -1
  139. package/plugins/search/src/events/inputHandlers.js +3 -1
  140. package/plugins/search/src/events/suggestionHandlers.js +12 -3
  141. package/plugins/search/src/search.scss +4 -1
  142. package/plugins/search/src/utils/parseOsNamesResults.js +15 -19
  143. package/providers/{esri → beta/esri}/src/esriProvider.js +1 -1
  144. package/providers/{esri → beta/esri}/src/index.js +1 -1
  145. package/providers/{esri → beta/esri}/src/mapEvents.js +2 -2
  146. package/providers/beta/open-names/dist/esm/im-reverse-geocode.js +2 -0
  147. package/providers/beta/open-names/dist/esm/index.js +2 -0
  148. package/providers/beta/open-names/dist/umd/im-reverse-geocode.js +2 -0
  149. package/providers/beta/open-names/dist/umd/im-reverse-geocode.js.LICENSE.txt +1 -0
  150. package/providers/beta/open-names/dist/umd/index.js +2 -0
  151. package/providers/beta/open-names/dist/umd/index.js.LICENSE.txt +1 -0
  152. package/providers/maplibre/dist/esm/im-maplibre-framework.js +1 -1
  153. package/providers/maplibre/dist/esm/im-maplibre-framework.js.LICENSE.txt +1 -1
  154. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  155. package/providers/maplibre/dist/esm/index.js +1 -1
  156. package/providers/maplibre/dist/umd/im-maplibre-framework.js +1 -1
  157. package/providers/maplibre/dist/umd/im-maplibre-framework.js.LICENSE.txt +1 -1
  158. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  159. package/providers/maplibre/dist/umd/index.js +1 -1
  160. package/providers/maplibre/src/defaults.js +3 -2
  161. package/providers/maplibre/src/index.js +36 -20
  162. package/providers/maplibre/src/maplibreProvider.js +140 -11
  163. package/providers/maplibre/src/utils/detectWebgl.js +1 -2
  164. package/providers/maplibre/src/utils/highlightFeatures.js +78 -67
  165. package/providers/maplibre/src/utils/labels.js +174 -120
  166. package/providers/maplibre/src/utils/maplibreFixes.js +2 -2
  167. package/providers/maplibre/src/utils/queryFeatures.js +146 -0
  168. package/providers/maplibre/src/utils/spatial.js +14 -17
  169. package/sonar-project.properties +40 -3
  170. package/src/App/components/Actions/Actions.jsx +2 -2
  171. package/src/App/components/Actions/Actions.module.scss +2 -2
  172. package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +3 -1
  173. package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +9 -0
  174. package/src/App/components/Logo/Logo.jsx +1 -1
  175. package/src/App/components/MapButton/MapButton.jsx +217 -41
  176. package/src/App/components/MapButton/MapButton.module.scss +32 -21
  177. package/src/App/components/MapButton/MapButton.test.jsx +128 -84
  178. package/src/App/components/Markers/Markers.jsx +1 -1
  179. package/src/App/components/Markers/Markers.module.scss +0 -5
  180. package/src/App/components/Panel/Panel.jsx +75 -45
  181. package/src/App/components/Panel/Panel.module.scss +18 -24
  182. package/src/App/components/Panel/Panel.test.jsx +18 -0
  183. package/src/App/components/PopupMenu/PopupMenu.jsx +246 -0
  184. package/src/App/components/PopupMenu/PopupMenu.module.scss +70 -0
  185. package/src/App/components/PopupMenu/PopupMenu.test.jsx +304 -0
  186. package/src/App/components/Tooltip/Tooltip.jsx +4 -2
  187. package/src/App/components/Tooltip/getTooltipPosition.js +1 -1
  188. package/src/App/components/Viewport/MapController.jsx +1 -1
  189. package/src/App/components/Viewport/MapStatus.jsx +1 -1
  190. package/src/App/components/Viewport/Viewport.jsx +2 -2
  191. package/src/App/components/Viewport/Viewport.module.scss +2 -2
  192. package/src/App/controls/keyboardActions.js +3 -2
  193. package/src/App/controls/keyboardShortcuts.js +4 -2
  194. package/src/App/hooks/useButtonStateEvaluator.js +58 -45
  195. package/src/App/hooks/useButtonStateEvaluator.test.js +36 -3
  196. package/src/App/hooks/useCrossHairAPI.js +49 -47
  197. package/src/App/hooks/useFocusVisible.js +2 -2
  198. package/src/App/hooks/useInterfaceAPI.js +36 -1
  199. package/src/App/hooks/useKeyboardHint.js +1 -1
  200. package/src/App/hooks/useKeyboardShortcuts.js +4 -2
  201. package/src/App/hooks/useMapAnnouncements.js +34 -32
  202. package/src/App/hooks/useMapEvents.js +12 -1
  203. package/src/App/hooks/useMapProviderOverrides.js +3 -3
  204. package/src/App/hooks/useMapStateSync.js +7 -1
  205. package/src/App/hooks/useMapURLSync.js +6 -1
  206. package/src/App/hooks/useMarkersAPI.js +88 -50
  207. package/src/App/hooks/useMediaQueryDispatch.test.js +48 -0
  208. package/src/App/hooks/useModalPanelBehaviour.js +46 -40
  209. package/src/App/hooks/useResizeObserver.js +2 -2
  210. package/src/App/initialiseApp.js +45 -38
  211. package/src/App/layout/Layout.jsx +6 -4
  212. package/src/App/layout/layout.module.scss +13 -17
  213. package/src/App/registry/keyboardShortcutRegistry.js +12 -2
  214. package/src/App/registry/keyboardShortcutRegistry.test.js +28 -0
  215. package/src/App/registry/mergeManifests.js +1 -1
  216. package/src/App/registry/panelRegistry.js +1 -1
  217. package/src/App/registry/panelRegistry.test.js +30 -1
  218. package/src/App/registry/pluginRegistry.js +14 -51
  219. package/src/App/registry/pluginRegistry.test.js +54 -6
  220. package/src/App/renderer/HtmlElementHost.jsx +186 -0
  221. package/src/App/renderer/HtmlElementHost.test.jsx +231 -0
  222. package/src/App/renderer/mapButtons.js +12 -9
  223. package/src/App/renderer/mapButtons.test.js +6 -4
  224. package/src/App/renderer/mapControls.js +6 -0
  225. package/src/App/renderer/mapControls.test.js +10 -2
  226. package/src/App/renderer/mapPanels.js +39 -26
  227. package/src/App/renderer/mapPanels.test.js +29 -0
  228. package/src/App/renderer/pluginWrapper.test.js +28 -0
  229. package/src/App/renderer/slotHelpers.js +60 -0
  230. package/src/App/renderer/slotHelpers.test.js +82 -0
  231. package/src/App/store/AppProvider.jsx +3 -0
  232. package/src/App/store/AppProvider.test.jsx +40 -0
  233. package/src/App/store/PluginProvider.jsx +7 -5
  234. package/src/App/store/appActionsMap.js +46 -1
  235. package/src/App/store/appActionsMap.test.js +21 -0
  236. package/src/App/store/appReducer.js +1 -0
  237. package/src/App/store/mapActionsMap.js +12 -24
  238. package/src/App/store/mapReducer.js +1 -1
  239. package/src/InteractiveMap/InteractiveMap.js +194 -13
  240. package/src/InteractiveMap/InteractiveMap.test.js +164 -2
  241. package/src/InteractiveMap/behaviourController.js +12 -2
  242. package/src/InteractiveMap/behaviourController.test.js +82 -1
  243. package/src/InteractiveMap/deviceChecker.js +1 -1
  244. package/src/InteractiveMap/deviceChecker.test.js +2 -2
  245. package/src/InteractiveMap/historyManager.js +41 -5
  246. package/src/InteractiveMap/historyManager.test.js +37 -6
  247. package/src/InteractiveMap/polyfills.js +39 -0
  248. package/src/InteractiveMap/polyfills.test.js +127 -0
  249. package/src/config/appConfig.js +5 -5
  250. package/src/config/appConfig.test.js +107 -42
  251. package/src/config/defaults.js +30 -20
  252. package/src/config/events.js +110 -4
  253. package/src/index.js +5 -2
  254. package/src/index.umd.js +1 -1
  255. package/src/scss/main.scss +1 -0
  256. package/src/scss/settings/_dimensions.scss +6 -2
  257. package/src/scss/tools/_border-focus.scss +20 -4
  258. package/src/services/eventBus.test.js +24 -1
  259. package/src/test-utils.js +1 -0
  260. package/src/types.js +590 -0
  261. package/src/utils/detectBreakpoint.js +78 -77
  262. package/src/utils/detectBreakpoint.test.js +50 -4
  263. package/src/utils/detectInterfaceType.js +4 -4
  264. package/src/utils/getIsFullscreen.js +3 -1
  265. package/src/utils/getSafeZoneInset.js +23 -7
  266. package/src/utils/getSafeZoneInset.test.js +18 -0
  267. package/src/utils/mapStateSync.js +3 -3
  268. package/src/utils/queryString.js +1 -1
  269. package/src/utils/stringToKebab.js +1 -1
  270. package/src/utils/toggleInertElements.js +37 -12
  271. package/webpack.dev.mjs +3 -4
  272. package/webpack.esm.mjs +8 -8
  273. package/webpack.umd.mjs +8 -8
  274. package/plugins/datasets/dist/esm/im-datasets-plugin.js +0 -1
  275. package/plugins/datasets/dist/esm/index.js +0 -2
  276. package/plugins/datasets/dist/umd/im-datasets-plugin.js +0 -1
  277. package/plugins/datasets/dist/umd/index.js +0 -2
  278. package/plugins/datasets/src/datasets.js +0 -60
  279. package/plugins/datasets/src/utils/bbox.js +0 -4
  280. package/plugins/draw-ml/dist/css/index.css +0 -1
  281. package/plugins/draw-ml/dist/esm/im-draw-ml-plugin.js +0 -1
  282. package/plugins/draw-ml/dist/esm/index.js +0 -2
  283. package/plugins/draw-ml/dist/umd/im-draw-ml-plugin.js +0 -1
  284. package/plugins/draw-ml/dist/umd/index.js +0 -2
  285. package/plugins/draw-ml/src/api/addFeature.js +0 -15
  286. package/plugins/draw-ml/src/api/newPolygon.js +0 -43
  287. package/plugins/draw-ml/src/draw.scss +0 -9
  288. package/plugins/draw-ml/src/events.js +0 -127
  289. package/plugins/draw-ml/src/manifest.js +0 -100
  290. package/plugins/draw-ml/src/modes/drawVertexMode.js +0 -275
  291. package/plugins/draw-ml/src/modes/editVertexMode.js +0 -426
  292. package/plugins/draw-ml/src/utils.js +0 -102
  293. package/plugins/frame/dist/esm/im-frame-plugin.js +0 -1
  294. package/plugins/frame/dist/esm/index.js +0 -2
  295. package/plugins/frame/dist/umd/im-frame-plugin.js +0 -1
  296. package/plugins/frame/dist/umd/index.js +0 -2
  297. package/plugins/map-styles/dist/esm/im-map-styles-plugin.js +0 -1
  298. package/plugins/map-styles/dist/esm/index.js +0 -2
  299. package/plugins/map-styles/dist/umd/im-map-styles-plugin.js +0 -1
  300. package/plugins/map-styles/dist/umd/index.js +0 -2
  301. package/plugins/scale-bar/dist/esm/im-scale-bar-plugin.js +0 -1
  302. package/plugins/scale-bar/dist/esm/index.js +0 -2
  303. package/plugins/scale-bar/dist/umd/im-scale-bar-plugin.js +0 -1
  304. package/plugins/scale-bar/dist/umd/index.js +0 -2
  305. package/plugins/search/src/utils/fetchDataset.js +0 -23
  306. package/plugins/use-location/dist/esm/im-use-location-plugin.js +0 -1
  307. package/plugins/use-location/dist/esm/index.js +0 -2
  308. package/plugins/use-location/dist/umd/im-use-location-plugin.js +0 -1
  309. package/plugins/use-location/dist/umd/index.js +0 -2
  310. package/providers/maplibre/dist/esm/im-maplibre-legacy-framework.js +0 -1
  311. package/providers/maplibre/dist/umd/im-maplibre-legacy-framework.js +0 -1
  312. package/providers/open-names/dist/esm/im-reverse-geocode.js +0 -2
  313. package/providers/open-names/dist/esm/index.js +0 -2
  314. package/providers/open-names/dist/umd/im-reverse-geocode.js +0 -2
  315. package/providers/open-names/dist/umd/index.js +0 -2
  316. /package/docs/architecture/{ARCHITECTURE_DIAGRAMS.md → architecture-diagrams.md} +0 -0
  317. /package/plugins/{datasets → beta/datasets}/dist/css/index.css +0 -0
  318. /package/plugins/{datasets/dist/esm/index.js.LICENSE.txt → beta/datasets/dist/esm/im-datasets-plugin.js.LICENSE.txt} +0 -0
  319. /package/plugins/{datasets/dist/umd → beta/datasets/dist/esm}/index.js.LICENSE.txt +0 -0
  320. /package/plugins/{draw-ml/dist/esm/index.js.LICENSE.txt → beta/datasets/dist/umd/im-datasets-plugin.js.LICENSE.txt} +0 -0
  321. /package/plugins/{draw-ml → beta/datasets}/dist/umd/index.js.LICENSE.txt +0 -0
  322. /package/plugins/{datasets → beta/datasets}/src/DatasetsInit.jsx +0 -0
  323. /package/plugins/{datasets → beta/datasets}/src/api/addDataset.js +0 -0
  324. /package/plugins/{datasets → beta/datasets}/src/api/hideDataset.js +0 -0
  325. /package/plugins/{datasets → beta/datasets}/src/api/hideFeatures.js +0 -0
  326. /package/plugins/{datasets → beta/datasets}/src/api/removeDataset.js +0 -0
  327. /package/plugins/{datasets → beta/datasets}/src/api/showDataset.js +0 -0
  328. /package/plugins/{datasets → beta/datasets}/src/api/showFeatures.js +0 -0
  329. /package/plugins/{datasets → beta/datasets}/src/datasets.scss +0 -0
  330. /package/plugins/{datasets → beta/datasets}/src/defaults.js +0 -0
  331. /package/plugins/{datasets → beta/datasets}/src/index.js +0 -0
  332. /package/plugins/{datasets → beta/datasets}/src/panels/Key.module.scss +0 -0
  333. /package/plugins/{datasets → beta/datasets}/src/panels/Layers.jsx +0 -0
  334. /package/plugins/{datasets → beta/datasets}/src/panels/Layers.module.scss +0 -0
  335. /package/plugins/{datasets → beta/datasets}/src/reducer.js +0 -0
  336. /package/plugins/{datasets → beta/datasets}/src/utils/debounce.js +0 -0
  337. /package/plugins/{datasets → beta/datasets}/src/utils/filters.js +0 -0
  338. /package/plugins/{draw-es → beta/draw-es}/src/DrawInit.jsx +0 -0
  339. /package/plugins/{draw-es → beta/draw-es}/src/api/addFeature.js +0 -0
  340. /package/plugins/{draw-es → beta/draw-es}/src/api/deleteFeature.js +0 -0
  341. /package/plugins/{draw-es → beta/draw-es}/src/api/editFeature.js +0 -0
  342. /package/plugins/{draw-es → beta/draw-es}/src/api/newPolygon.js +0 -0
  343. /package/plugins/{draw-es → beta/draw-es}/src/events.js +0 -0
  344. /package/plugins/{draw-es → beta/draw-es}/src/graphic.js +0 -0
  345. /package/plugins/{draw-es → beta/draw-es}/src/index.js +0 -0
  346. /package/plugins/{draw-es → beta/draw-es}/src/manifest.js +0 -0
  347. /package/plugins/{draw-es → beta/draw-es}/src/reducer.js +0 -0
  348. /package/plugins/{draw-es → beta/draw-es}/src/sketchViewModel.js +0 -0
  349. /package/plugins/{frame → beta/draw-ml}/dist/esm/index.js.LICENSE.txt +0 -0
  350. /package/plugins/{frame → beta/draw-ml}/dist/umd/index.js.LICENSE.txt +0 -0
  351. /package/plugins/{draw-ml → beta/draw-ml}/src/DrawInit.jsx +0 -0
  352. /package/plugins/{draw-ml → beta/draw-ml}/src/api/deleteFeature.js +0 -0
  353. /package/plugins/{draw-ml → beta/draw-ml}/src/index.js +0 -0
  354. /package/plugins/{draw-ml → beta/draw-ml}/src/modes/disabledMode.js +0 -0
  355. /package/plugins/{draw-ml/src → beta/draw-ml/src/utils}/snapHelpers.js +0 -0
  356. /package/plugins/{frame → beta/frame}/dist/css/index.css +0 -0
  357. /package/plugins/{map-styles → beta/frame}/dist/esm/index.js.LICENSE.txt +0 -0
  358. /package/plugins/{map-styles → beta/frame}/dist/umd/index.js.LICENSE.txt +0 -0
  359. /package/plugins/{frame → beta/frame}/package.json +0 -0
  360. /package/plugins/{frame → beta/frame}/src/FrameInit.jsx +0 -0
  361. /package/plugins/{frame → beta/frame}/src/api/addFrame.js +0 -0
  362. /package/plugins/{frame → beta/frame}/src/api/editFeature.js +0 -0
  363. /package/plugins/{frame → beta/frame}/src/config.js +0 -0
  364. /package/plugins/{frame → beta/frame}/src/frame.scss +0 -0
  365. /package/plugins/{frame → beta/frame}/src/index.js +0 -0
  366. /package/plugins/{frame → beta/frame}/src/manifest.js +0 -0
  367. /package/plugins/{frame → beta/frame}/src/reducer.js +0 -0
  368. /package/plugins/{frame → beta/frame}/src/utils.js +0 -0
  369. /package/plugins/{scale-bar → beta/map-styles}/dist/esm/index.js.LICENSE.txt +0 -0
  370. /package/plugins/{scale-bar → beta/map-styles}/dist/umd/index.js.LICENSE.txt +0 -0
  371. /package/plugins/{map-styles → beta/map-styles}/src/MapStylesInit.jsx +0 -0
  372. /package/plugins/{map-styles → beta/map-styles}/src/config.js +0 -0
  373. /package/plugins/{map-styles → beta/map-styles}/src/index.js +0 -0
  374. /package/plugins/{scale-bar → beta/scale-bar}/dist/css/index.css +0 -0
  375. /package/plugins/{use-location → beta/scale-bar}/dist/esm/index.js.LICENSE.txt +0 -0
  376. /package/plugins/{use-location → beta/scale-bar}/dist/umd/index.js.LICENSE.txt +0 -0
  377. /package/plugins/{scale-bar → beta/scale-bar}/package.json +0 -0
  378. /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.jsx +0 -0
  379. /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.test.jsx +0 -0
  380. /package/plugins/{scale-bar → beta/scale-bar}/src/index.js +0 -0
  381. /package/plugins/{scale-bar → beta/scale-bar}/src/index.test.js +0 -0
  382. /package/plugins/{scale-bar → beta/scale-bar}/src/manifest.js +0 -0
  383. /package/plugins/{scale-bar → beta/scale-bar}/src/scaleBar.scss +0 -0
  384. /package/plugins/{scale-bar → beta/scale-bar}/src/utils.js +0 -0
  385. /package/plugins/{scale-bar → beta/scale-bar}/src/utils.test.js +0 -0
  386. /package/{providers/open-names → plugins/beta/use-location}/dist/esm/index.js.LICENSE.txt +0 -0
  387. /package/{providers/open-names → plugins/beta/use-location}/dist/umd/index.js.LICENSE.txt +0 -0
  388. /package/plugins/{use-location → beta/use-location}/src/UseLocation.jsx +0 -0
  389. /package/plugins/{use-location → beta/use-location}/src/UseLocationInit.jsx +0 -0
  390. /package/plugins/{use-location → beta/use-location}/src/defaults.js +0 -0
  391. /package/plugins/{use-location → beta/use-location}/src/events.js +0 -0
  392. /package/plugins/{use-location → beta/use-location}/src/index.js +0 -0
  393. /package/plugins/{use-location → beta/use-location}/src/manifest.js +0 -0
  394. /package/plugins/{use-location → beta/use-location}/src/reducer.js +0 -0
  395. /package/providers/{esri → beta/esri}/src/appEvents.js +0 -0
  396. /package/providers/{esri → beta/esri}/src/defaults.js +0 -0
  397. /package/providers/{esri → beta/esri}/src/esriProvider.scss +0 -0
  398. /package/providers/{esri → beta/esri}/src/utils/coords.js +0 -0
  399. /package/providers/{esri → beta/esri}/src/utils/detectWebGL.js +0 -0
  400. /package/providers/{esri → beta/esri}/src/utils/esriFixes.js +0 -0
  401. /package/providers/{esri → beta/esri}/src/utils/query.js +0 -0
  402. /package/providers/{esri → beta/esri}/src/utils/spatial.js +0 -0
  403. /package/providers/{open-names → beta/open-names}/dist/esm/im-reverse-geocode.js.LICENSE.txt +0 -0
  404. /package/providers/{open-names/dist/umd/im-reverse-geocode.js.LICENSE.txt → beta/open-names/dist/esm/index.js.LICENSE.txt} +0 -0
  405. /package/providers/{open-names → beta/open-names}/src/index.js +0 -0
  406. /package/providers/{open-names → beta/open-names}/src/reverseGeocode.js +0 -0
  407. /package/providers/{open-names → beta/open-names}/src/utils/mapToLocationModel.js +0 -0
@@ -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
  }
@@ -111,9 +112,9 @@ function applyMapboxSnapPatches(colors) {
111
112
  return r
112
113
  }
113
114
 
114
- // Skip when disabled, clean up internal arrays to prevent memory accumulation
115
+ // Skip when disabled or zooming, clean up internal arrays to prevent memory accumulation
115
116
  proto.snapToClosestPoint = function(e) {
116
- if (!this.status) {
117
+ if (!this.status || this.map?._isZooming) {
117
118
  return
118
119
  }
119
120
  try {
@@ -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
@@ -138,6 +140,8 @@ function applyMapboxSnapPatches(colors) {
138
140
  function pollUntil(checkFn, onSuccess) {
139
141
  (function poll() {
140
142
  const result = checkFn()
143
+ // null signals to stop polling, falsy continues polling
144
+ if (result === null) return
141
145
  result ? onSuccess(result) : requestAnimationFrame(poll)
142
146
  })()
143
147
  }
@@ -265,7 +269,7 @@ export function initMapLibreSnap(map, draw, snapOptions = {}) {
265
269
  // Handle style changes - re-patch source and ensure snap layer exists
266
270
  map.on('style.load', () => {
267
271
  pollUntil(
268
- () => map.getSource('mapbox-gl-draw-hot'),
272
+ () => map._removed ? null : map.getSource('mapbox-gl-draw-hot'),
269
273
  (source) => {
270
274
  patchSourceData(source)
271
275
 
@@ -293,24 +297,26 @@ export function initMapLibreSnap(map, draw, snapOptions = {}) {
293
297
  )
294
298
  })
295
299
 
296
- // Hide snap indicator during zoom
300
+ // Suppress snap processing during zoom (indicator freezes in place)
297
301
  map.on('zoomstart', () => {
298
- if (map.getLayer(SNAP_HELPER_LAYER)) {
299
- map.setLayoutProperty(SNAP_HELPER_LAYER, 'visibility', 'none')
300
- }
302
+ map._isZooming = true
301
303
  })
302
304
 
303
305
  map.on('zoomend', () => {
304
- // Only show indicator if snap is enabled
305
- const snap = map._snapInstance
306
- if (map.getLayer(SNAP_HELPER_LAYER) && snap?.status) {
307
- map.setLayoutProperty(SNAP_HELPER_LAYER, 'visibility', 'visible')
306
+ map._isZooming = false
307
+ // Force hide then re-show to reset indicator at new zoom level (Safari fix)
308
+ if (map.getLayer(SNAP_HELPER_LAYER)) {
309
+ map.setLayoutProperty(SNAP_HELPER_LAYER, 'visibility', 'none')
310
+ const snap = map._snapInstance
311
+ if (snap?.status) {
312
+ map.setLayoutProperty(SNAP_HELPER_LAYER, 'visibility', 'visible')
313
+ }
308
314
  }
309
315
  })
310
316
 
311
317
  // Initial setup - poll until draw source exists
312
318
  pollUntil(
313
- () => map.getSource('mapbox-gl-draw-hot'),
319
+ () => map._removed ? null : map.getSource('mapbox-gl-draw-hot'),
314
320
  createSnap
315
321
  )
316
322
  }
@@ -0,0 +1,497 @@
1
+ import createVertex from '../../../../../node_modules/@mapbox/mapbox-gl-draw/src/lib/create_vertex.js'
2
+
3
+ import {
4
+ getSnapInstance,
5
+ isSnapActive,
6
+ isSnapEnabled,
7
+ getSnapLngLat,
8
+ triggerSnapAtPoint,
9
+ triggerSnapAtCenter,
10
+ createSnappedEvent,
11
+ createSnappedClickEvent
12
+ } from '../utils/snapHelpers.js'
13
+
14
+ /**
15
+ * Factory function to create a draw mode for either polygons or lines.
16
+ * Reduces duplication by sharing common event handling, snap detection, etc.
17
+ *
18
+ * @param {Object} ParentMode - DrawPolygon or DrawLineString from mapbox-gl-draw
19
+ * @param {Object} config - Configuration for the mode
20
+ * @param {string} config.featureProp - Property name on state ('polygon' or 'line')
21
+ * @param {string} config.geometryType - 'Polygon' or 'LineString'
22
+ * @param {Function} config.getCoords - Function to get coordinates from feature
23
+ * @param {Function} config.validateClick - Validation function for clicks
24
+ * @param {Function} config.createVertices - Function to create vertex display features
25
+ */
26
+ export const createDrawMode = (ParentMode, config) => {
27
+ const {
28
+ featureProp,
29
+ geometryType,
30
+ getCoords,
31
+ validateClick,
32
+ createVertices,
33
+ excludeFeatureIdFromSetup = false,
34
+ finishOnInvalidClick = false // For lines: finish when clicking same spot (like double-click)
35
+ } = config
36
+
37
+ const getFeature = (state) => state[featureProp]
38
+
39
+ return {
40
+ ...ParentMode,
41
+
42
+ onSetup(options) {
43
+ const { map } = this
44
+
45
+ // Some parent modes (DrawLineString) interpret featureId as "continue existing"
46
+ // rather than "use this ID for new feature"
47
+ const parentOptions = excludeFeatureIdFromSetup
48
+ ? { ...options, featureId: undefined }
49
+ : options
50
+
51
+ const state = {
52
+ ...ParentMode.onSetup.call(this, parentOptions),
53
+ ...options
54
+ }
55
+
56
+ // Add initial props
57
+ state[featureProp].properties = options.properties
58
+
59
+ const { container, interfaceType, vertexMarkerId } = state
60
+ const vertexMarker = container.querySelector(`#${vertexMarkerId}`)
61
+ vertexMarker.style.display = ['touch', 'keyboard'].includes(interfaceType) ? 'block' : 'none'
62
+ state.vertexMarker = vertexMarker
63
+
64
+ // Bind all handlers once
65
+ const bind = (name, fn) => (this[name] = fn.bind(this, state))
66
+ const handlers = {
67
+ keydownHandler: this.onKeydown,
68
+ keyupHandler: this.onKeyup,
69
+ focusHandler: this.onFocus,
70
+ blurHandler: this.onBlur,
71
+ createHandler: this.onCreate,
72
+ moveHandler: this.onMove,
73
+ pointerdownHandler: this.onPointerdown,
74
+ pointermoveHandler: this.onPointermove,
75
+ pointerupHandler: this.onPointerup,
76
+ vertexButtonClickHandler: this.onVertexButtonClick,
77
+ undoHandler: this.onUndo
78
+ }
79
+ Object.entries(handlers).forEach(([k, fn]) => bind(k, fn))
80
+
81
+ // Register events
82
+ this._listeners = [
83
+ [window, 'keydown', this.keydownHandler],
84
+ [window, 'keyup', this.keyupHandler],
85
+ [window, 'click', this.vertexButtonClickHandler],
86
+ [container, 'focus', this.focusHandler],
87
+ [container, 'blur', this.blurHandler],
88
+ [container, 'pointermove', this.pointermoveHandler],
89
+ [container, 'pointerup', this.pointerupHandler],
90
+ [map, 'pointerdown', this.pointerdownHandler],
91
+ [map, 'draw.create', this.createHandler],
92
+ [map, 'move', this.moveHandler],
93
+ [map, 'draw.undo', this.undoHandler]
94
+ ]
95
+ this._listeners.forEach(([t, e, h]) => t.addEventListener ? t.addEventListener(e, h) : t.on(e, h))
96
+
97
+ return state
98
+ },
99
+
100
+ onClick(state, e) {
101
+ // Skip non-primary clicks, undo operations, or clicks outside canvas
102
+ if (e.originalEvent.button > 0 || this.map._undoInProgress || e.originalEvent.target !== this.map.getCanvas()) {
103
+ return
104
+ }
105
+ const snap = getSnapInstance(this.map)
106
+ if (isSnapEnabled(state) && isSnapActive(snap)) {
107
+ e = createSnappedEvent(e, snap)
108
+ } else {
109
+ const coords = getCoords(getFeature(state))
110
+ if (coords.length > 0) {
111
+ coords[coords.length - 1] = [e.lngLat.lng, e.lngLat.lat]
112
+ }
113
+ }
114
+ const coordsBefore = getCoords(getFeature(state)).length
115
+ ParentMode.onClick.call(this, state, e)
116
+ // Push undo if a vertex was added
117
+ if (getCoords(getFeature(state)).length > coordsBefore) {
118
+ this.pushDrawUndo(state)
119
+ }
120
+ },
121
+
122
+ onTap() {
123
+ return
124
+ },
125
+
126
+ doClick(state) {
127
+ // Skip during undo operation
128
+ if (this.map._undoInProgress) {
129
+ return
130
+ }
131
+
132
+ const feature = getFeature(state)
133
+ const coords = getCoords(feature)
134
+ this.dispatchVertexChange(coords)
135
+
136
+ if (!validateClick(feature)) {
137
+ // For lines: clicking same spot (like double-click) should finish the line
138
+ if (finishOnInvalidClick && coords.length > 1) {
139
+ coords.pop()
140
+ this.map.fire('draw.create', {
141
+ features: [feature.toGeoJSON()]
142
+ })
143
+ this.changeMode('simple_select', { featureIds: [feature.id] })
144
+ }
145
+ return
146
+ }
147
+
148
+ const snap = getSnapInstance(this.map)
149
+ const snappedEvent = isSnapEnabled(state) && createSnappedClickEvent(this.map, snap)
150
+ const coordsBefore = coords.length
151
+
152
+ if (snappedEvent) {
153
+ ParentMode.onClick.call(this, state, snappedEvent)
154
+ this._ctx.store.render()
155
+ } else {
156
+ this._simulateMouse('click', ParentMode.onClick, state)
157
+ }
158
+
159
+ // Push undo if a vertex was added
160
+ if (getCoords(getFeature(state)).length > coordsBefore) {
161
+ this.pushDrawUndo(state)
162
+ }
163
+ },
164
+
165
+ dispatchVertexChange(coords) {
166
+ this.map.fire('draw.vertexchange', {
167
+ numVertecies: coords.length
168
+ })
169
+ },
170
+
171
+ /**
172
+ * Push an undo operation for the last added vertex
173
+ */
174
+ pushDrawUndo(state) {
175
+ const undoStack = this.map._undoStack
176
+ // Don't push during undo operations
177
+ if (!undoStack || this.map._undoInProgress) {
178
+ return
179
+ }
180
+ undoStack.push({
181
+ type: 'draw_vertex',
182
+ geometryType,
183
+ featureId: getFeature(state).id
184
+ })
185
+ },
186
+
187
+ /**
188
+ * Undo the last added vertex during drawing
189
+ */
190
+ undoVertex(state) {
191
+ const feature = getFeature(state)
192
+ const coords = getCoords(feature)
193
+
194
+ if (coords.length < 2) {
195
+ return false
196
+ }
197
+
198
+ // Undoing last vertex requires reinitializing the feature
199
+ if (coords.length === 2) {
200
+ return this._reinitializeFeature(state, feature)
201
+ }
202
+
203
+ this._removeLastVertex(state, feature, coords)
204
+ return true
205
+ },
206
+
207
+ /**
208
+ * Reinitialize feature when undoing to 0 vertices
209
+ * For Polygon: reinitialize in place
210
+ * For LineString: restart the draw mode with fresh state
211
+ */
212
+ _reinitializeFeature(state, feature) {
213
+ const featureId = feature.id
214
+ this._ctx.store.delete([featureId])
215
+
216
+ // LineString: restart the draw mode with fresh state but same feature ID
217
+ if (geometryType === 'LineString') {
218
+ const undoStack = this.map._undoStack
219
+ if (undoStack) {
220
+ undoStack.clear()
221
+ }
222
+ // Restart draw with same options (excludeFeatureIdFromSetup prevents "continue" mode)
223
+ this._ctx.api.changeMode('draw_line', {
224
+ featureId,
225
+ container: state.container,
226
+ interfaceType: state.interfaceType,
227
+ vertexMarkerId: state.vertexMarkerId,
228
+ addVertexButtonId: state.addVertexButtonId,
229
+ getSnapEnabled: state.getSnapEnabled,
230
+ properties: state.properties
231
+ })
232
+ return true
233
+ }
234
+
235
+ // Polygon: reinitialize in place
236
+ const center = this.map.getCenter()
237
+ const initialCoords = [[center.lng, center.lat], [center.lng, center.lat]]
238
+ const newFeature = this.newFeature({
239
+ type: 'Feature',
240
+ properties: state.properties || {},
241
+ geometry: {
242
+ type: geometryType,
243
+ coordinates: [initialCoords]
244
+ }
245
+ })
246
+ newFeature.id = featureId
247
+ this._ctx.store.add(newFeature)
248
+
249
+ state[featureProp] = newFeature
250
+ state.currentVertexPosition = 0
251
+
252
+ this._ctx.store.render()
253
+ this._simulateMouse('mousemove', ParentMode.onMouseMove, state)
254
+ this._ctx.store.render()
255
+
256
+ this.dispatchVertexChange(initialCoords)
257
+ return true
258
+ },
259
+
260
+ /**
261
+ * Remove the last committed vertex and update rubber band
262
+ */
263
+ _removeLastVertex(state, feature, coords) {
264
+ // Structure during drawing: [v1, v2, ..., vN, rubber_band]
265
+ const ring = geometryType === 'Polygon' ? feature.coordinates[0] : coords
266
+ ring.splice(ring.length - 2, 1)
267
+
268
+ // Snap rubber band to new last vertex position
269
+ const newLastVertex = ring[ring.length - 2]
270
+ if (newLastVertex) {
271
+ ring[ring.length - 1] = [...newLastVertex]
272
+ }
273
+
274
+ // Keep parent mode's vertex counter in sync (min 1 for rubber band)
275
+ state.currentVertexPosition = Math.max(1, state.currentVertexPosition - 1)
276
+
277
+ this._ctx.store.render()
278
+ this._updateRubberBand(state, getCoords(feature))
279
+ },
280
+
281
+ /**
282
+ * Update rubber band position based on interface type
283
+ */
284
+ _updateRubberBand(state, coords) {
285
+ if (['touch', 'keyboard'].includes(state.interfaceType)) {
286
+ // Touch/keyboard: move to map center for add point to work
287
+ this._simulateMouse('mousemove', ParentMode.onMouseMove, state)
288
+ this._ctx.store.render()
289
+ } else {
290
+ // Mouse: keep rubber band at current position
291
+ const rubberBandIndex = geometryType === 'Polygon' ? coords.length - 2 : coords.length - 1
292
+ const rubberBandPos = coords[rubberBandIndex]
293
+ if (rubberBandPos) {
294
+ const lngLat = { lng: rubberBandPos[0], lat: rubberBandPos[1] }
295
+ const point = this.map.project(lngLat)
296
+ ParentMode.onMouseMove.call(this, state, {
297
+ lngLat,
298
+ point,
299
+ originalEvent: new MouseEvent('mousemove', { clientX: point.x, clientY: point.y })
300
+ })
301
+ this._ctx.store.render()
302
+ }
303
+ }
304
+ this.dispatchVertexChange(coords)
305
+ },
306
+
307
+ /**
308
+ * Handle draw.undo event
309
+ */
310
+ onUndo(state, e) {
311
+ if (e.operation?.type === 'draw_vertex') {
312
+ this.undoVertex(state)
313
+ }
314
+ },
315
+
316
+ _simulateMouse(type, fn, state) {
317
+ const { map } = this
318
+ const center = map.getCenter()
319
+ const point = map.project(center)
320
+ fn.call(this, state, {
321
+ lngLat: center,
322
+ point,
323
+ originalEvent: new MouseEvent(type, {
324
+ clientX: point.x,
325
+ clientY: point.y,
326
+ bubbles: true,
327
+ cancelable: true
328
+ })
329
+ })
330
+ this._ctx.store.render()
331
+
332
+ this.map.fire('draw.geometrychange', state.polygon || state.line)
333
+ },
334
+
335
+ _setInterface(state, type, show = true) {
336
+ state.interfaceType = type
337
+ if (show) {
338
+ state.vertexMarker.style.display = 'block'
339
+ }
340
+ },
341
+
342
+ onCreate(state, e) {
343
+ const draw = this._ctx.api
344
+ const feature = e.features[0]
345
+ draw.delete(feature.id)
346
+ feature.id = state.featureId
347
+ draw.add(feature, { userProperties: true })
348
+ },
349
+
350
+ onVertexButtonClick(state, e) {
351
+ // Only trigger for the specific add vertex button, and skip during undo
352
+ if (state.addVertexButtonId && !this.map._undoInProgress && e.target.closest(`#${state.addVertexButtonId}`)) {
353
+ this.doClick(state)
354
+ }
355
+ },
356
+
357
+ onTouchStart(state, e) {
358
+ this._setInterface(state, 'touch')
359
+ this.onMove(state, e)
360
+ },
361
+
362
+ onTouchEnd(state, e) {
363
+ this._setInterface(state, 'touch')
364
+ this.onMove(state, e)
365
+ },
366
+
367
+ onKeydown(state, e) {
368
+ // Undo with Cmd/Ctrl+Z (works without viewport focus, but not in input fields)
369
+ if (e.key === 'z' && (e.metaKey || e.ctrlKey) && !e.shiftKey) {
370
+ const tag = document.activeElement?.tagName
371
+ if (tag === 'INPUT' || tag === 'TEXTAREA') {
372
+ return
373
+ }
374
+ e.preventDefault()
375
+ e.stopPropagation()
376
+ const undoStack = this.map._undoStack
377
+ if (undoStack && undoStack.length > 0) {
378
+ const operation = undoStack.pop()
379
+ if (operation?.type === 'draw_vertex') {
380
+ // Set flag to prevent click interference during undo
381
+ this.map._undoInProgress = true
382
+ setTimeout(() => { this.map._undoInProgress = false }, 100)
383
+ this.undoVertex(state)
384
+ }
385
+ }
386
+ return
387
+ }
388
+ if (document.activeElement !== state.container) {
389
+ return
390
+ }
391
+ if (e.key === 'Escape') {
392
+ return e.preventDefault()
393
+ }
394
+ if (e.key === 'Enter') {
395
+ state.isActive = true
396
+ }
397
+ this._setInterface(state, 'keyboard')
398
+ this.onMove(state, e)
399
+ },
400
+
401
+ onKeyup(state, e) {
402
+ if (e.key === 'Escape' || document.activeElement !== state.container) {
403
+ return
404
+ }
405
+ this._setInterface(state, 'keyboard')
406
+ this.onMove(state, e)
407
+ if (e.key === 'Enter' && state.isActive) {
408
+ this.doClick(state)
409
+ }
410
+ },
411
+
412
+ onFocus(state) {
413
+ state.vertexMarker.style.display = ['touch', 'keyboard'].includes(state.interfaceType) ? 'block' : 'none'
414
+ },
415
+
416
+ onBlur(state, e) {
417
+ if (e.target !== state.container) {
418
+ state.vertexMarker.style.display = 'none'
419
+ }
420
+ },
421
+
422
+ onMouseMove(state, e) {
423
+ if (isSnapEnabled(state)) {
424
+ const snap = getSnapInstance(this.map)
425
+ triggerSnapAtPoint(snap, this.map, e.point)
426
+
427
+ const snappedLngLat = getSnapLngLat(snap)
428
+ if (snappedLngLat) {
429
+ e = { ...e, lngLat: snappedLngLat }
430
+ }
431
+ }
432
+
433
+ this.map.fire('draw.geometrychange', state.polygon || state.line)
434
+
435
+ ParentMode.onMouseMove.call(this, state, e)
436
+ },
437
+
438
+ onMove(state) {
439
+ if (['touch', 'keyboard'].includes(state.interfaceType)) {
440
+ if (isSnapEnabled(state)) {
441
+ triggerSnapAtCenter(getSnapInstance(this.map), this.map)
442
+ }
443
+
444
+ const snap = getSnapInstance(this.map)
445
+ const snappedLngLat = isSnapEnabled(state) && getSnapLngLat(snap)
446
+
447
+ if (snappedLngLat) {
448
+ const point = this.map.project([snappedLngLat.lng, snappedLngLat.lat])
449
+ ParentMode.onMouseMove.call(this, state, {
450
+ lngLat: snappedLngLat,
451
+ point,
452
+ originalEvent: new MouseEvent('mousemove', {
453
+ clientX: point.x,
454
+ clientY: point.y,
455
+ bubbles: true,
456
+ cancelable: true
457
+ })
458
+ })
459
+ this._ctx.store.render()
460
+ } else {
461
+ this._simulateMouse('mousemove', ParentMode.onMouseMove, state)
462
+ }
463
+ }
464
+ },
465
+
466
+ onPointerdown(state, e) {
467
+ if (e.pointerType !== 'touch') {
468
+ this._setInterface(state, 'pointer', false)
469
+ }
470
+ },
471
+
472
+ onPointermove(state, e) {
473
+ if (e.pointerType !== 'touch') {
474
+ state.vertexMarker.style.display = 'none'
475
+ }
476
+ },
477
+
478
+ onPointerup(state) {
479
+ this.dispatchVertexChange(getCoords(getFeature(state)))
480
+ },
481
+
482
+ toDisplayFeatures(state, geojson, display) {
483
+ ParentMode.toDisplayFeatures.call(this, state, geojson, display)
484
+
485
+ const feature = getFeature(state)
486
+ if (geojson.geometry.type === geometryType && geojson.id === feature.id) {
487
+ createVertices(geojson, display, createVertex)
488
+ }
489
+ },
490
+
491
+ onStop(state) {
492
+ ParentMode.onStop.call(this, state)
493
+ this._listeners.forEach(([t, e, h]) => t.removeEventListener ? t.removeEventListener(e, h) : t.off(e, h))
494
+ state.vertexMarker.style.display = 'none'
495
+ }
496
+ }
497
+ }
@@ -0,0 +1,18 @@
1
+ import DrawLineString from '../../../../../node_modules/@mapbox/mapbox-gl-draw/src/modes/draw_line_string.js'
2
+ import { isValidLineClick } from '../utils/spatial.js'
3
+ import { createDrawMode } from './createDrawMode.js'
4
+
5
+ export const DrawLineMode = createDrawMode(DrawLineString, {
6
+ featureProp: 'line',
7
+ geometryType: 'LineString',
8
+ getCoords: (feature) => feature.coordinates,
9
+ validateClick: (feature) => isValidLineClick(feature.coordinates),
10
+ excludeFeatureIdFromSetup: true, // DrawLineString interprets featureId as "continue existing"
11
+ finishOnInvalidClick: true, // Clicking same spot (like double-click) finishes the line
12
+ createVertices: (geojson, display, createVertex) => {
13
+ const coords = geojson.geometry.coordinates
14
+ for (let i = 1; i < coords.length - 1; i++) {
15
+ display(createVertex(geojson.id, coords[i], `${i}`))
16
+ }
17
+ }
18
+ })
@@ -0,0 +1,16 @@
1
+ import DrawPolygon from '../../../../../node_modules/@mapbox/mapbox-gl-draw/src/modes/draw_polygon.js'
2
+ import { isValidClick } from '../utils/spatial.js'
3
+ import { createDrawMode } from './createDrawMode.js'
4
+
5
+ export const DrawPolygonMode = createDrawMode(DrawPolygon, {
6
+ featureProp: 'polygon',
7
+ geometryType: 'Polygon',
8
+ getCoords: (feature) => feature.coordinates[0],
9
+ validateClick: (feature) => isValidClick(feature.coordinates),
10
+ createVertices: (geojson, display, createVertex) => {
11
+ const ring = geojson.geometry.coordinates[0]
12
+ for (let i = 1; i < ring.length - 2; i++) {
13
+ display(createVertex(geojson.id, ring[i], `0.${i}`))
14
+ }
15
+ }
16
+ })