@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
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Pure geometry helper functions for multi-ring/multi-part geometry support.
3
+ * These functions handle coordinate transformations between flat arrays and
4
+ * hierarchical GeoJSON structures for Polygon, MultiPolygon, LineString, and MultiLineString.
5
+ */
6
+
7
+ /**
8
+ * Get flat coordinates array from feature for all geometry types.
9
+ * Flattens all rings/parts into a single array for unified vertex navigation.
10
+ *
11
+ * @param {Object} feature - GeoJSON geometry object
12
+ * @returns {Array<[number, number]>} Flat array of all coordinates
13
+ */
14
+ export const getCoords = (feature) => {
15
+ if (!feature?.coordinates) return []
16
+ switch (feature.type) {
17
+ case 'LineString':
18
+ return feature.coordinates
19
+ case 'Polygon':
20
+ return feature.coordinates.flat(1)
21
+ case 'MultiLineString':
22
+ return feature.coordinates.flat(1)
23
+ case 'MultiPolygon':
24
+ return feature.coordinates.flat(2)
25
+ default:
26
+ return []
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Get segment metadata for multi-ring/multi-part geometries.
32
+ * Each segment represents a ring (for Polygon) or part (for Multi*).
33
+ *
34
+ * @param {Object} feature - GeoJSON geometry object
35
+ * @returns {Array<{start: number, length: number, path: number[], closed: boolean}>}
36
+ * Array of segment metadata objects where:
37
+ * - start: Starting index in flat coordinates array
38
+ * - length: Number of coordinates in this segment
39
+ * - path: Hierarchical path indices to reach this segment in GeoJSON
40
+ * - closed: Whether this segment is closed (true for Polygon rings)
41
+ */
42
+ export const getRingSegments = (feature) => {
43
+ if (!feature?.coordinates) return []
44
+ const segments = []
45
+ let start = 0
46
+
47
+ switch (feature.type) {
48
+ case 'LineString':
49
+ segments.push({ start: 0, length: feature.coordinates.length, path: [], closed: false })
50
+ break
51
+ case 'Polygon':
52
+ feature.coordinates.forEach((ring, ringIdx) => {
53
+ segments.push({ start, length: ring.length, path: [ringIdx], closed: true })
54
+ start += ring.length
55
+ })
56
+ break
57
+ case 'MultiLineString':
58
+ feature.coordinates.forEach((line, lineIdx) => {
59
+ segments.push({ start, length: line.length, path: [lineIdx], closed: false })
60
+ start += line.length
61
+ })
62
+ break
63
+ case 'MultiPolygon':
64
+ feature.coordinates.forEach((polygon, polyIdx) => {
65
+ polygon.forEach((ring, ringIdx) => {
66
+ segments.push({ start, length: ring.length, path: [polyIdx, ringIdx], closed: true })
67
+ start += ring.length
68
+ })
69
+ })
70
+ break
71
+ default:
72
+ break
73
+ }
74
+
75
+ return segments
76
+ }
77
+
78
+ /**
79
+ * Find which segment a flat vertex index belongs to.
80
+ *
81
+ * @param {Array} segments - Array of segment metadata from getRingSegments
82
+ * @param {number} flatIdx - Flat vertex index
83
+ * @returns {{segment: Object, localIdx: number}|null}
84
+ * Object with segment metadata and local index within that segment, or null if not found
85
+ */
86
+ export const getSegmentForIndex = (segments, flatIdx) => {
87
+ for (const seg of segments) {
88
+ if (flatIdx >= seg.start && flatIdx < seg.start + seg.length) {
89
+ return { segment: seg, localIdx: flatIdx - seg.start }
90
+ }
91
+ }
92
+ return null
93
+ }
94
+
95
+ /**
96
+ * Get modifiable coordinate array at a specific hierarchical path.
97
+ * Returns a reference to the actual coordinate array in the GeoJSON structure.
98
+ *
99
+ * @param {Object} geojson - Full GeoJSON feature object
100
+ * @param {number[]} path - Hierarchical path indices (e.g., [0] for first ring, [1, 0] for second polygon's first ring)
101
+ * @returns {Array<[number, number]>} Reference to coordinate array at path
102
+ */
103
+ export const getModifiableCoords = (geojson, path) => {
104
+ let coords = geojson.geometry.coordinates
105
+ for (const idx of path) {
106
+ coords = coords[idx]
107
+ }
108
+ return coords
109
+ }
110
+
111
+ /**
112
+ * Convert mapbox-gl-draw coord_path string to flat vertex index.
113
+ * coord_path format: "ringIdx.vertexIdx" for Polygon, "polyIdx.ringIdx.vertexIdx" for MultiPolygon, etc.
114
+ *
115
+ * @param {Object} feature - GeoJSON geometry object
116
+ * @param {string} coordPath - coord_path string from mapbox-gl-draw
117
+ * @returns {number} Flat vertex index in the unified coordinate array
118
+ */
119
+ export const coordPathToFlatIndex = (feature, coordPath) => {
120
+ const parts = coordPath.split('.').map(Number)
121
+ const segments = getRingSegments(feature)
122
+
123
+ // Match coord_path to segment
124
+ for (const seg of segments) {
125
+ // Check if path matches (compare all but last element which is the local vertex index)
126
+ const pathMatches = seg.path.every((val, idx) => val === parts[idx])
127
+ if (pathMatches && parts.length === seg.path.length + 1) {
128
+ const localIdx = parts[parts.length - 1]
129
+ return seg.start + localIdx
130
+ }
131
+ }
132
+
133
+ // Fallback: just use the last number (works for simple geometries)
134
+ return parts[parts.length - 1]
135
+ }
@@ -0,0 +1,2 @@
1
+ export const scalePoint = (point, scale) => ({ x: point.x * scale, y: point.y * scale })
2
+ export const isOnSVG = (el) => el instanceof window.SVGElement || el.ownerSVGElement
@@ -0,0 +1,134 @@
1
+ import {
2
+ getSnapInstance, isSnapEnabled, triggerSnapAtPoint, getSnapLngLat,
3
+ clearSnapState, clearSnapIndicator
4
+ } from '../../utils/snapHelpers.js'
5
+ import { coordPathToFlatIndex } from './geometryHelpers.js'
6
+ import { isOnSVG } from './helpers.js'
7
+
8
+ const touchVertexTarget = `
9
+ <svg width='48' height='48' viewBox='0 0 48 48' fill-rule='evenodd' style='display:none;position:absolute;top:50%;left:50%;margin:24px 0 0 -24px' class='touch-vertex-target' data-touch-vertex-target>
10
+ <circle cx='24' cy='24' r='24' fill='currentColor'/>
11
+ <path d="M37.543 25H34a1 1 0 1 1 0-2h3.629l-.836-.837a1 1 0 0 1 1.414-1.414l2.5 2.501A1 1 0 0 1 41 24a1 1 0 0 1-.487.858l-2.306 2.306a1 1 0 0 1-1.414-1.414l.75-.75zM23 10.414l-.793.793a1 1 0 0 1-1.414-1.414l2.5-2.5C23.481 7.105 23.734 7 24 7s.519.105.707.293l2.5 2.5a1 1 0 0 1-1.414 1.414L25 10.414V14a1 1 0 1 1-2 0v-3.586zM7 24a1 1 0 0 1 .293-.75l2.5-2.501a1 1 0 0 1 1.414 1.414l-.836.837H14a1 1 0 1 1 0 2h-3.543l.75.75a1 1 0 0 1-1.414 1.414l-2.306-2.306A1 1 0 0 1 7 24zm16.293 16.707l-2.5-2.5a1 1 0 0 1 1.414-1.414l.793.793V34a1 1 0 1 1 2 0v3.586l.793-.793a1 1 0 0 1 1.414 1.414l-2.5 2.5c-.188.188-.441.293-.707.293s-.519-.105-.707-.293zM24 20c2.208 0 4 1.792 4 4s-1.792 4-4 4-4-1.792-4-4 1.792-4 4-4z" fill="#fff"/>
12
+ </svg>
13
+ `
14
+
15
+ export const touchHandlers = {
16
+ addTouchVertexTarget(state) {
17
+ let el = state.container.querySelector('[data-touch-vertex-target]')
18
+ if (!el) {
19
+ state.container.insertAdjacentHTML('beforeend', touchVertexTarget)
20
+ el = state.container.querySelector('[data-touch-vertex-target]')
21
+ }
22
+ state.touchVertexTarget = el
23
+ },
24
+
25
+ updateTouchVertexTarget(state, point) {
26
+ if (point && state.interfaceType === 'touch' && state.selectedVertexIndex >= 0) {
27
+ Object.assign(state.touchVertexTarget.style, { display: 'block', top: `${point.y}px`, left: `${point.x}px` })
28
+ } else {
29
+ state.touchVertexTarget.style.display = 'none'
30
+ }
31
+ },
32
+
33
+ hideTouchVertexIndicator(state) {
34
+ state.touchVertexTarget.style.display = 'none'
35
+ },
36
+
37
+ onPointerevent(state, e) {
38
+ state.interfaceType = e.pointerType === 'touch' ? 'touch' : 'pointer'
39
+ state.isPanEnabled = true
40
+ if (e.pointerType === 'touch' && e.type === 'pointermove' && !isOnSVG(e.target.parentNode) && !state._ignorePointermoveDeselect) {
41
+ this.changeMode(state, { selectedVertexIndex: -1, selectedVertexType: null, coordPath: null })
42
+ }
43
+ },
44
+
45
+ // Empty stubs required by DirectSelect
46
+ onTouchStart() {},
47
+ onTouchMove() {},
48
+ onTouchEnd() {},
49
+
50
+ onTouchend(state) {
51
+ clearSnapState(getSnapInstance(this.map))
52
+ if (state?.featureId) {
53
+ this.syncVertices(state)
54
+
55
+ // Push undo for the move if touch actually moved
56
+ if (state._touchMoved && state._moveStartPosition && state._moveStartIndex !== undefined) {
57
+ this.pushUndo({
58
+ type: 'move_vertex',
59
+ featureId: state.featureId,
60
+ vertexIndex: state._moveStartIndex,
61
+ previousPosition: state._moveStartPosition
62
+ })
63
+ }
64
+ state._moveStartPosition = null
65
+ state._moveStartIndex = undefined
66
+ state._touchMoved = false
67
+ }
68
+ },
69
+
70
+ onTap(state, e) {
71
+ // Hide snap indicator on any tap
72
+ const snap = getSnapInstance(this.map)
73
+ if (snap) {
74
+ clearSnapIndicator(snap, this.map)
75
+ }
76
+
77
+ const meta = e.featureTarget?.properties.meta
78
+ const coordPath = e.featureTarget?.properties.coord_path
79
+
80
+ if (meta === 'vertex') {
81
+ const feature = this.getFeature(state.featureId)
82
+ const idx = coordPathToFlatIndex(feature, coordPath)
83
+ this.changeMode(state, {
84
+ selectedVertexIndex: idx,
85
+ selectedVertexType: 'vertex',
86
+ coordPath
87
+ })
88
+ } else if (meta === 'midpoint') {
89
+ this.insertVertex({ ...state, selectedVertexIndex: this.getVertexIndexFromMidpoint(state, coordPath), selectedVertexType: 'midpoint' })
90
+ } else {
91
+ this.clickNoTarget(state)
92
+ }
93
+ },
94
+
95
+ onTouchstart(state, e) {
96
+ clearSnapState(getSnapInstance(this.map))
97
+ const vertex = state.vertecies?.[state.selectedVertexIndex]
98
+ if (!vertex || !isOnSVG(e.target.parentNode)) {
99
+ return
100
+ }
101
+
102
+ // Save starting position for undo
103
+ state._moveStartPosition = [...vertex]
104
+ state._moveStartIndex = state.selectedVertexIndex
105
+ state._touchMoved = false
106
+
107
+ const touch = { x: e.touches[0].clientX, y: e.touches[0].clientY }
108
+ const style = window.getComputedStyle(state.touchVertexTarget)
109
+ state.deltaTarget = { x: touch.x - Number.parseFloat(style.left), y: touch.y - Number.parseFloat(style.top) }
110
+ const vertexPt = this.map.project(vertex)
111
+ state.deltaVertex = { x: (touch.x / state.scale) - vertexPt.x, y: (touch.y / state.scale) - vertexPt.y }
112
+ },
113
+
114
+ onTouchmove(state, e) {
115
+ if (state.selectedVertexIndex < 0 || !isOnSVG(e.target.parentNode)) {
116
+ return
117
+ }
118
+
119
+ state._touchMoved = true
120
+
121
+ const touch = { x: e.touches[0].clientX, y: e.touches[0].clientY }
122
+ const screenPt = { x: (touch.x / state.scale) - state.deltaVertex.x, y: (touch.y / state.scale) - state.deltaVertex.y }
123
+
124
+ let finalCoord = this.map.unproject(screenPt)
125
+ if (isSnapEnabled(state)) {
126
+ const snap = getSnapInstance(this.map)
127
+ triggerSnapAtPoint(snap, this.map, screenPt)
128
+ finalCoord = getSnapLngLat(snap) || finalCoord
129
+ }
130
+
131
+ this.moveVertex(state, finalCoord)
132
+ this.updateTouchVertexTarget(state, { x: touch.x - state.deltaTarget.x, y: touch.y - state.deltaTarget.y })
133
+ }
134
+ }
@@ -0,0 +1,133 @@
1
+ import {
2
+ getRingSegments,
3
+ getSegmentForIndex,
4
+ getModifiableCoords
5
+ } from './geometryHelpers.js'
6
+ import { scalePoint } from './helpers.js'
7
+
8
+ export const undoHandlers = {
9
+ // Fire geometry change event (for external listeners)
10
+ fireGeometryChange(state) {
11
+ const feature = this.getFeature(state.featureId)
12
+ if (feature) {
13
+ this.map.fire('draw.update', {
14
+ features: [feature.toGeoJSON()],
15
+ action: 'change_coordinates'
16
+ })
17
+ }
18
+ },
19
+
20
+ // Undo support
21
+ pushUndo(operation) {
22
+ const undoStack = this.map._undoStack
23
+ if (!undoStack) {
24
+ return
25
+ }
26
+ undoStack.push(operation)
27
+ },
28
+
29
+ handleUndo(state) {
30
+ const undoStack = this.map._undoStack
31
+ if (!undoStack || undoStack.length === 0) {
32
+ return
33
+ }
34
+
35
+ const op = undoStack.pop()
36
+
37
+ if (op.type === 'move_vertex') {
38
+ this.undoMoveVertex(state, op)
39
+ } else if (op.type === 'insert_vertex') {
40
+ this.undoInsertVertex(state, op)
41
+ } else if (op.type === 'delete_vertex') {
42
+ this.undoDeleteVertex(state, op)
43
+ }
44
+ },
45
+
46
+ undoMoveVertex(state, op) {
47
+ const { vertexIndex, previousPosition, featureId } = op
48
+ const feature = this.getFeature(featureId)
49
+ if (!feature) return
50
+
51
+ const geojson = feature.toGeoJSON()
52
+ const segments = getRingSegments(feature)
53
+ const result = getSegmentForIndex(segments, vertexIndex)
54
+ if (!result) return
55
+
56
+ const coords = getModifiableCoords(geojson, result.segment.path)
57
+ coords[result.localIdx] = previousPosition
58
+ this._applyUndoAndSync(state, geojson, featureId)
59
+
60
+ // Update touch vertex target position
61
+ const vertex = state.vertecies[state.selectedVertexIndex]
62
+ if (vertex) {
63
+ this.updateTouchVertexTarget(state, scalePoint(this.map.project(vertex), state.scale))
64
+ }
65
+ },
66
+
67
+ undoInsertVertex(state, op) {
68
+ const { vertexIndex, featureId } = op
69
+ const feature = this.getFeature(featureId)
70
+ if (!feature) return
71
+
72
+ const geojson = feature.toGeoJSON()
73
+ const segments = getRingSegments(feature)
74
+ const result = getSegmentForIndex(segments, vertexIndex)
75
+ if (!result) return
76
+
77
+ const coords = getModifiableCoords(geojson, result.segment.path)
78
+ coords.splice(result.localIdx, 1)
79
+ this._applyUndoAndSync(state, geojson, featureId)
80
+
81
+ // Clear DirectSelect's coordinate selection
82
+ this.clearSelectedCoordinates()
83
+ this.hideTouchVertexIndicator(state)
84
+ this.changeMode(state, { selectedVertexIndex: -1, selectedVertexType: null })
85
+ },
86
+
87
+ undoDeleteVertex(state, op) {
88
+ const { vertexIndex, position, featureId } = op
89
+ const feature = this.getFeature(featureId)
90
+ if (!feature) {
91
+ return
92
+ }
93
+
94
+ const geojson = feature.toGeoJSON()
95
+ const segments = getRingSegments(feature)
96
+
97
+ // Try to find segment containing vertexIndex
98
+ let result = getSegmentForIndex(segments, vertexIndex)
99
+
100
+ // If not found, vertex might be at segment boundary
101
+ if (!result) {
102
+ for (const seg of segments) {
103
+ if (vertexIndex === seg.start + seg.length) {
104
+ result = { segment: seg, localIdx: seg.length }
105
+ break
106
+ }
107
+ }
108
+ }
109
+
110
+ if (!result) {
111
+ return
112
+ }
113
+
114
+ const coords = getModifiableCoords(geojson, result.segment.path)
115
+ coords.splice(result.localIdx, 0, position)
116
+ this._applyUndoAndSync(state, geojson, featureId)
117
+
118
+ // Update touch vertex target to restored vertex position
119
+ const vertex = state.vertecies[vertexIndex]
120
+ if (vertex) {
121
+ this.updateTouchVertexTarget(state, scalePoint(this.map.project(vertex), state.scale))
122
+ }
123
+ this.changeMode(state, { selectedVertexIndex: vertexIndex, selectedVertexType: 'vertex', coordPath: this.getCoordPath(state, vertexIndex) })
124
+ },
125
+
126
+ _applyUndoAndSync(state, geojson, featureId) {
127
+ this._ctx.api.add(geojson)
128
+ state.vertecies = this.getVerticies(featureId)
129
+ state.midpoints = this.getMidpoints(featureId)
130
+ this._ctx.store.render()
131
+ this.fireGeometryChange(state)
132
+ }
133
+ }
@@ -0,0 +1,141 @@
1
+ import {
2
+ getCoords,
3
+ getRingSegments,
4
+ getSegmentForIndex,
5
+ getModifiableCoords
6
+ } from './geometryHelpers.js'
7
+
8
+ const ARROW_OFFSETS = { ArrowUp: [0, -1], ArrowDown: [0, 1], ArrowLeft: [-1, 0], ArrowRight: [1, 0] }
9
+ const NUDGE = 1, STEP = 5
10
+
11
+ export const vertexOperations = {
12
+ updateMidpoint(coordinates) {
13
+ setTimeout(() => {
14
+ this.map.getSource('mapbox-gl-draw-hot').setData({
15
+ type: 'Feature',
16
+ properties: { meta: 'midpoint', active: 'true', id: 'active-midpoint' },
17
+ geometry: { type: 'Point', coordinates }
18
+ })
19
+ }, 0)
20
+ },
21
+
22
+ updateVertex(state, direction) {
23
+ const [idx, type] = this.getVertexOrMidpoint(state, direction)
24
+ if (idx < 0 || !type) {
25
+ return
26
+ }
27
+ this.changeMode(state, { selectedVertexIndex: idx, selectedVertexType: type, ...(type === 'vertex' && { coordPath: this.getCoordPath(state, idx) }) })
28
+ },
29
+
30
+ getOffset(coord, e) {
31
+ const pt = this.map.project(coord)
32
+ const offset = e?.shiftKey ? NUDGE : STEP
33
+ const [dx, dy] = e ? ARROW_OFFSETS[e.key].map(v => v * offset) : [0, 0]
34
+ return this.map.unproject({ x: pt.x + dx, y: pt.y + dy })
35
+ },
36
+
37
+ getNewCoord(state, e) {
38
+ return this.getOffset(getCoords(this.getFeature(state.featureId))[state.selectedVertexIndex], e)
39
+ },
40
+
41
+ insertVertex(state, e) {
42
+ const midIdx = state.selectedVertexIndex - state.vertecies.length
43
+ const newCoord = this.getOffset(state.midpoints[midIdx], e)
44
+ const feature = this.getFeature(state.featureId)
45
+ const geojson = feature.toGeoJSON()
46
+
47
+ // Find which segment this midpoint belongs to and calculate insertion position
48
+ const segments = getRingSegments(feature)
49
+ let globalInsertIdx = midIdx + 1
50
+ let insertSegment = null
51
+ let localInsertIdx = 0
52
+
53
+ // Map midpoint index to segment and local position
54
+ let midpointCounter = 0
55
+ for (const seg of segments) {
56
+ // Must match getMidpoints calculation
57
+ const segMidpoints = seg.closed ? seg.length : seg.length - 1
58
+ if (midIdx < midpointCounter + segMidpoints) {
59
+ insertSegment = seg
60
+ localInsertIdx = (midIdx - midpointCounter) + 1
61
+ globalInsertIdx = seg.start + localInsertIdx
62
+ break
63
+ }
64
+ midpointCounter += segMidpoints
65
+ }
66
+
67
+ if (!insertSegment) return
68
+
69
+ const coords = getModifiableCoords(geojson, insertSegment.path)
70
+ coords.splice(localInsertIdx, 0, [newCoord.lng, newCoord.lat])
71
+ this._ctx.api.add(geojson)
72
+
73
+ this.pushUndo({ type: 'insert_vertex', featureId: state.featureId, vertexIndex: globalInsertIdx })
74
+ this.changeMode(state, { selectedVertexIndex: globalInsertIdx, selectedVertexType: 'vertex', coordPath: this.getCoordPath(state, globalInsertIdx) })
75
+ },
76
+
77
+ moveVertex(state, coord, options = {}) {
78
+ if (options.checkSnap && state.enableSnap !== false) {
79
+ const snap = this.map._snapInstance
80
+ if (snap?.snapStatus && snap.snapCoords?.length >= 2) {
81
+ coord = { lng: snap.snapCoords[0], lat: snap.snapCoords[1] }
82
+ }
83
+ }
84
+
85
+ const feature = this.getFeature(state.featureId)
86
+ const geojson = feature.toGeoJSON()
87
+ const segments = getRingSegments(feature)
88
+ const result = getSegmentForIndex(segments, state.selectedVertexIndex)
89
+ if (!result) return
90
+
91
+ const coords = getModifiableCoords(geojson, result.segment.path)
92
+ coords[result.localIdx] = [coord.lng, coord.lat]
93
+ this._ctx.api.add(geojson)
94
+ state.vertecies = this.getVerticies(state.featureId)
95
+
96
+ this.map.fire('draw.geometrychange', state.feature)
97
+ },
98
+
99
+ deleteVertex(state) {
100
+ const feature = this.getFeature(state.featureId)
101
+ if (!feature) {
102
+ return
103
+ }
104
+
105
+ const segments = getRingSegments(feature)
106
+ const result = getSegmentForIndex(segments, state.selectedVertexIndex)
107
+ if (!result) {
108
+ return
109
+ }
110
+
111
+ const { segment } = result
112
+ // Minimum vertices per segment: 3 for closed rings (mapbox-gl-draw's internal representation), 2 for lines
113
+ const minVertices = segment.closed ? 3 : 2
114
+ if (segment.length <= minVertices) {
115
+ return
116
+ }
117
+
118
+ // Save position for undo before deletion
119
+ const deletedPosition = [...state.vertecies[state.selectedVertexIndex]]
120
+ const deletedIndex = state.selectedVertexIndex
121
+
122
+ this._ctx.api.trash()
123
+
124
+ // Clear DirectSelect's coordinate selection to prevent visual artifacts
125
+ this.clearSelectedCoordinates()
126
+ // Force feature re-render to clear vertex highlights
127
+ feature.changed()
128
+ this._ctx.store.render()
129
+
130
+ // Push undo operation
131
+ this.pushUndo({
132
+ type: 'delete_vertex',
133
+ featureId: state.featureId,
134
+ vertexIndex: deletedIndex,
135
+ position: deletedPosition
136
+ })
137
+
138
+ // Clear selection after delete
139
+ this.changeMode(state, { selectedVertexIndex: -1, selectedVertexType: null })
140
+ }
141
+ }
@@ -0,0 +1,121 @@
1
+ import {
2
+ getCoords,
3
+ getRingSegments,
4
+ getSegmentForIndex
5
+ } from './geometryHelpers.js'
6
+ import { spatialNavigate } from '../../utils/spatial.js'
7
+
8
+ export const vertexQueries = {
9
+ findVertexIndex(coords, targetCoord, currentIdx) {
10
+ // Search for vertex, preferring matches near currentIdx to handle duplicate coords (e.g., closing vertices)
11
+ const matches = []
12
+ coords.forEach((c, i) => {
13
+ if (c[0] === targetCoord[0] && c[1] === targetCoord[1]) {
14
+ matches.push(i)
15
+ }
16
+ })
17
+
18
+ if (matches.length === 0) return -1
19
+ if (matches.length === 1) return matches[0]
20
+
21
+ // Multiple matches - pick closest to current selection
22
+ if (currentIdx >= 0) {
23
+ return matches.reduce((best, idx) =>
24
+ Math.abs(idx - currentIdx) < Math.abs(best - currentIdx) ? idx : best
25
+ )
26
+ }
27
+ return matches[0]
28
+ },
29
+
30
+ getCoordPath(state, idx) {
31
+ const feature = this.getFeature(state.featureId)
32
+ if (!feature) return '0'
33
+
34
+ const segments = getRingSegments(feature)
35
+ const result = getSegmentForIndex(segments, idx)
36
+ if (!result) return '0'
37
+
38
+ const { segment, localIdx } = result
39
+ return [...segment.path, localIdx].join('.')
40
+ },
41
+
42
+ syncVertices(state) {
43
+ state.vertecies = this.getVerticies(state.featureId)
44
+ state.midpoints = this.getMidpoints(state.featureId)
45
+ },
46
+
47
+ getVerticies(featureId) {
48
+ return getCoords(this.getFeature(featureId)) || []
49
+ },
50
+
51
+ getMidpoints(featureId) {
52
+ const feature = this.getFeature(featureId)
53
+ const coords = getCoords(feature)
54
+ const segments = getRingSegments(feature)
55
+ if (!coords?.length || !segments.length) {
56
+ return []
57
+ }
58
+
59
+ const midpoints = []
60
+ // Create midpoints within each segment, respecting boundaries
61
+ for (const seg of segments) {
62
+ // For closed rings, create midpoint between every vertex including last→first
63
+ // For open lines, create midpoints only between consecutive vertices (no wrap-around)
64
+ const count = seg.closed ? seg.length : seg.length - 1
65
+ for (let i = 0; i < count; i++) {
66
+ const idx = seg.start + i
67
+ const nextIdx = seg.start + ((i + 1) % seg.length)
68
+ const [x1, y1] = coords[idx]
69
+ const [x2, y2] = coords[nextIdx]
70
+ midpoints.push([(x1 + x2) / 2, (y1 + y2) / 2])
71
+ }
72
+ }
73
+ return midpoints
74
+ },
75
+
76
+ getVertexOrMidpoint(state, direction) {
77
+ // Ensure vertices and midpoints are populated
78
+ if (!state.vertecies?.length) {
79
+ state.vertecies = this.getVerticies(state.featureId)
80
+ state.midpoints = this.getMidpoints(state.featureId)
81
+ }
82
+ if (!state.vertecies?.length) {
83
+ return [-1, null]
84
+ }
85
+ const project = (p) => p ? Object.values(this.map.project(p)) : null
86
+ const pixels = [...state.vertecies.map(project), ...state.midpoints.map(project)].filter(Boolean)
87
+ if (!pixels.length) {
88
+ return [-1, null]
89
+ }
90
+ const start = pixels[state.selectedVertexIndex] || Object.values(this.map.project(this.map.getCenter()))
91
+ const idx = spatialNavigate(start, pixels, direction)
92
+ return [idx, idx < state.vertecies.length ? 'vertex' : 'midpoint']
93
+ },
94
+
95
+ getVertexIndexFromMidpoint(state, coordPath) {
96
+ const feature = this.getFeature(state.featureId)
97
+ const segments = getRingSegments(feature)
98
+ const parts = coordPath.split('.').map(Number)
99
+
100
+ // Find which segment this coord_path belongs to
101
+ let midpointOffset = 0
102
+ for (const seg of segments) {
103
+ const pathMatches = seg.path.every((val, idx) => val === parts[idx])
104
+ if (pathMatches && parts.length === seg.path.length + 1) {
105
+ // In DirectSelect, midpoint coord_path represents the insertion index
106
+ // The midpoint between vertex N and N+1 has coord_path ending in N+1
107
+ // So our flat midpoint index is one less than the coord_path index
108
+ const insertionIdx = parts[parts.length - 1]
109
+ const localMidpointIdx = insertionIdx > 0 ? insertionIdx - 1 : seg.length - 2
110
+ // Midpoints are indexed after all vertices
111
+ return state.vertecies.length + midpointOffset + localMidpointIdx
112
+ }
113
+ // Count midpoints in this segment (must match getMidpoints calculation)
114
+ const segMidpoints = seg.closed ? seg.length : seg.length - 1
115
+ midpointOffset += segMidpoints
116
+ }
117
+
118
+ // Fallback
119
+ return state.vertecies.length
120
+ }
121
+ }