@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,155 @@
1
+ import { initialState, actions } from './reducer.js'
2
+
3
+ describe('initialState', () => {
4
+ it('has correct defaults', () => {
5
+ expect(initialState).toEqual({
6
+ enabled: false,
7
+ dataLayers: [],
8
+ markerColor: null,
9
+ interactionMode: null,
10
+ multiSelect: false,
11
+ contiguous: false,
12
+ selectedFeatures: [],
13
+ selectionBounds: null,
14
+ closeOnAction: true
15
+ })
16
+ })
17
+ })
18
+
19
+ describe('ENABLE/DISABLE actions', () => {
20
+ it('ENABLE sets enabled and merges payload', () => {
21
+ const state = { ...initialState, enabled: false }
22
+ const payload = { dataLayers: [1], markerColor: 'red' }
23
+ const result = actions.ENABLE(state, payload)
24
+
25
+ expect(result.enabled).toBe(true)
26
+ expect(result.dataLayers).toEqual([1])
27
+ expect(result.markerColor).toBe('red')
28
+ expect(result).not.toBe(state)
29
+ })
30
+
31
+ it('DISABLE sets enabled to false, clears selection, and preserves other state', () => {
32
+ const state = { ...initialState, enabled: true, dataLayers: [1], markerColor: 'red', selectedFeatures: [{ featureId: 'f1' }], selectionBounds: [0, 0, 1, 1] }
33
+ const result = actions.DISABLE(state)
34
+
35
+ expect(result.enabled).toBe(false)
36
+ expect(result.dataLayers).toEqual([1])
37
+ expect(result.markerColor).toBe('red')
38
+ expect(result.selectedFeatures).toEqual([])
39
+ expect(result.selectionBounds).toBeNull()
40
+ expect(result).not.toBe(state)
41
+ })
42
+ })
43
+
44
+
45
+ describe('TOGGLE_SELECTED_FEATURES action', () => {
46
+ const createFeature = (id, layerId = 'layer1') => ({
47
+ featureId: id,
48
+ layerId,
49
+ idProperty: 'id',
50
+ properties: { name: `Feature ${id}` },
51
+ geometry: { type: 'Point', coordinates: [0, 0] }
52
+ })
53
+
54
+ it('handles single-select, multi-select, add/remove, and replaceAll', () => {
55
+ let state = { ...initialState, selectionBounds: { sw: [0, 0], ne: [1, 1] } }
56
+
57
+ // Single-select: add
58
+ state = actions.TOGGLE_SELECTED_FEATURES(state, createFeature('f1'))
59
+ expect(state.selectedFeatures).toHaveLength(1)
60
+
61
+ // Single-select: replace
62
+ state = actions.TOGGLE_SELECTED_FEATURES(state, createFeature('f2'))
63
+ expect(state.selectedFeatures[0].featureId).toBe('f2')
64
+
65
+ // Toggle off same - clears bounds
66
+ state = actions.TOGGLE_SELECTED_FEATURES(state, createFeature('f2'))
67
+ expect(state.selectedFeatures).toHaveLength(0)
68
+ expect(state.selectionBounds).toBeNull()
69
+
70
+ // Multi-select: add multiple
71
+ state = { ...state, selectionBounds: { sw: [0, 0], ne: [1, 1] } }
72
+ state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f1'), multiSelect: true })
73
+ state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f2'), multiSelect: true })
74
+ expect(state.selectedFeatures.map(f => f.featureId)).toEqual(['f1', 'f2'])
75
+
76
+ // Multi-select: remove (not last) - clears bounds for recalculation
77
+ state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f1'), multiSelect: true })
78
+ expect(state.selectedFeatures.map(f => f.featureId)).toEqual(['f2'])
79
+ expect(state.selectionBounds).toBeNull()
80
+
81
+ // Multi-select: remove last - clears bounds
82
+ state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f2'), multiSelect: true })
83
+ expect(state.selectedFeatures).toHaveLength(0)
84
+ expect(state.selectionBounds).toBeNull()
85
+
86
+ // addToExisting false removes feature - clears bounds when empty
87
+ state = { ...state, selectionBounds: { sw: [0, 0], ne: [1, 1] } }
88
+ state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f2'), multiSelect: true })
89
+ state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f2'), addToExisting: false })
90
+ expect(state.selectedFeatures).toHaveLength(0)
91
+ expect(state.selectionBounds).toBeNull()
92
+
93
+ // replaceAll replaces everything
94
+ state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f3'), replaceAll: true })
95
+ expect(state.selectedFeatures).toHaveLength(1)
96
+ expect(state.selectedFeatures[0].featureId).toBe('f3')
97
+ })
98
+
99
+ it('handles null or empty selectedFeatures gracefully', () => {
100
+ let state = { ...initialState, selectedFeatures: null }
101
+ state = actions.TOGGLE_SELECTED_FEATURES(state, createFeature('f1'))
102
+ expect(state.selectedFeatures).toHaveLength(1)
103
+
104
+ state = { ...initialState, selectedFeatures: [] }
105
+ state = actions.TOGGLE_SELECTED_FEATURES(state, { ...createFeature('f2'), addToExisting: false })
106
+ expect(state.selectedFeatures).toEqual([])
107
+ })
108
+
109
+ it('matches by both featureId and layerId', () => {
110
+ const state = { ...initialState, selectedFeatures: [createFeature('f1', 'layer1')] }
111
+ const payload = createFeature('f1', 'layer2')
112
+ const result = actions.TOGGLE_SELECTED_FEATURES(state, payload)
113
+ expect(result.selectedFeatures[0].layerId).toBe('layer2')
114
+ })
115
+ })
116
+
117
+ describe('UPDATE_SELECTED_BOUNDS action', () => {
118
+ it('updates selectionBounds correctly', () => {
119
+ const state = { ...initialState, selectionBounds: { sw: [0, 0], ne: [1, 1] } }
120
+ const newBounds = { sw: [0, 0], ne: [2, 2] }
121
+ const result = actions.UPDATE_SELECTED_BOUNDS(state, newBounds)
122
+ expect(result.selectionBounds).toEqual(newBounds)
123
+
124
+ // unchanged bounds returns same state
125
+ const result2 = actions.UPDATE_SELECTED_BOUNDS(state, { sw: [0, 0], ne: [1, 1] })
126
+ expect(result2).toBe(state)
127
+ })
128
+ })
129
+
130
+ describe('CLEAR_SELECTED_FEATURES action', () => {
131
+ it('resets selection and bounds', () => {
132
+ const state = {
133
+ ...initialState,
134
+ selectedFeatures: [1],
135
+ selectionBounds: { sw: [0, 0], ne: [1, 1] }
136
+ }
137
+ const result = actions.CLEAR_SELECTED_FEATURES(state)
138
+ expect(result.selectedFeatures).toEqual([])
139
+ expect(result.selectionBounds).toBeNull()
140
+ expect(result).not.toBe(state)
141
+ })
142
+ })
143
+
144
+ describe('actions object', () => {
145
+ it('exports all action handlers as functions', () => {
146
+ expect(Object.keys(actions)).toEqual([
147
+ 'ENABLE',
148
+ 'DISABLE',
149
+ 'TOGGLE_SELECTED_FEATURES',
150
+ 'UPDATE_SELECTED_BOUNDS',
151
+ 'CLEAR_SELECTED_FEATURES'
152
+ ])
153
+ Object.values(actions).forEach(fn => expect(typeof fn).toBe('function'))
154
+ })
155
+ })
@@ -0,0 +1,57 @@
1
+ import { buildStylesMap } from './buildStylesMap.js'
2
+ import { DEFAULTS } from '../defaults.js'
3
+ import { getValueForStyle } from '../../../../src/utils/getValueForStyle.js'
4
+
5
+ jest.mock('../../../../src/utils/getValueForStyle.js', () => ({
6
+ getValueForStyle: jest.fn((value) => value)
7
+ }))
8
+
9
+ describe('buildStylesMap', () => {
10
+ beforeEach(() => {
11
+ jest.clearAllMocks()
12
+ })
13
+
14
+ it('returns empty object when mapStyle is falsy or dataLayers empty', () => {
15
+ expect(buildStylesMap([{ layerId: 'test' }], null)).toEqual({})
16
+ expect(buildStylesMap([{ layerId: 'test' }], undefined)).toEqual({})
17
+ expect(buildStylesMap([], { id: 'default' })).toEqual({})
18
+ })
19
+
20
+ it('builds correct stylesMap with defaults and layer overrides', () => {
21
+ const dataLayers = [
22
+ { layerId: 'custom1', selectedStroke: 'red', selectedFill: 'blue', selectedStrokeWidth: 5 },
23
+ { layerId: 'custom2' } // uses defaults
24
+ ]
25
+ const mapStyle = { id: 'default-style' }
26
+
27
+ const result = buildStylesMap(dataLayers, mapStyle)
28
+
29
+ // Layer-specific values
30
+ expect(result.custom1).toEqual({
31
+ stroke: 'red',
32
+ fill: 'blue',
33
+ strokeWidth: 5
34
+ })
35
+
36
+ // Default fallback values
37
+ expect(result.custom2.stroke).toBe(DEFAULTS.selectedStroke)
38
+ expect(result.custom2.fill).toBe(DEFAULTS.selectedFill)
39
+ expect(result.custom2.strokeWidth).toBe(DEFAULTS.selectedStrokeWidth)
40
+ })
41
+
42
+ it('calls getValueForStyle for stroke and fill with mapStyle.id', () => {
43
+ const dataLayers = [
44
+ { layerId: 'layer1', selectedStroke: 'strokeVal', selectedFill: 'fillVal' }
45
+ ]
46
+ const mapStyle = { id: 'mapStyleId' }
47
+
48
+ buildStylesMap(dataLayers, mapStyle)
49
+
50
+ expect(getValueForStyle).toHaveBeenCalledWith('strokeVal', 'mapStyleId')
51
+ expect(getValueForStyle).toHaveBeenCalledWith('fillVal', 'mapStyleId')
52
+
53
+ // Ensure strokeWidth is not passed to getValueForStyle
54
+ const numberCalls = getValueForStyle.mock.calls.filter(call => typeof call[0] === 'number')
55
+ expect(numberCalls).toHaveLength(0)
56
+ })
57
+ })
@@ -6,9 +6,9 @@ export const buildLayerConfigMap = dataLayers => {
6
6
  return map
7
7
  }
8
8
 
9
- export const getFeaturesAtPoint = (mapProvider, point) => {
9
+ export const getFeaturesAtPoint = (mapProvider, point, options) => {
10
10
  try {
11
- return mapProvider?.getFeaturesAtPoint(point) || []
11
+ return mapProvider?.getFeaturesAtPoint(point, options) || []
12
12
  } catch (err) {
13
13
  console.warn('Feature query failed:', err)
14
14
  return []
@@ -0,0 +1,56 @@
1
+ import { buildLayerConfigMap, getFeaturesAtPoint, findMatchingFeature } from './featureQueries.js'
2
+
3
+ describe('buildLayerConfigMap', () => {
4
+ it('builds map keyed by layerId, handles empty array', () => {
5
+ const dataLayers = [
6
+ { layerId: 'a', value: 1 },
7
+ { layerId: 'b', value: 2 }
8
+ ]
9
+
10
+ const result = buildLayerConfigMap(dataLayers)
11
+ expect(result).toEqual({
12
+ a: { layerId: 'a', value: 1 },
13
+ b: { layerId: 'b', value: 2 }
14
+ })
15
+
16
+ expect(buildLayerConfigMap([])).toEqual({})
17
+ })
18
+ })
19
+
20
+ describe('getFeaturesAtPoint', () => {
21
+ it('returns features or empty array, handles errors', () => {
22
+ const features = [{ id: 'f1' }]
23
+ const mockProvider = {
24
+ getFeaturesAtPoint: jest.fn(() => features)
25
+ }
26
+
27
+ expect(getFeaturesAtPoint(mockProvider, { x: 0, y: 0 })).toBe(features)
28
+ expect(getFeaturesAtPoint(null, { x: 0, y: 0 })).toEqual([])
29
+ expect(getFeaturesAtPoint(undefined, { x: 0, y: 0 })).toEqual([])
30
+
31
+ // error handling
32
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
33
+ const errorProvider = { getFeaturesAtPoint: jest.fn(() => { throw new Error('fail') }) }
34
+ expect(getFeaturesAtPoint(errorProvider, { x: 0, y: 0 })).toEqual([])
35
+ expect(consoleSpy).toHaveBeenCalledWith('Feature query failed:', expect.any(Error))
36
+ consoleSpy.mockRestore()
37
+ })
38
+ })
39
+
40
+ describe('findMatchingFeature', () => {
41
+ const layerConfigMap = { layer1: { layerId: 'layer1' } }
42
+
43
+ it('returns first feature matching config, null otherwise', () => {
44
+ const features = [
45
+ { id: 'f1', layer: { id: 'unknown' } },
46
+ { id: 'f2', layer: { id: 'layer1' } }
47
+ ]
48
+ const result = findMatchingFeature(features, layerConfigMap)
49
+ expect(result).toEqual({ feature: features[1], config: layerConfigMap.layer1 })
50
+
51
+ expect(findMatchingFeature([], layerConfigMap)).toBeNull()
52
+ expect(findMatchingFeature([{ id: 'f3', layer: {} }], layerConfigMap)).toBeNull()
53
+ expect(findMatchingFeature([{ id: 'f4' }], layerConfigMap)).toBeNull()
54
+ expect(findMatchingFeature([{ id: 'f5', layer: { id: 'other' } }], {})).toBeNull()
55
+ })
56
+ })
@@ -0,0 +1,95 @@
1
+ import { polygon, multiPolygon, lineString, multiLineString, point, multiPoint } from '@turf/helpers'
2
+ import booleanDisjoint from '@turf/boolean-disjoint'
3
+
4
+ /**
5
+ * Convert a GeoJSON Feature or geometry-like object into a Turf geometry.
6
+ *
7
+ * @param {Object} featureOrGeom - Either a Feature with a `.geometry` property or a raw GeoJSON geometry object.
8
+ * @returns {Object} Turf geometry (Polygon, LineString, Point, etc.)
9
+ *
10
+ * @throws Will throw if the geometry type is not supported.
11
+ */
12
+ function toTurfGeometry(featureOrGeom) {
13
+ const geom = featureOrGeom.geometry || featureOrGeom
14
+
15
+ switch (geom.type) {
16
+ case 'Polygon':
17
+ return polygon(geom.coordinates);
18
+ case 'MultiPolygon':
19
+ return multiPolygon(geom.coordinates);
20
+ case 'LineString':
21
+ return lineString(geom.coordinates);
22
+ case 'MultiLineString':
23
+ return multiLineString(geom.coordinates);
24
+ case 'Point':
25
+ return point(geom.coordinates);
26
+ case 'MultiPoint':
27
+ return multiPoint(geom.coordinates);
28
+ default:
29
+ throw new Error(`Unsupported geometry type: ${geom.type}`)
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Check if a feature is contiguous (touches or overlaps) with any feature in an array.
35
+ *
36
+ * @param {Object} feature - The feature to test
37
+ * @param {Array} features - Array of features to test against
38
+ * @returns {boolean} True if the feature is contiguous with at least one feature in the array
39
+ */
40
+ function isContiguousWithAny(feature, features) {
41
+ return features.some(f => !booleanDisjoint(toTurfGeometry(f), toTurfGeometry(feature)))
42
+ }
43
+
44
+ /**
45
+ * Check if a single polygon/multipolygon feature can be split.
46
+ *
47
+ * @param {Array} features - Array of features
48
+ * @returns {boolean} True if exactly one polygon or multipolygon feature
49
+ */
50
+ function canSplitFeatures(features) {
51
+ if (features.length !== 1) {
52
+ return false
53
+ }
54
+ const type = features[0].geometry?.type
55
+ return type === 'Polygon' || type === 'MultiPolygon'
56
+ }
57
+
58
+ /**
59
+ * Check if all features form a single contiguous group (can be merged).
60
+ * Uses flood-fill to find connected components.
61
+ *
62
+ * @param {Array} features - Array of features to test
63
+ * @returns {boolean} True if 2+ features and all are contiguous
64
+ */
65
+ function areAllContiguous(features) {
66
+ if (features.length < 2) {
67
+ return false
68
+ }
69
+
70
+ const connected = new Set([0])
71
+ let changed = true
72
+
73
+ while (changed) {
74
+ changed = false
75
+ for (let i = 1; i < features.length; i++) {
76
+ if (connected.has(i)) {
77
+ continue
78
+ }
79
+ const connectedFeatures = [...connected].map(idx => features[idx])
80
+ if (isContiguousWithAny(features[i], connectedFeatures)) {
81
+ connected.add(i)
82
+ changed = true
83
+ }
84
+ }
85
+ }
86
+
87
+ return connected.size === features.length
88
+ }
89
+
90
+ export {
91
+ toTurfGeometry,
92
+ isContiguousWithAny,
93
+ canSplitFeatures,
94
+ areAllContiguous
95
+ }
@@ -0,0 +1,93 @@
1
+ import { toTurfGeometry, isContiguousWithAny, canSplitFeatures, areAllContiguous } from './spatial.js'
2
+ import { polygon, multiPolygon, lineString, multiLineString, point, multiPoint } from '@turf/helpers'
3
+
4
+ describe('toTurfGeometry', () => {
5
+ const geomTypes = [
6
+ { type: 'Polygon', coords: [[[0,0],[1,0],[1,1],[0,0]]], fn: polygon },
7
+ { type: 'MultiPolygon', coords: [[[[0,0],[1,0],[1,1],[0,0]]]], fn: multiPolygon },
8
+ { type: 'LineString', coords: [[0,0],[1,1]], fn: lineString },
9
+ { type: 'MultiLineString', coords: [[[0,0],[1,1]]], fn: multiLineString },
10
+ { type: 'Point', coords: [0,0], fn: point },
11
+ { type: 'MultiPoint', coords: [[0,0],[1,1]], fn: multiPoint }
12
+ ]
13
+
14
+ geomTypes.forEach(({ type, coords, fn }) => {
15
+ it(`converts ${type} Feature`, () => {
16
+ const feature = { type: 'Feature', geometry: { type, coordinates: coords } }
17
+ expect(toTurfGeometry(feature)).toEqual(fn(coords))
18
+ })
19
+
20
+ it(`converts raw ${type} geometry`, () => {
21
+ const geom = { type, coordinates: coords }
22
+ expect(toTurfGeometry(geom)).toEqual(fn(coords))
23
+ })
24
+ })
25
+
26
+ it('throws on unsupported geometry', () => {
27
+ expect(() => toTurfGeometry({ type: 'Feature', geometry: { type: 'Circle', coordinates: [] } }))
28
+ .toThrow('Unsupported geometry type: Circle')
29
+ })
30
+ })
31
+
32
+ describe('isContiguousWithAny', () => {
33
+ const makePolygonFeature = (coords) => ({
34
+ type: 'Feature',
35
+ geometry: { type: 'Polygon', coordinates: [coords] }
36
+ })
37
+
38
+ const featureA = makePolygonFeature([[0,0],[2,0],[2,2],[0,2],[0,0]])
39
+ const featureB = makePolygonFeature([[2,0],[4,0],[4,2],[2,2],[2,0]]) // shares edge with A
40
+ const featureC = makePolygonFeature([[5,5],[7,5],[7,7],[5,7],[5,5]]) // disjoint from A and B
41
+
42
+ it('returns true when feature overlaps with one in the array', () => {
43
+ const overlapping = makePolygonFeature([[1,0],[3,0],[3,2],[1,2],[1,0]]) // overlaps A
44
+ expect(isContiguousWithAny(overlapping, [featureA])).toBe(true)
45
+ })
46
+
47
+ it('returns true when feature shares an edge with one in the array', () => {
48
+ expect(isContiguousWithAny(featureB, [featureA])).toBe(true)
49
+ })
50
+
51
+ it('returns true when feature is contiguous with at least one in the array', () => {
52
+ expect(isContiguousWithAny(featureB, [featureC, featureA])).toBe(true)
53
+ })
54
+
55
+ it('returns false when feature is disjoint from all features', () => {
56
+ expect(isContiguousWithAny(featureC, [featureA, featureB])).toBe(false)
57
+ })
58
+
59
+ it('returns false when features array is empty', () => {
60
+ expect(isContiguousWithAny(featureA, [])).toBe(false)
61
+ })
62
+ })
63
+
64
+ describe('canSplitFeatures', () => {
65
+ it.each([
66
+ [[{ geometry: { type: 'Polygon' } }], true],
67
+ [[{ geometry: { type: 'MultiPolygon' } }], true],
68
+ [[{ geometry: { type: 'LineString' } }], false],
69
+ [[], false],
70
+ [[{ geometry: { type: 'Polygon' } }, { geometry: { type: 'Polygon' } }], false]
71
+ ])('returns expected result for %j', (features, expected) => {
72
+ expect(canSplitFeatures(features)).toBe(expected)
73
+ })
74
+ })
75
+
76
+ describe('areAllContiguous', () => {
77
+ const poly = (coords) => ({ geometry: { type: 'Polygon', coordinates: [coords] } })
78
+ const A = poly([[0,0],[2,0],[2,2],[0,2],[0,0]])
79
+ const B = poly([[2,0],[4,0],[4,2],[2,2],[2,0]]) // touches A
80
+ const C = poly([[4,0],[6,0],[6,2],[4,2],[4,0]]) // touches B
81
+ const D = poly([[10,10],[12,10],[12,12],[10,12],[10,10]]) // isolated
82
+
83
+ it.each([
84
+ [[], false],
85
+ [[A], false],
86
+ [[A, B], true],
87
+ [[A, B, C], true],
88
+ [[A, D], false],
89
+ [[A, B, D], false]
90
+ ])('returns expected result for %# features', (features, expected) => {
91
+ expect(areAllContiguous(features)).toBe(expected)
92
+ })
93
+ })
@@ -1 +1 @@
1
- .im-c-search-form{display:none;padding:0;min-height:auto;border-radius:0}.im-c-search-form:before{border-radius:0}.im-o-app--mobile .im-c-search-form{position:absolute;left:0;top:0;right:0;height:auto}.im-o-app--tablet .im-c-search-form,.im-o-app--desktop .im-c-search-form{border-radius:var(--button-border-radius)}.im-o-app--tablet .im-c-search-form:before,.im-o-app--desktop .im-c-search-form:before{border-radius:var(--button-border-radius)}.im-o-app--mobile .im-c-search-form--default-expanded{position:relative;margin:calc(-1*var(--primary-gap)) calc(-1*var(--primary-gap)) 0}.im-o-app--mobile .im-c-search-form--default-expanded .im-c-search-suggestions{position:absolute;top:100%;width:100%;left:0}.im-c-search-close-button:before{box-shadow:none}.im-c-search-close-button[data-focus-visible=true]{z-index:1}.im-c-search-suggestions{max-height:calc(100vh - 66px);overflow-y:auto;scroll-padding:0;margin:0;padding:0;background-color:var(--background-color);list-style:none}.im-c-search-suggestions__item{margin:0;padding:calc(var(--divider-gap) + 2px) var(--primary-gap);position:relative;color:var(--secondary-text-color);border-top:1px solid var(--button-hover-color);cursor:pointer}.im-c-search-suggestions__item mark{font-weight:bold;color:var(--foreground-color);background-color:rgba(0,0,0,0);pointer-events:none}@media(hover: hover)and (pointer: fine){.im-c-search-suggestions__item:hover:not([aria-selected=true]){background-color:var(--button-hover-color)}}.im-c-search-suggestions__item[aria-selected=true]{background-color:var(--button-hover-color)}.im-c-search-suggestions__item[aria-selected=true] mark{color:var(--fixed-foreground-color)}.im-c-search-suggestions__item:hover:not([aria-selected=true]) .im-c-search-suggestions__label{text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:2px;text-decoration-color:var(--foreground-color);text-decoration-skip-ink:none}.im-c-search-suggestions__item[aria-selected=true] .im-c-search-suggestions__label{outline:3px solid rgba(0,0,0,0);color:var(--focus-border-color);background-color:var(--focus-outline-color);box-shadow:0 -2px var(--focus-outline-color),0 4px var(--focus-border-color);text-decoration:none}.im-c-search-suggestions__label{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.im-o-app--mobile .im-c-search-form--default-expanded .im-c-search-suggestions{box-shadow:var(--panel-box-shadow);clip-path:inset(0px 0px -20px 0px)}.im-o-app--tablet .im-c-search-suggestions,.im-o-app--desktop .im-c-search-suggestions{border-bottom-left-radius:var(--button-border-radius);border-bottom-right-radius:var(--button-border-radius)}.im-o-app--tablet .im-c-search-suggestions__item,.im-o-app--desktop .im-c-search-suggestions__item{padding:var(--divider-gap) calc(var(--divider-gap) + 2px)}.im-c-search__input-container{display:flex;position:relative;flex:1 1 auto;min-height:auto;height:var(--button-size)}.im-c-search__input-container:before{content:"";z-index:1;position:absolute;inset:-1px;border-radius:var(--button-border-radius);border:2px solid var(--foreground-color);pointer-events:none}.im-c-search__input-container:after{position:absolute;content:"";pointer-events:none;z-index:99;top:-1px;right:-1px;bottom:-1px;left:-1px;border:var(--focus-border-width) solid var(--focus-border-color);outline:var(--focus-outline-width) solid var(--focus-outline-color);border-radius:calc(var(--button-border-radius) - 2px);opacity:0}@media(prefers-reduced-motion: no-preference){.im-c-search__input-container:after{transition:opacity var(--duration) ease}}.im-c-search__input{width:100%;border:0;padding:var(--divider-gap) calc(var(--divider-gap) + 2px);color:var(--foreground-color);background-color:rgba(0,0,0,0);outline:0}.im-c-search__input::placeholder{color:var(--foreground-color)}.im-c-search__input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}.im-c-search__input::-moz-search-clear{display:none}.im-c-search__hint{display:none}.im-o-app--exclusive-control .im-o-app__top .im-c-button-wrapper,.im-o-app--exclusive-control .im-o-app__right .im-c-button-wrapper{opacity:0;z-index:-1}.im-o-app--exclusive-control .im-o-app__inset .im-c-panel{opacity:0;z-index:-1}.im-o-app--mobile .im-c-search__input-container{margin:var(--primary-gap)}.im-c-search__input-container--keyboard-focus-within:after{opacity:1}
1
+ .im-c-search-form{display:none;padding:0;min-height:auto;border-radius:0}.im-c-search-form:before{border-radius:0}.im-o-app--mobile .im-c-search-form{position:absolute;left:0;top:0;right:0;height:auto}.im-o-app--tablet .im-c-search-form,.im-o-app--desktop .im-c-search-form{border-radius:var(--button-border-radius)}.im-o-app--tablet .im-c-search-form:before,.im-o-app--desktop .im-c-search-form:before{border-radius:var(--button-border-radius)}.im-o-app--mobile .im-c-search-form--default-expanded{position:relative;margin:calc(-1*var(--primary-gap)) calc(-1*var(--primary-gap)) 0}.im-o-app--mobile .im-c-search-form--default-expanded .im-c-search-suggestions{position:absolute;top:100%;width:100%;left:0}.im-c-search-close-button:before{box-shadow:none}.im-c-search-close-button[data-focus-visible=true]{z-index:1}.im-c-search-suggestions{max-height:calc(100vh - 66px);overflow-y:auto;scroll-padding:0;margin:0;padding:0;background-color:var(--background-color);list-style:none}.im-c-search-suggestions__item{margin:0;padding:calc(var(--divider-gap) + 2px) var(--primary-gap);position:relative;color:var(--secondary-text-color);border-top:1px solid var(--button-hover-color);cursor:pointer}.im-c-search-suggestions__item mark{font-weight:bold;color:var(--foreground-color);background-color:rgba(0,0,0,0);pointer-events:none}@media(hover: hover)and (pointer: fine){.im-c-search-suggestions__item:hover:not([aria-selected=true]){background-color:var(--button-hover-color)}}.im-c-search-suggestions__item[aria-selected=true]{background-color:var(--button-hover-color)}.im-c-search-suggestions__item[aria-selected=true] mark{color:var(--fixed-foreground-color)}.im-c-search-suggestions__item:hover:not([aria-selected=true]) .im-c-search-suggestions__label{text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:2px;text-decoration-color:var(--foreground-color);text-decoration-skip-ink:none}.im-c-search-suggestions__item[aria-selected=true] .im-c-search-suggestions__label{outline:3px solid rgba(0,0,0,0);color:var(--focus-border-color);background-color:var(--focus-outline-color);box-shadow:0 -2px var(--focus-outline-color),0 4px var(--focus-border-color);text-decoration:none}.im-c-search-suggestions__label{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.im-o-app--mobile .im-c-search-form--default-expanded .im-c-search-suggestions{box-shadow:var(--panel-box-shadow);clip-path:inset(0px 0px -20px 0px)}.im-o-app--tablet .im-c-search-suggestions,.im-o-app--desktop .im-c-search-suggestions{border-bottom-left-radius:var(--button-border-radius);border-bottom-right-radius:var(--button-border-radius)}.im-o-app--tablet .im-c-search-suggestions__item,.im-o-app--desktop .im-c-search-suggestions__item{padding:var(--divider-gap) calc(var(--divider-gap) + 2px)}.im-c-search__input-container{display:flex;position:relative;flex:1 1 auto;min-height:auto;height:var(--button-size)}.im-c-search__input-container:before{content:"";z-index:1;position:absolute;top:-1px;right:-1px;bottom:-1px;left:-1px;border-radius:var(--button-border-radius);border:2px solid var(--foreground-color);pointer-events:none}.im-c-search__input-container:after{position:absolute;content:"";pointer-events:none;z-index:99;top:-1px;right:-1px;bottom:-1px;left:-1px;border:var(--focus-border-width) solid var(--focus-border-color);outline:var(--focus-outline-width) solid var(--focus-outline-color);border-radius:calc(var(--button-border-radius) - 2px);opacity:0}@media(prefers-reduced-motion: no-preference){.im-c-search__input-container:after{transition:opacity var(--duration) ease}}.im-c-search__input{width:100%;border:0;padding:var(--divider-gap) calc(var(--divider-gap) + 2px);color:var(--foreground-color);background-color:rgba(0,0,0,0);outline:0}.im-c-search__input::placeholder{color:var(--foreground-color)}.im-c-search__input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none;display:none}.im-c-search__input::-moz-search-clear{display:none}.im-c-search__hint{display:none}.im-o-app--exclusive-control .im-o-app__top .im-c-button-wrapper,.im-o-app--exclusive-control .im-o-app__right .im-c-button-wrapper{opacity:0;z-index:-1}.im-o-app--exclusive-control .im-o-app__inset .im-c-panel{opacity:0;z-index:-1}.im-o-app--mobile .im-c-search__input-container{margin:var(--primary-gap)}.im-c-search__input-container--keyboard-focus-within:after{opacity:1}