@defra/interactive-map 0.0.3-alpha → 0.0.5-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/README.md +16 -57
  2. package/dist/css/index.css +1 -1
  3. package/dist/esm/im-core.js +1 -1
  4. package/dist/esm/index.js +1 -1
  5. package/dist/umd/im-core.js +1 -1
  6. package/dist/umd/index.js +1 -1
  7. package/docs/api/button-definition.md +176 -0
  8. package/docs/api/context.md +124 -0
  9. package/docs/api/control-definition.md +72 -0
  10. package/docs/api/icon-definition.md +28 -0
  11. package/docs/api/map-style-config.md +76 -0
  12. package/docs/api/marker-config.md +51 -0
  13. package/docs/api/panel-definition.md +117 -0
  14. package/docs/api/slots.md +25 -0
  15. package/docs/api.md +721 -0
  16. package/docs/architecture.md +139 -0
  17. package/docs/building-a-plugin.md +149 -0
  18. package/docs/getting-started.md +46 -0
  19. package/docs/govuk-prototype.md +0 -0
  20. package/docs/plugins/interact.md +205 -0
  21. package/docs/plugins/map-styles.md +84 -0
  22. package/docs/plugins/plugin-context.md +67 -0
  23. package/docs/plugins/plugin-descriptor.md +71 -0
  24. package/docs/plugins/plugin-manifest.md +118 -0
  25. package/docs/plugins/scale-bar.md +62 -0
  26. package/docs/plugins/search.md +162 -0
  27. package/docs/plugins.md +116 -0
  28. package/govuk-prototype-kit.config.json +7 -7
  29. package/package.json +16 -2
  30. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +2 -0
  31. package/plugins/beta/datasets/dist/esm/index.js +2 -0
  32. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
  33. package/plugins/beta/datasets/dist/umd/index.js +2 -0
  34. package/plugins/beta/datasets/src/datasets.js +113 -0
  35. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +206 -0
  36. package/plugins/beta/datasets/src/fetch/fetchGeoJSON.js +38 -0
  37. package/plugins/{datasets → beta/datasets}/src/handleSetMapStyle.js +8 -1
  38. package/plugins/{datasets → beta/datasets}/src/manifest.js +1 -1
  39. package/plugins/{datasets → beta/datasets}/src/mapLayers.js +42 -4
  40. package/plugins/{datasets → beta/datasets}/src/panels/Key.jsx +1 -1
  41. package/plugins/beta/datasets/src/utils/bbox.js +115 -0
  42. package/plugins/beta/draw-ml/dist/css/index.css +1 -0
  43. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -0
  44. package/plugins/beta/draw-ml/dist/esm/index.js +2 -0
  45. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -0
  46. package/plugins/beta/draw-ml/dist/umd/index.js +2 -0
  47. package/plugins/beta/draw-ml/src/api/addFeature.js +27 -0
  48. package/plugins/{draw-ml → beta/draw-ml}/src/api/editFeature.js +17 -6
  49. package/plugins/beta/draw-ml/src/api/merge.js +9 -0
  50. package/plugins/beta/draw-ml/src/api/newLine.js +68 -0
  51. package/plugins/beta/draw-ml/src/api/newPolygon.js +68 -0
  52. package/plugins/beta/draw-ml/src/api/split.js +81 -0
  53. package/plugins/{draw-ml → beta/draw-ml}/src/defaults.js +3 -0
  54. package/plugins/beta/draw-ml/src/draw.scss +32 -0
  55. package/plugins/beta/draw-ml/src/events.js +171 -0
  56. package/plugins/beta/draw-ml/src/manifest.js +132 -0
  57. package/plugins/{draw-ml → beta/draw-ml}/src/mapboxDraw.js +51 -2
  58. package/plugins/{draw-ml → beta/draw-ml}/src/mapboxSnap.js +18 -12
  59. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +497 -0
  60. package/plugins/beta/draw-ml/src/modes/drawLineMode.js +18 -0
  61. package/plugins/beta/draw-ml/src/modes/drawPolygonMode.js +16 -0
  62. package/plugins/beta/draw-ml/src/modes/editVertex/geometryHelpers.js +135 -0
  63. package/plugins/beta/draw-ml/src/modes/editVertex/helpers.js +2 -0
  64. package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +134 -0
  65. package/plugins/beta/draw-ml/src/modes/editVertex/undoHandlers.js +133 -0
  66. package/plugins/beta/draw-ml/src/modes/editVertex/vertexOperations.js +141 -0
  67. package/plugins/beta/draw-ml/src/modes/editVertex/vertexQueries.js +121 -0
  68. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +437 -0
  69. package/plugins/{draw-ml → beta/draw-ml}/src/reducer.js +30 -2
  70. package/plugins/{draw-ml → beta/draw-ml}/src/styles.js +66 -36
  71. package/plugins/beta/draw-ml/src/undoStack.js +50 -0
  72. package/plugins/beta/draw-ml/src/utils/debounce.js +16 -0
  73. package/plugins/beta/draw-ml/src/utils/flattenStyleProperties.js +49 -0
  74. package/plugins/beta/draw-ml/src/utils/spatial.js +258 -0
  75. package/plugins/beta/frame/dist/esm/im-frame-plugin.js +1 -0
  76. package/plugins/beta/frame/dist/esm/index.js +2 -0
  77. package/plugins/beta/frame/dist/umd/im-frame-plugin.js +1 -0
  78. package/plugins/beta/frame/dist/umd/index.js +2 -0
  79. package/plugins/{frame → beta/frame}/src/Frame.jsx +1 -1
  80. package/plugins/{map-styles → beta/map-styles}/dist/css/index.css +1 -1
  81. package/plugins/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -0
  82. package/plugins/beta/map-styles/dist/esm/index.js +2 -0
  83. package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -0
  84. package/plugins/beta/map-styles/dist/umd/index.js +2 -0
  85. package/plugins/{map-styles → beta/map-styles}/src/MapStyles.jsx +1 -1
  86. package/plugins/{map-styles → beta/map-styles}/src/manifest.js +8 -10
  87. package/plugins/{map-styles → beta/map-styles}/src/mapStyles.scss +4 -1
  88. package/plugins/beta/scale-bar/dist/esm/im-scale-bar-plugin.js +1 -0
  89. package/plugins/beta/scale-bar/dist/esm/index.js +2 -0
  90. package/plugins/beta/scale-bar/dist/umd/im-scale-bar-plugin.js +1 -0
  91. package/plugins/beta/scale-bar/dist/umd/index.js +2 -0
  92. package/plugins/beta/use-location/dist/esm/im-use-location-plugin.js +1 -0
  93. package/plugins/beta/use-location/dist/esm/index.js +2 -0
  94. package/plugins/beta/use-location/dist/umd/im-use-location-plugin.js +1 -0
  95. package/plugins/beta/use-location/dist/umd/index.js +2 -0
  96. package/plugins/interact/dist/css/index.css +1 -1
  97. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  98. package/plugins/interact/dist/esm/index.js +1 -1
  99. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  100. package/plugins/interact/dist/umd/index.js +1 -1
  101. package/plugins/interact/src/InteractInit.jsx +1 -1
  102. package/plugins/interact/src/InteractInit.test.js +97 -0
  103. package/plugins/interact/src/api/clear.test.js +18 -0
  104. package/plugins/interact/src/api/disable.test.js +12 -0
  105. package/plugins/interact/src/api/enable.test.js +48 -0
  106. package/plugins/interact/src/api/selectFeature.test.js +33 -0
  107. package/plugins/interact/src/api/unselectFeature.test.js +33 -0
  108. package/plugins/interact/src/defaults.js +2 -0
  109. package/plugins/interact/src/events.js +37 -52
  110. package/plugins/interact/src/events.test.js +186 -0
  111. package/plugins/interact/src/hooks/useHighlightSync.js +1 -1
  112. package/plugins/interact/src/hooks/useHighlightSync.test.js +212 -0
  113. package/plugins/interact/src/hooks/useInteractionHandlers.js +80 -41
  114. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +316 -0
  115. package/plugins/interact/src/index.test.js +22 -0
  116. package/plugins/interact/src/interact.scss +6 -0
  117. package/plugins/interact/src/manifest.js +1 -1
  118. package/plugins/interact/src/manifest.test.js +94 -0
  119. package/plugins/interact/src/reducer.js +28 -21
  120. package/plugins/interact/src/reducer.test.js +155 -0
  121. package/plugins/interact/src/utils/buildStylesMap.test.js +57 -0
  122. package/plugins/interact/src/utils/featureQueries.js +2 -2
  123. package/plugins/interact/src/utils/featureQueries.test.js +56 -0
  124. package/plugins/interact/src/utils/spatial.js +95 -0
  125. package/plugins/interact/src/utils/spatial.test.js +93 -0
  126. package/plugins/search/dist/css/index.css +1 -1
  127. package/plugins/search/dist/esm/im-search-plugin.js +1 -1
  128. package/plugins/search/dist/esm/index.js +1 -1
  129. package/plugins/search/dist/umd/im-search-plugin.js +1 -1
  130. package/plugins/search/dist/umd/index.js +1 -1
  131. package/plugins/search/src/Search.jsx +2 -2
  132. package/plugins/search/src/components/Form/Form.jsx +1 -1
  133. package/plugins/search/src/components/Suggestions/Suggestions.jsx +5 -5
  134. package/plugins/search/src/datasets.js +1 -1
  135. package/plugins/search/src/defaults.js +3 -0
  136. package/plugins/search/src/events/fetchSuggestions.js +48 -53
  137. package/plugins/search/src/events/formHandlers.js +3 -2
  138. package/plugins/search/src/events/index.js +1 -1
  139. package/plugins/search/src/events/inputHandlers.js +3 -1
  140. package/plugins/search/src/events/suggestionHandlers.js +12 -3
  141. package/plugins/search/src/search.scss +4 -1
  142. package/plugins/search/src/utils/parseOsNamesResults.js +15 -19
  143. package/providers/{esri → beta/esri}/src/esriProvider.js +1 -1
  144. package/providers/{esri → beta/esri}/src/index.js +1 -1
  145. package/providers/{esri → beta/esri}/src/mapEvents.js +2 -2
  146. package/providers/beta/open-names/dist/esm/im-reverse-geocode.js +2 -0
  147. package/providers/beta/open-names/dist/esm/index.js +2 -0
  148. package/providers/beta/open-names/dist/umd/im-reverse-geocode.js +2 -0
  149. package/providers/beta/open-names/dist/umd/im-reverse-geocode.js.LICENSE.txt +1 -0
  150. package/providers/beta/open-names/dist/umd/index.js +2 -0
  151. package/providers/beta/open-names/dist/umd/index.js.LICENSE.txt +1 -0
  152. package/providers/maplibre/dist/esm/im-maplibre-framework.js +1 -1
  153. package/providers/maplibre/dist/esm/im-maplibre-framework.js.LICENSE.txt +1 -1
  154. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  155. package/providers/maplibre/dist/esm/index.js +1 -1
  156. package/providers/maplibre/dist/umd/im-maplibre-framework.js +1 -1
  157. package/providers/maplibre/dist/umd/im-maplibre-framework.js.LICENSE.txt +1 -1
  158. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  159. package/providers/maplibre/dist/umd/index.js +1 -1
  160. package/providers/maplibre/src/defaults.js +3 -2
  161. package/providers/maplibre/src/index.js +36 -20
  162. package/providers/maplibre/src/maplibreProvider.js +140 -11
  163. package/providers/maplibre/src/utils/detectWebgl.js +1 -2
  164. package/providers/maplibre/src/utils/highlightFeatures.js +78 -67
  165. package/providers/maplibre/src/utils/labels.js +174 -120
  166. package/providers/maplibre/src/utils/maplibreFixes.js +2 -2
  167. package/providers/maplibre/src/utils/queryFeatures.js +146 -0
  168. package/providers/maplibre/src/utils/spatial.js +14 -17
  169. package/sonar-project.properties +40 -3
  170. package/src/App/components/Actions/Actions.jsx +2 -2
  171. package/src/App/components/Actions/Actions.module.scss +2 -2
  172. package/src/App/components/KeyboardHelp/KeyboardHelp.jsx +3 -1
  173. package/src/App/components/KeyboardHelp/KeyboardHelp.test.jsx +9 -0
  174. package/src/App/components/Logo/Logo.jsx +1 -1
  175. package/src/App/components/MapButton/MapButton.jsx +217 -41
  176. package/src/App/components/MapButton/MapButton.module.scss +32 -21
  177. package/src/App/components/MapButton/MapButton.test.jsx +128 -84
  178. package/src/App/components/Markers/Markers.jsx +1 -1
  179. package/src/App/components/Markers/Markers.module.scss +0 -5
  180. package/src/App/components/Panel/Panel.jsx +75 -45
  181. package/src/App/components/Panel/Panel.module.scss +18 -24
  182. package/src/App/components/Panel/Panel.test.jsx +18 -0
  183. package/src/App/components/PopupMenu/PopupMenu.jsx +246 -0
  184. package/src/App/components/PopupMenu/PopupMenu.module.scss +70 -0
  185. package/src/App/components/PopupMenu/PopupMenu.test.jsx +304 -0
  186. package/src/App/components/Tooltip/Tooltip.jsx +4 -2
  187. package/src/App/components/Tooltip/getTooltipPosition.js +1 -1
  188. package/src/App/components/Viewport/MapController.jsx +1 -1
  189. package/src/App/components/Viewport/MapStatus.jsx +1 -1
  190. package/src/App/components/Viewport/Viewport.jsx +2 -2
  191. package/src/App/components/Viewport/Viewport.module.scss +2 -2
  192. package/src/App/controls/keyboardActions.js +3 -2
  193. package/src/App/controls/keyboardShortcuts.js +4 -2
  194. package/src/App/hooks/useButtonStateEvaluator.js +58 -45
  195. package/src/App/hooks/useButtonStateEvaluator.test.js +36 -3
  196. package/src/App/hooks/useCrossHairAPI.js +49 -47
  197. package/src/App/hooks/useFocusVisible.js +2 -2
  198. package/src/App/hooks/useInterfaceAPI.js +36 -1
  199. package/src/App/hooks/useKeyboardHint.js +1 -1
  200. package/src/App/hooks/useKeyboardShortcuts.js +4 -2
  201. package/src/App/hooks/useMapAnnouncements.js +34 -32
  202. package/src/App/hooks/useMapEvents.js +12 -1
  203. package/src/App/hooks/useMapProviderOverrides.js +3 -3
  204. package/src/App/hooks/useMapStateSync.js +7 -1
  205. package/src/App/hooks/useMapURLSync.js +6 -1
  206. package/src/App/hooks/useMarkersAPI.js +88 -50
  207. package/src/App/hooks/useMediaQueryDispatch.test.js +48 -0
  208. package/src/App/hooks/useModalPanelBehaviour.js +46 -40
  209. package/src/App/hooks/useResizeObserver.js +2 -2
  210. package/src/App/initialiseApp.js +45 -38
  211. package/src/App/layout/Layout.jsx +6 -4
  212. package/src/App/layout/layout.module.scss +13 -17
  213. package/src/App/registry/keyboardShortcutRegistry.js +12 -2
  214. package/src/App/registry/keyboardShortcutRegistry.test.js +28 -0
  215. package/src/App/registry/mergeManifests.js +1 -1
  216. package/src/App/registry/panelRegistry.js +1 -1
  217. package/src/App/registry/panelRegistry.test.js +30 -1
  218. package/src/App/registry/pluginRegistry.js +14 -51
  219. package/src/App/registry/pluginRegistry.test.js +54 -6
  220. package/src/App/renderer/HtmlElementHost.jsx +186 -0
  221. package/src/App/renderer/HtmlElementHost.test.jsx +231 -0
  222. package/src/App/renderer/mapButtons.js +12 -9
  223. package/src/App/renderer/mapButtons.test.js +6 -4
  224. package/src/App/renderer/mapControls.js +6 -0
  225. package/src/App/renderer/mapControls.test.js +10 -2
  226. package/src/App/renderer/mapPanels.js +39 -26
  227. package/src/App/renderer/mapPanels.test.js +29 -0
  228. package/src/App/renderer/pluginWrapper.test.js +28 -0
  229. package/src/App/renderer/slotHelpers.js +60 -0
  230. package/src/App/renderer/slotHelpers.test.js +82 -0
  231. package/src/App/store/AppProvider.jsx +3 -0
  232. package/src/App/store/AppProvider.test.jsx +40 -0
  233. package/src/App/store/PluginProvider.jsx +7 -5
  234. package/src/App/store/appActionsMap.js +46 -1
  235. package/src/App/store/appActionsMap.test.js +21 -0
  236. package/src/App/store/appReducer.js +1 -0
  237. package/src/App/store/mapActionsMap.js +12 -24
  238. package/src/App/store/mapReducer.js +1 -1
  239. package/src/InteractiveMap/InteractiveMap.js +194 -13
  240. package/src/InteractiveMap/InteractiveMap.test.js +164 -2
  241. package/src/InteractiveMap/behaviourController.js +12 -2
  242. package/src/InteractiveMap/behaviourController.test.js +82 -1
  243. package/src/InteractiveMap/deviceChecker.js +1 -1
  244. package/src/InteractiveMap/deviceChecker.test.js +2 -2
  245. package/src/InteractiveMap/historyManager.js +41 -5
  246. package/src/InteractiveMap/historyManager.test.js +37 -6
  247. package/src/InteractiveMap/polyfills.js +39 -0
  248. package/src/InteractiveMap/polyfills.test.js +127 -0
  249. package/src/config/appConfig.js +5 -5
  250. package/src/config/appConfig.test.js +107 -42
  251. package/src/config/defaults.js +30 -20
  252. package/src/config/events.js +110 -4
  253. package/src/index.js +5 -2
  254. package/src/index.umd.js +1 -1
  255. package/src/scss/main.scss +1 -0
  256. package/src/scss/settings/_dimensions.scss +6 -2
  257. package/src/scss/tools/_border-focus.scss +20 -4
  258. package/src/services/eventBus.test.js +24 -1
  259. package/src/test-utils.js +1 -0
  260. package/src/types.js +590 -0
  261. package/src/utils/detectBreakpoint.js +78 -77
  262. package/src/utils/detectBreakpoint.test.js +50 -4
  263. package/src/utils/detectInterfaceType.js +4 -4
  264. package/src/utils/getIsFullscreen.js +3 -1
  265. package/src/utils/getSafeZoneInset.js +23 -7
  266. package/src/utils/getSafeZoneInset.test.js +18 -0
  267. package/src/utils/mapStateSync.js +3 -3
  268. package/src/utils/queryString.js +1 -1
  269. package/src/utils/stringToKebab.js +1 -1
  270. package/src/utils/toggleInertElements.js +37 -12
  271. package/webpack.dev.mjs +3 -4
  272. package/webpack.esm.mjs +8 -8
  273. package/webpack.umd.mjs +8 -8
  274. package/plugins/datasets/dist/esm/im-datasets-plugin.js +0 -1
  275. package/plugins/datasets/dist/esm/index.js +0 -2
  276. package/plugins/datasets/dist/umd/im-datasets-plugin.js +0 -1
  277. package/plugins/datasets/dist/umd/index.js +0 -2
  278. package/plugins/datasets/src/datasets.js +0 -60
  279. package/plugins/datasets/src/utils/bbox.js +0 -4
  280. package/plugins/draw-ml/dist/css/index.css +0 -1
  281. package/plugins/draw-ml/dist/esm/im-draw-ml-plugin.js +0 -1
  282. package/plugins/draw-ml/dist/esm/index.js +0 -2
  283. package/plugins/draw-ml/dist/umd/im-draw-ml-plugin.js +0 -1
  284. package/plugins/draw-ml/dist/umd/index.js +0 -2
  285. package/plugins/draw-ml/src/api/addFeature.js +0 -15
  286. package/plugins/draw-ml/src/api/newPolygon.js +0 -43
  287. package/plugins/draw-ml/src/draw.scss +0 -9
  288. package/plugins/draw-ml/src/events.js +0 -127
  289. package/plugins/draw-ml/src/manifest.js +0 -100
  290. package/plugins/draw-ml/src/modes/drawVertexMode.js +0 -275
  291. package/plugins/draw-ml/src/modes/editVertexMode.js +0 -426
  292. package/plugins/draw-ml/src/utils.js +0 -102
  293. package/plugins/frame/dist/esm/im-frame-plugin.js +0 -1
  294. package/plugins/frame/dist/esm/index.js +0 -2
  295. package/plugins/frame/dist/umd/im-frame-plugin.js +0 -1
  296. package/plugins/frame/dist/umd/index.js +0 -2
  297. package/plugins/map-styles/dist/esm/im-map-styles-plugin.js +0 -1
  298. package/plugins/map-styles/dist/esm/index.js +0 -2
  299. package/plugins/map-styles/dist/umd/im-map-styles-plugin.js +0 -1
  300. package/plugins/map-styles/dist/umd/index.js +0 -2
  301. package/plugins/scale-bar/dist/esm/im-scale-bar-plugin.js +0 -1
  302. package/plugins/scale-bar/dist/esm/index.js +0 -2
  303. package/plugins/scale-bar/dist/umd/im-scale-bar-plugin.js +0 -1
  304. package/plugins/scale-bar/dist/umd/index.js +0 -2
  305. package/plugins/search/src/utils/fetchDataset.js +0 -23
  306. package/plugins/use-location/dist/esm/im-use-location-plugin.js +0 -1
  307. package/plugins/use-location/dist/esm/index.js +0 -2
  308. package/plugins/use-location/dist/umd/im-use-location-plugin.js +0 -1
  309. package/plugins/use-location/dist/umd/index.js +0 -2
  310. package/providers/maplibre/dist/esm/im-maplibre-legacy-framework.js +0 -1
  311. package/providers/maplibre/dist/umd/im-maplibre-legacy-framework.js +0 -1
  312. package/providers/open-names/dist/esm/im-reverse-geocode.js +0 -2
  313. package/providers/open-names/dist/esm/index.js +0 -2
  314. package/providers/open-names/dist/umd/im-reverse-geocode.js +0 -2
  315. package/providers/open-names/dist/umd/index.js +0 -2
  316. /package/docs/architecture/{ARCHITECTURE_DIAGRAMS.md → architecture-diagrams.md} +0 -0
  317. /package/plugins/{datasets → beta/datasets}/dist/css/index.css +0 -0
  318. /package/plugins/{datasets/dist/esm/index.js.LICENSE.txt → beta/datasets/dist/esm/im-datasets-plugin.js.LICENSE.txt} +0 -0
  319. /package/plugins/{datasets/dist/umd → beta/datasets/dist/esm}/index.js.LICENSE.txt +0 -0
  320. /package/plugins/{draw-ml/dist/esm/index.js.LICENSE.txt → beta/datasets/dist/umd/im-datasets-plugin.js.LICENSE.txt} +0 -0
  321. /package/plugins/{draw-ml → beta/datasets}/dist/umd/index.js.LICENSE.txt +0 -0
  322. /package/plugins/{datasets → beta/datasets}/src/DatasetsInit.jsx +0 -0
  323. /package/plugins/{datasets → beta/datasets}/src/api/addDataset.js +0 -0
  324. /package/plugins/{datasets → beta/datasets}/src/api/hideDataset.js +0 -0
  325. /package/plugins/{datasets → beta/datasets}/src/api/hideFeatures.js +0 -0
  326. /package/plugins/{datasets → beta/datasets}/src/api/removeDataset.js +0 -0
  327. /package/plugins/{datasets → beta/datasets}/src/api/showDataset.js +0 -0
  328. /package/plugins/{datasets → beta/datasets}/src/api/showFeatures.js +0 -0
  329. /package/plugins/{datasets → beta/datasets}/src/datasets.scss +0 -0
  330. /package/plugins/{datasets → beta/datasets}/src/defaults.js +0 -0
  331. /package/plugins/{datasets → beta/datasets}/src/index.js +0 -0
  332. /package/plugins/{datasets → beta/datasets}/src/panels/Key.module.scss +0 -0
  333. /package/plugins/{datasets → beta/datasets}/src/panels/Layers.jsx +0 -0
  334. /package/plugins/{datasets → beta/datasets}/src/panels/Layers.module.scss +0 -0
  335. /package/plugins/{datasets → beta/datasets}/src/reducer.js +0 -0
  336. /package/plugins/{datasets → beta/datasets}/src/utils/debounce.js +0 -0
  337. /package/plugins/{datasets → beta/datasets}/src/utils/filters.js +0 -0
  338. /package/plugins/{draw-es → beta/draw-es}/src/DrawInit.jsx +0 -0
  339. /package/plugins/{draw-es → beta/draw-es}/src/api/addFeature.js +0 -0
  340. /package/plugins/{draw-es → beta/draw-es}/src/api/deleteFeature.js +0 -0
  341. /package/plugins/{draw-es → beta/draw-es}/src/api/editFeature.js +0 -0
  342. /package/plugins/{draw-es → beta/draw-es}/src/api/newPolygon.js +0 -0
  343. /package/plugins/{draw-es → beta/draw-es}/src/events.js +0 -0
  344. /package/plugins/{draw-es → beta/draw-es}/src/graphic.js +0 -0
  345. /package/plugins/{draw-es → beta/draw-es}/src/index.js +0 -0
  346. /package/plugins/{draw-es → beta/draw-es}/src/manifest.js +0 -0
  347. /package/plugins/{draw-es → beta/draw-es}/src/reducer.js +0 -0
  348. /package/plugins/{draw-es → beta/draw-es}/src/sketchViewModel.js +0 -0
  349. /package/plugins/{frame → beta/draw-ml}/dist/esm/index.js.LICENSE.txt +0 -0
  350. /package/plugins/{frame → beta/draw-ml}/dist/umd/index.js.LICENSE.txt +0 -0
  351. /package/plugins/{draw-ml → beta/draw-ml}/src/DrawInit.jsx +0 -0
  352. /package/plugins/{draw-ml → beta/draw-ml}/src/api/deleteFeature.js +0 -0
  353. /package/plugins/{draw-ml → beta/draw-ml}/src/index.js +0 -0
  354. /package/plugins/{draw-ml → beta/draw-ml}/src/modes/disabledMode.js +0 -0
  355. /package/plugins/{draw-ml/src → beta/draw-ml/src/utils}/snapHelpers.js +0 -0
  356. /package/plugins/{frame → beta/frame}/dist/css/index.css +0 -0
  357. /package/plugins/{map-styles → beta/frame}/dist/esm/index.js.LICENSE.txt +0 -0
  358. /package/plugins/{map-styles → beta/frame}/dist/umd/index.js.LICENSE.txt +0 -0
  359. /package/plugins/{frame → beta/frame}/package.json +0 -0
  360. /package/plugins/{frame → beta/frame}/src/FrameInit.jsx +0 -0
  361. /package/plugins/{frame → beta/frame}/src/api/addFrame.js +0 -0
  362. /package/plugins/{frame → beta/frame}/src/api/editFeature.js +0 -0
  363. /package/plugins/{frame → beta/frame}/src/config.js +0 -0
  364. /package/plugins/{frame → beta/frame}/src/frame.scss +0 -0
  365. /package/plugins/{frame → beta/frame}/src/index.js +0 -0
  366. /package/plugins/{frame → beta/frame}/src/manifest.js +0 -0
  367. /package/plugins/{frame → beta/frame}/src/reducer.js +0 -0
  368. /package/plugins/{frame → beta/frame}/src/utils.js +0 -0
  369. /package/plugins/{scale-bar → beta/map-styles}/dist/esm/index.js.LICENSE.txt +0 -0
  370. /package/plugins/{scale-bar → beta/map-styles}/dist/umd/index.js.LICENSE.txt +0 -0
  371. /package/plugins/{map-styles → beta/map-styles}/src/MapStylesInit.jsx +0 -0
  372. /package/plugins/{map-styles → beta/map-styles}/src/config.js +0 -0
  373. /package/plugins/{map-styles → beta/map-styles}/src/index.js +0 -0
  374. /package/plugins/{scale-bar → beta/scale-bar}/dist/css/index.css +0 -0
  375. /package/plugins/{use-location → beta/scale-bar}/dist/esm/index.js.LICENSE.txt +0 -0
  376. /package/plugins/{use-location → beta/scale-bar}/dist/umd/index.js.LICENSE.txt +0 -0
  377. /package/plugins/{scale-bar → beta/scale-bar}/package.json +0 -0
  378. /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.jsx +0 -0
  379. /package/plugins/{scale-bar → beta/scale-bar}/src/ScaleBar.test.jsx +0 -0
  380. /package/plugins/{scale-bar → beta/scale-bar}/src/index.js +0 -0
  381. /package/plugins/{scale-bar → beta/scale-bar}/src/index.test.js +0 -0
  382. /package/plugins/{scale-bar → beta/scale-bar}/src/manifest.js +0 -0
  383. /package/plugins/{scale-bar → beta/scale-bar}/src/scaleBar.scss +0 -0
  384. /package/plugins/{scale-bar → beta/scale-bar}/src/utils.js +0 -0
  385. /package/plugins/{scale-bar → beta/scale-bar}/src/utils.test.js +0 -0
  386. /package/{providers/open-names → plugins/beta/use-location}/dist/esm/index.js.LICENSE.txt +0 -0
  387. /package/{providers/open-names → plugins/beta/use-location}/dist/umd/index.js.LICENSE.txt +0 -0
  388. /package/plugins/{use-location → beta/use-location}/src/UseLocation.jsx +0 -0
  389. /package/plugins/{use-location → beta/use-location}/src/UseLocationInit.jsx +0 -0
  390. /package/plugins/{use-location → beta/use-location}/src/defaults.js +0 -0
  391. /package/plugins/{use-location → beta/use-location}/src/events.js +0 -0
  392. /package/plugins/{use-location → beta/use-location}/src/index.js +0 -0
  393. /package/plugins/{use-location → beta/use-location}/src/manifest.js +0 -0
  394. /package/plugins/{use-location → beta/use-location}/src/reducer.js +0 -0
  395. /package/providers/{esri → beta/esri}/src/appEvents.js +0 -0
  396. /package/providers/{esri → beta/esri}/src/defaults.js +0 -0
  397. /package/providers/{esri → beta/esri}/src/esriProvider.scss +0 -0
  398. /package/providers/{esri → beta/esri}/src/utils/coords.js +0 -0
  399. /package/providers/{esri → beta/esri}/src/utils/detectWebGL.js +0 -0
  400. /package/providers/{esri → beta/esri}/src/utils/esriFixes.js +0 -0
  401. /package/providers/{esri → beta/esri}/src/utils/query.js +0 -0
  402. /package/providers/{esri → beta/esri}/src/utils/spatial.js +0 -0
  403. /package/providers/{open-names → beta/open-names}/dist/esm/im-reverse-geocode.js.LICENSE.txt +0 -0
  404. /package/providers/{open-names/dist/umd/im-reverse-geocode.js.LICENSE.txt → beta/open-names/dist/esm/index.js.LICENSE.txt} +0 -0
  405. /package/providers/{open-names → beta/open-names}/src/index.js +0 -0
  406. /package/providers/{open-names → beta/open-names}/src/reverseGeocode.js +0 -0
  407. /package/providers/{open-names → beta/open-names}/src/utils/mapToLocationModel.js +0 -0
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Generic reducer action that shallow-merges the payload into state.
3
+ * Shared by MAP_MOVE, MAP_MOVE_END and MAP_FIRST_IDLE.
4
+ */
5
+ const mergePayload = (state, payload) => ({
6
+ ...state,
7
+ ...payload
8
+ })
9
+
1
10
  const setMapReady = (state) => {
2
11
  return {
3
12
  ...state,
@@ -5,27 +14,6 @@ const setMapReady = (state) => {
5
14
  }
6
15
  }
7
16
 
8
- const mapMove = (state, payload) => {
9
- return {
10
- ...state,
11
- ...payload
12
- }
13
- }
14
-
15
- const mapMoveEnd = (state, payload) => {
16
- return {
17
- ...state,
18
- ...payload
19
- }
20
- }
21
-
22
- const mapFirstIdle = (state, payload) => {
23
- return {
24
- ...state,
25
- ...payload
26
- }
27
- }
28
-
29
17
  const setMapStyle = (state, payload) => {
30
18
  return {
31
19
  ...state,
@@ -78,9 +66,9 @@ const removeMarker = (state, payload) => {
78
66
 
79
67
  export const actionsMap = {
80
68
  SET_MAP_READY: setMapReady,
81
- MAP_MOVE: mapMove,
82
- MAP_MOVE_END: mapMoveEnd,
83
- MAP_FIRST_IDLE: mapFirstIdle,
69
+ MAP_MOVE: mergePayload,
70
+ MAP_MOVE_END: mergePayload,
71
+ MAP_FIRST_IDLE: mergePayload,
84
72
  SET_MAP_STYLE: setMapStyle,
85
73
  SET_MAP_SIZE: setMapSize,
86
74
  UPDATE_CROSS_HAIR: updateCrossHair,
@@ -19,7 +19,7 @@ export const initialState = (config) => {
19
19
 
20
20
  return {
21
21
  isMapReady: false,
22
- mapStyle: !pluginHandlesMapStyles ? mapStyle : null,
22
+ mapStyle: pluginHandlesMapStyles ? null : mapStyle,
23
23
  mapSize,
24
24
  center,
25
25
  zoom,
@@ -1,5 +1,16 @@
1
1
  // src/InteractiveMap/InteractiveMap.js
2
+
3
+ /**
4
+ * @typedef {import('../types.js').ButtonDefinition} ButtonDefinition
5
+ * @typedef {import('../types.js').ControlDefinition} ControlDefinition
6
+ * @typedef {import('../types.js').InteractiveMapConfig} InteractiveMapConfig
7
+ * @typedef {import('../types.js').MarkerOptions} MarkerOptions
8
+ * @typedef {import('../types.js').PanelDefinition} PanelDefinition
9
+ */
10
+
2
11
  import '../scss/main.scss'
12
+ // Polyfills to ensure entry point works on all devices
13
+ import './polyfills.js'
3
14
  import historyManager from './historyManager.js'
4
15
  import { parseDataProperties } from './parseDataProperties.js'
5
16
  import { checkDeviceSupport } from './deviceChecker.js'
@@ -13,14 +24,26 @@ import { createInterfaceDetector, getInterfaceType } from '../utils/detectInterf
13
24
  import { createReverseGeocode } from '../services/reverseGeocode.js'
14
25
  import { EVENTS as events } from '../config/events.js'
15
26
  import { createEventBus } from '../services/eventBus.js'
27
+ import { toggleInertElements } from '../utils/toggleInertElements.js'
16
28
 
29
+ /**
30
+ * Main entry point for the Interactive Map component.
31
+ * Handles initialization, lifecycle, and public API methods.
32
+ */
17
33
  export default class InteractiveMap {
18
34
  _openButton = null
19
35
  _root = null // keep react root internally
20
36
  _breakpointDetector = null
21
37
  _interfaceDetectorCleanup = null
22
38
  _hybridBehaviourCleanup = null
23
-
39
+ _isHidden = false // tracks if map is hidden but preserved (hybrid mode)
40
+
41
+ /**
42
+ * Create a new InteractiveMap instance.
43
+ *
44
+ * @param {string} id - The DOM element ID to mount the map into.
45
+ * @param {Partial<InteractiveMapConfig>} [props={}] - Configuration options.
46
+ */
24
47
  constructor (id, props = {}) {
25
48
  this.id = id
26
49
  this.rootEl = document.getElementById(id)
@@ -62,7 +85,6 @@ export default class InteractiveMap {
62
85
  })
63
86
  }
64
87
 
65
- // Private methods
66
88
  _initialize () {
67
89
  if (['buttonFirst', 'hybrid'].includes(this.config.behaviour)) {
68
90
  this._openButton = createButton(this.config, this.rootEl, (e) => {
@@ -80,20 +102,48 @@ export default class InteractiveMap {
80
102
  }
81
103
 
82
104
  _handleButtonClick (e) {
83
- this.loadApp()
84
105
  history.pushState({ isBack: true }, '', e.currentTarget.getAttribute('href'))
106
+ if (this._isHidden) {
107
+ this.showApp()
108
+ } else {
109
+ this.loadApp()
110
+ }
111
+ }
112
+
113
+ _removeMapParamFromUrl (href, key) {
114
+ const regex = new RegExp(`[?&]${key}=[^&]*(&|$)`)
115
+
116
+ if (!regex.test(href)) {
117
+ return href
118
+ }
119
+
120
+ return href
121
+ .replace(regex, (_, p1) => {
122
+ return p1 === '&' ? '?' : ''
123
+ })
124
+ .replace(/\?$/, '')
85
125
  }
86
126
 
87
127
  _handleExitClick () {
88
- this.removeApp()
89
- // Remove the map param from the URL using regex to prevent encoding
128
+ if (this.config.preserveStateOnClose) {
129
+ this.hideApp()
130
+ } else {
131
+ this.removeApp()
132
+ }
133
+
90
134
  const key = this.config.mapViewParamKey
91
135
  const href = location.href
92
- const newUrl = href.replace(new RegExp(`[?&]${key}=[^&]*(&|$)`), (_, p1) => (p1 === '&' ? '?' : '')).replace(/\?$/, '')
136
+ const newUrl = this._removeMapParamFromUrl(href, key)
137
+
93
138
  history.replaceState(history.state, '', newUrl)
94
139
  }
95
140
 
96
- // Public methods
141
+ /**
142
+ * Load and initialize the map application.
143
+ *
144
+ * @internal Not intended for end-user use.
145
+ * @returns {Promise<void>}
146
+ */
97
147
  async loadApp () {
98
148
  if (this._openButton) {
99
149
  this._openButton.style.display = 'none'
@@ -130,10 +180,10 @@ export default class InteractiveMap {
130
180
  delete appInstance._root
131
181
 
132
182
  // Only assign properties but don't eventBus methods
133
- const protectedKeys = ['on', 'off', 'emit']
183
+ const protectedKeys = new Set(['on', 'off', 'emit'])
134
184
 
135
185
  Object.keys(appInstance).forEach(key => {
136
- if (!protectedKeys.includes(key)) {
186
+ if (!protectedKeys.has(key)) {
137
187
  this[key] = appInstance[key]
138
188
  }
139
189
  })
@@ -146,6 +196,11 @@ export default class InteractiveMap {
146
196
  }
147
197
  }
148
198
 
199
+ /**
200
+ * Remove the map application and restore the initial state.
201
+ *
202
+ * @internal Not intended for end-user use.
203
+ */
149
204
  removeApp () {
150
205
  if (this._root && typeof this.unmount === 'function') {
151
206
  this.unmount()
@@ -162,6 +217,57 @@ export default class InteractiveMap {
162
217
  this.eventBus.emit(events.MAP_DESTROY, { mapId: this.id })
163
218
  }
164
219
 
220
+ /**
221
+ * Hide the map application without destroying it (preserves state).
222
+ * Used in hybrid mode when resizing below breakpoint.
223
+ *
224
+ * @internal Not intended for end-user use.
225
+ */
226
+ hideApp () {
227
+ this._isHidden = true
228
+ this.rootEl.style.display = 'none'
229
+
230
+ // Restore inert elements before focusing button
231
+ toggleInertElements({ containerEl: this.rootEl, isFullscreen: false })
232
+
233
+ if (this._openButton) {
234
+ this._openButton.removeAttribute('style')
235
+ this._openButton.focus()
236
+ }
237
+
238
+ // Remove fullscreen classes
239
+ document.documentElement.classList.remove('im-is-fullscreen')
240
+ this.rootEl.classList.remove('im-is-fullscreen')
241
+
242
+ // Reset page title (remove prepended map title)
243
+ const parts = document.title.split(': ')
244
+ if (parts.length > 1) {
245
+ document.title = parts[parts.length - 1]
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Show a previously hidden map application.
251
+ * Used in hybrid mode when resizing above breakpoint or clicking button.
252
+ *
253
+ * @internal Not intended for end-user use.
254
+ */
255
+ showApp () {
256
+ this._isHidden = false
257
+ this.rootEl.style.display = ''
258
+
259
+ if (this._openButton) {
260
+ this._openButton.style.display = 'none'
261
+ }
262
+
263
+ updateDOMState(this)
264
+ }
265
+
266
+ /**
267
+ * Destroy the map instance and clean up all resources.
268
+ *
269
+ * @internal Not intended for end-user use.
270
+ */
165
271
  destroy () {
166
272
  this.removeApp()
167
273
  this._breakpointDetector?.destroy()
@@ -171,54 +277,129 @@ export default class InteractiveMap {
171
277
  this.eventBus.destroy()
172
278
  }
173
279
 
174
- // API - EventBus methods
280
+ /**
281
+ * Subscribe to an event.
282
+ *
283
+ * @param {string} event - Event name.
284
+ * @param {Function} callback - Event handler.
285
+ */
175
286
  on (...args) {
176
287
  this.eventBus.on(...args)
177
288
  }
178
289
 
290
+ /**
291
+ * Unsubscribe from an event.
292
+ *
293
+ * @param {string} event - Event name.
294
+ * @param {Function} callback - Event handler to remove.
295
+ */
179
296
  off (...args) {
180
297
  this.eventBus.off(...args)
181
298
  }
182
299
 
300
+ /**
301
+ * Emit an event.
302
+ *
303
+ * @param {string} event - Event name.
304
+ * @param {any} [data] - Event data.
305
+ */
183
306
  emit (...args) {
184
307
  this.eventBus.emit(...args)
185
308
  }
186
309
 
187
- // API - location markers
310
+ /**
311
+ * Add a marker to the map.
312
+ *
313
+ * @param {string} id - Unique marker identifier.
314
+ * @param {[number, number]} coords - Coordinates [lng, lat] or [easting, northing] depending on crs.
315
+ * @param {MarkerOptions} [options] - Optional marker appearance options.
316
+ */
188
317
  addMarker (id, coords, options) {
189
318
  this.eventBus.emit(events.APP_ADD_MARKER, { id, coords, options })
190
319
  }
191
320
 
321
+ /**
322
+ * Remove a marker from the map.
323
+ *
324
+ * @param {string} id - Marker identifier to remove.
325
+ */
192
326
  removeMarker (id) {
193
327
  this.eventBus.emit(events.APP_REMOVE_MARKER, id)
194
328
  }
195
329
 
196
- // API - change app mode
330
+ /**
331
+ * Set the application mode.
332
+ *
333
+ * @param {string} mode - Mode identifier.
334
+ */
197
335
  setMode (mode) {
198
336
  this.eventBus.emit(events.APP_SET_MODE, mode)
199
337
  }
200
338
 
201
- // Interface API add button/panel/control, remove panel
339
+ /**
340
+ * Add a button to the UI.
341
+ *
342
+ * @param {string} id - Unique button identifier.
343
+ * @param {ButtonDefinition} config - Button configuration.
344
+ */
202
345
  addButton (id, config) {
203
346
  this.eventBus.emit(events.APP_ADD_BUTTON, { id, config })
204
347
  }
205
348
 
349
+ /**
350
+ * Set or toggle a button state.
351
+ *
352
+ * @param {string} id - Button identifier.
353
+ * @param {'hidden'|'pressed'|'disabled'} prop - The button state to change.
354
+ * @param {boolean} [value] - Optional boolean. If provided, sets state explicitly; otherwise toggles.
355
+ */
356
+ toggleButtonState (id, prop, value) {
357
+ this.eventBus.emit(events.APP_TOGGLE_BUTTON_STATE, { id, prop, value })
358
+ }
359
+
360
+ /**
361
+ * Add a panel to the UI.
362
+ *
363
+ * @param {string} id - Unique panel identifier.
364
+ * @param {PanelDefinition} config - Panel configuration.
365
+ */
206
366
  addPanel (id, config) {
207
367
  this.eventBus.emit(events.APP_ADD_PANEL, { id, config })
208
368
  }
209
369
 
370
+ /**
371
+ * Remove a panel from the UI.
372
+ *
373
+ * @param {string} id - Panel identifier to remove.
374
+ */
210
375
  removePanel (id) {
211
376
  this.eventBus.emit(events.APP_REMOVE_PANEL, id)
212
377
  }
213
378
 
379
+ /**
380
+ * Show a panel.
381
+ *
382
+ * @param {string} id - Panel identifier to show.
383
+ */
214
384
  showPanel (id) {
215
385
  this.eventBus.emit(events.APP_SHOW_PANEL, id)
216
386
  }
217
387
 
388
+ /**
389
+ * Hide a panel.
390
+ *
391
+ * @param {string} id - Panel identifier to hide.
392
+ */
218
393
  hidePanel (id) {
219
394
  this.eventBus.emit(events.APP_HIDE_PANEL, id)
220
395
  }
221
396
 
397
+ /**
398
+ * Add a custom control to the UI.
399
+ *
400
+ * @param {string} id - Unique control identifier.
401
+ * @param {ControlDefinition} config - Control configuration.
402
+ */
222
403
  addControl (id, config) {
223
404
  this.eventBus.emit(events.APP_ADD_CONTROL, { id, config })
224
405
  }
@@ -268,20 +268,23 @@ describe('InteractiveMap Core Functionality', () => {
268
268
  expect(() => map.destroy()).not.toThrow()
269
269
  })
270
270
 
271
- it('_handleExitClick removes app and calls replaceState', () => {
271
+ it('_handleExitClick removes app when preserveStateOnClose is false', () => {
272
272
  const replaceStateSpy = jest.spyOn(history, 'replaceState').mockImplementation(() => {})
273
273
 
274
274
  const map = new InteractiveMap('map', {
275
275
  behaviour: 'buttonFirst',
276
276
  mapProvider: mapProviderMock,
277
- mapViewParamKey: 'mv'
277
+ mapViewParamKey: 'mv',
278
+ preserveStateOnClose: false
278
279
  })
279
280
 
280
281
  const removeAppSpy = jest.spyOn(map, 'removeApp').mockImplementation(() => {})
282
+ const hideAppSpy = jest.spyOn(map, 'hideApp').mockImplementation(() => {})
281
283
 
282
284
  map._handleExitClick()
283
285
 
284
286
  expect(removeAppSpy).toHaveBeenCalled()
287
+ expect(hideAppSpy).not.toHaveBeenCalled()
285
288
  expect(replaceStateSpy).toHaveBeenCalledWith(
286
289
  history.state,
287
290
  '',
@@ -289,8 +292,158 @@ describe('InteractiveMap Core Functionality', () => {
289
292
  )
290
293
 
291
294
  removeAppSpy.mockRestore()
295
+ hideAppSpy.mockRestore()
292
296
  replaceStateSpy.mockRestore()
293
297
  })
298
+
299
+ it('_handleExitClick hides app when preserveStateOnClose is true', () => {
300
+ const replaceStateSpy = jest.spyOn(history, 'replaceState').mockImplementation(() => {})
301
+
302
+ const map = new InteractiveMap('map', {
303
+ behaviour: 'buttonFirst',
304
+ mapProvider: mapProviderMock,
305
+ mapViewParamKey: 'mv',
306
+ preserveStateOnClose: true
307
+ })
308
+
309
+ const removeAppSpy = jest.spyOn(map, 'removeApp').mockImplementation(() => {})
310
+ const hideAppSpy = jest.spyOn(map, 'hideApp').mockImplementation(() => {})
311
+
312
+ map._handleExitClick()
313
+
314
+ expect(hideAppSpy).toHaveBeenCalled()
315
+ expect(removeAppSpy).not.toHaveBeenCalled()
316
+ expect(replaceStateSpy).toHaveBeenCalled()
317
+
318
+ removeAppSpy.mockRestore()
319
+ hideAppSpy.mockRestore()
320
+ replaceStateSpy.mockRestore()
321
+ })
322
+
323
+ it('_handleButtonClick calls showApp when map is hidden', async () => {
324
+ const map = new InteractiveMap('map', { behaviour: 'buttonFirst', mapProvider: mapProviderMock })
325
+ map._isHidden = true
326
+ const showAppSpy = jest.spyOn(map, 'showApp').mockImplementation(() => {})
327
+ const loadAppSpy = jest.spyOn(map, 'loadApp').mockResolvedValue()
328
+ const pushStateSpy = jest.spyOn(history, 'pushState').mockImplementation(() => {})
329
+ const fakeEvent = { currentTarget: { getAttribute: jest.fn().mockReturnValue('/?mv=map') } }
330
+
331
+ await openButtonCallback(fakeEvent)
332
+
333
+ expect(showAppSpy).toHaveBeenCalled()
334
+ expect(loadAppSpy).not.toHaveBeenCalled()
335
+ expect(pushStateSpy).toHaveBeenCalled()
336
+
337
+ showAppSpy.mockRestore()
338
+ loadAppSpy.mockRestore()
339
+ pushStateSpy.mockRestore()
340
+ })
341
+
342
+ it('hideApp sets _isHidden and hides element', () => {
343
+ const map = new InteractiveMap('map', { behaviour: 'buttonFirst', mapProvider: mapProviderMock })
344
+ map._openButton = mockButtonInstance
345
+
346
+ map.hideApp()
347
+
348
+ expect(map._isHidden).toBe(true)
349
+ expect(map.rootEl.style.display).toBe('none')
350
+ expect(mockButtonInstance.removeAttribute).toHaveBeenCalledWith('style')
351
+ expect(mockButtonInstance.focus).toHaveBeenCalled()
352
+ })
353
+
354
+ it('hideApp restores document title when it contains a map prefix', () => {
355
+ document.title = 'Map View: Original Page Title'
356
+
357
+ const map = new InteractiveMap('map', {
358
+ behaviour: 'buttonFirst',
359
+ mapProvider: mapProviderMock
360
+ })
361
+
362
+ map._openButton = mockButtonInstance
363
+ map.hideApp()
364
+
365
+ expect(document.title).toBe('Original Page Title')
366
+ })
367
+
368
+ it('hideApp works when _openButton is null', () => {
369
+ const map = new InteractiveMap('map', {
370
+ behaviour: 'buttonFirst',
371
+ mapProvider: mapProviderMock
372
+ })
373
+
374
+ map._openButton = null
375
+ map.hideApp()
376
+
377
+ expect(map._isHidden).toBe(true)
378
+ expect(map.rootEl.style.display).toBe('none')
379
+ })
380
+
381
+ it('showApp sets _isHidden false and shows element', () => {
382
+ const map = new InteractiveMap('map', { behaviour: 'buttonFirst', mapProvider: mapProviderMock })
383
+ map._isHidden = true
384
+ map._openButton = mockButtonInstance
385
+ map.rootEl.style.display = 'none'
386
+
387
+ map.showApp()
388
+
389
+ expect(map._isHidden).toBe(false)
390
+ expect(map.rootEl.style.display).toBe('')
391
+ expect(mockButtonInstance.style.display).toBe('none')
392
+ expect(updateDOMState).toHaveBeenCalledWith(map)
393
+ })
394
+
395
+ it('showApp works when _openButton is null', () => {
396
+ const map = new InteractiveMap('map', {
397
+ behaviour: 'buttonFirst',
398
+ mapProvider: mapProviderMock
399
+ })
400
+
401
+ map._isHidden = true
402
+ map._openButton = null
403
+ map.rootEl.style.display = 'none'
404
+
405
+ map.showApp()
406
+
407
+ expect(map._isHidden).toBe(false)
408
+ expect(map.rootEl.style.display).toBe('')
409
+ expect(updateDOMState).toHaveBeenCalledWith(map)
410
+ })
411
+ })
412
+
413
+ describe('_removeMapParamFromUrl', () => {
414
+ let map
415
+
416
+ beforeEach(() => {
417
+ document.body.innerHTML = '<div id="map"></div>'
418
+ map = new InteractiveMap('map', {
419
+ mapProvider: { load: jest.fn() },
420
+ mapViewParamKey: 'mv'
421
+ })
422
+ })
423
+
424
+ it('removes param when followed by another param (& branch)', () => {
425
+ const href = 'https://example.com/page?mv=map&foo=1'
426
+ const result = map._removeMapParamFromUrl(href, 'mv')
427
+ expect(result).toBe('https://example.com/page?foo=1')
428
+ })
429
+
430
+ it('removes param when it is the last param (end-of-string branch)', () => {
431
+ const href = 'https://example.com/page?foo=1&mv=map'
432
+ const result = map._removeMapParamFromUrl(href, 'mv')
433
+ expect(result).toBe('https://example.com/page?foo=1')
434
+ })
435
+
436
+ it('removes lone param and trailing ?', () => {
437
+ const href = 'https://example.com/page?mv=map'
438
+ const result = map._removeMapParamFromUrl(href, 'mv')
439
+ expect(result).toBe('https://example.com/page')
440
+ })
441
+
442
+ it('returns href unchanged when param does not exist (no-match branch)', () => {
443
+ const href = 'https://example.com/page?foo=1'
444
+ const result = map._removeMapParamFromUrl(href, 'mv')
445
+ expect(result).toBe(href)
446
+ })
294
447
  })
295
448
 
296
449
  describe('InteractiveMap Public API Methods', () => {
@@ -347,4 +500,13 @@ describe('InteractiveMap Public API Methods', () => {
347
500
  expect(map.eventBus.emit).toHaveBeenCalledWith('app:showpanel', 'panel2')
348
501
  expect(map.eventBus.emit).toHaveBeenCalledWith('app:hidepanel', 'panel3')
349
502
  })
503
+
504
+ it('delegates toggleButtonState correctly', () => {
505
+ map.toggleButtonState('btn-1', 'disabled', true)
506
+
507
+ expect(map.eventBus.emit).toHaveBeenCalledWith(
508
+ 'app:togglebuttonstate',
509
+ { id: 'btn-1', prop: 'disabled', value: true }
510
+ )
511
+ })
350
512
  })
@@ -1,5 +1,6 @@
1
1
  import { getQueryParam } from '../utils/queryString.js'
2
2
  import { isHybridFullscreen } from '../utils/getIsFullscreen.js'
3
+ import { updateDOMState } from './domStateManager.js'
3
4
  import defaults from '../config/defaults.js'
4
5
 
5
6
  // -----------------------------------------------------------------------------
@@ -56,9 +57,18 @@ function setupBehavior (mapInstance) {
56
57
 
57
58
  const handleChange = () => {
58
59
  if (shouldLoadComponent(mapInstance.config)) {
59
- mapInstance.loadApp()
60
+ if (mapInstance._isHidden) {
61
+ mapInstance.showApp()
62
+ } else if (mapInstance._root == null) {
63
+ mapInstance.loadApp()
64
+ } else {
65
+ // Map is showing - update DOM state for fullscreen/inline transition
66
+ updateDOMState(mapInstance)
67
+ }
68
+ } else if (mapInstance._root) {
69
+ mapInstance.hideApp()
60
70
  } else {
61
- mapInstance.removeApp()
71
+ // No action
62
72
  }
63
73
  }
64
74