@mailwoman/react 10.0.0 → 10.2.0

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 (389) hide show
  1. package/README.md +18 -16
  2. package/fonts.css +97 -22
  3. package/lib/common/About.tsx +10 -10
  4. package/lib/common/ClientOnly.tsx +2 -14
  5. package/lib/common/CopyButton.tsx +9 -3
  6. package/lib/common/LoadingIndicator.tsx +13 -5
  7. package/lib/common/PresetChips.tsx +11 -8
  8. package/lib/common/confidence-tiers.ts +8 -7
  9. package/lib/common/cx.ts +3 -1
  10. package/lib/common/text-tokens.ts +10 -13
  11. package/lib/common/useBuildInfo.ts +54 -0
  12. package/lib/common/useClipboard.ts +2 -11
  13. package/lib/common.ts +22 -0
  14. package/lib/index.ts +27 -18
  15. package/lib/map/AppIdentity.tsx +89 -0
  16. package/lib/map/CompareToggle.tsx +8 -31
  17. package/lib/map/DebugInfo.tsx +75 -0
  18. package/lib/map/Geocoder.tsx +65 -50
  19. package/lib/map/GeocoderControls.tsx +425 -141
  20. package/lib/map/GraticuleLayer.tsx +56 -0
  21. package/lib/map/MapCanvas.tsx +26 -13
  22. package/lib/map/MapChipRow.tsx +4 -8
  23. package/lib/map/MapCompass.tsx +18 -25
  24. package/lib/map/MapControlStack.tsx +11 -10
  25. package/lib/map/MapFooter.tsx +5 -17
  26. package/lib/map/MapProgressBar.tsx +10 -13
  27. package/lib/map/MapSearchBar.tsx +31 -17
  28. package/lib/map/MapSheet.tsx +24 -15
  29. package/lib/map/OverlayLayers.tsx +14 -3
  30. package/lib/map/PlaceAutocomplete.tsx +12 -21
  31. package/lib/map/PlaceMarker.tsx +10 -4
  32. package/lib/map/ResolvedPlaceLayers.tsx +14 -15
  33. package/lib/map/ResultCamera.tsx +10 -31
  34. package/lib/map/ResultOverlay.tsx +14 -16
  35. package/lib/map/ResultPanel.tsx +1 -20
  36. package/lib/map/VersionPicker.tsx +10 -19
  37. package/lib/map/fake-runtime.ts +46 -19
  38. package/lib/map/geometry.ts +14 -47
  39. package/lib/map/graticule.ts +80 -0
  40. package/lib/map/map-debug.ts +4 -2
  41. package/lib/map/place-render.ts +30 -62
  42. package/lib/map/types.ts +57 -116
  43. package/lib/map/useCompareState.ts +5 -4
  44. package/lib/map/useGeocode.ts +7 -5
  45. package/lib/map/useMapBearing.ts +13 -13
  46. package/lib/map/useMapLabelPick.ts +21 -30
  47. package/lib/map/useMapPlaceRender.ts +3 -7
  48. package/lib/map/usePlaceAutocomplete.ts +44 -50
  49. package/lib/map.ts +92 -0
  50. package/lib/pipeline/CandidatePicker.tsx +13 -5
  51. package/lib/pipeline/ConfidenceCell.tsx +2 -2
  52. package/lib/pipeline/FailureDiagnostic.tsx +8 -2
  53. package/lib/pipeline/PipelineExplorer.tsx +6 -3
  54. package/lib/pipeline/QueryForm.tsx +5 -2
  55. package/lib/pipeline/ResolvedPlace.tsx +2 -2
  56. package/lib/pipeline/SpanHighlight.tsx +7 -5
  57. package/lib/pipeline/TimingPanel.tsx +1 -1
  58. package/lib/pipeline/TreeView.tsx +2 -2
  59. package/lib/pipeline/copy.ts +22 -25
  60. package/lib/pipeline/presets.ts +15 -13
  61. package/lib/pipeline/types.ts +20 -17
  62. package/lib/pipeline/useParsePipeline.ts +16 -13
  63. package/lib/pipeline.ts +36 -0
  64. package/lib/poi/AbstainPanel.tsx +1 -1
  65. package/lib/poi/LiveResultsBlock.tsx +2 -1
  66. package/lib/poi/POIExplorer.tsx +29 -17
  67. package/lib/poi/QueryInput.tsx +1 -1
  68. package/lib/poi/SubjectPanel.tsx +4 -3
  69. package/lib/poi/runtime.ts +14 -16
  70. package/lib/poi/types.ts +58 -47
  71. package/lib/poi/usePOISearch.ts +91 -82
  72. package/lib/poi.ts +40 -0
  73. package/lib/runtime/useReleaseRuntime.ts +122 -90
  74. package/lib/search/SearchModal.tsx +478 -0
  75. package/lib/search/search.css +282 -0
  76. package/lib/search/types.ts +68 -0
  77. package/lib/search.ts +11 -0
  78. package/out/common/About.d.ts +6 -6
  79. package/out/common/About.d.ts.map +1 -1
  80. package/out/common/About.js +1 -1
  81. package/out/common/About.js.map +1 -1
  82. package/out/common/ClientOnly.d.ts +2 -11
  83. package/out/common/ClientOnly.d.ts.map +1 -1
  84. package/out/common/ClientOnly.js +1 -13
  85. package/out/common/ClientOnly.js.map +1 -1
  86. package/out/common/CopyButton.d.ts +9 -3
  87. package/out/common/CopyButton.d.ts.map +1 -1
  88. package/out/common/CopyButton.js.map +1 -1
  89. package/out/common/LoadingIndicator.d.ts +13 -5
  90. package/out/common/LoadingIndicator.d.ts.map +1 -1
  91. package/out/common/LoadingIndicator.js.map +1 -1
  92. package/out/common/PresetChips.d.ts +11 -8
  93. package/out/common/PresetChips.d.ts.map +1 -1
  94. package/out/common/PresetChips.js.map +1 -1
  95. package/out/common/confidence-tiers.d.ts +8 -7
  96. package/out/common/confidence-tiers.d.ts.map +1 -1
  97. package/out/common/confidence-tiers.js +7 -6
  98. package/out/common/confidence-tiers.js.map +1 -1
  99. package/out/common/cx.d.ts +3 -1
  100. package/out/common/cx.d.ts.map +1 -1
  101. package/out/common/cx.js +3 -1
  102. package/out/common/cx.js.map +1 -1
  103. package/out/common/text-tokens.d.ts +6 -11
  104. package/out/common/text-tokens.d.ts.map +1 -1
  105. package/out/common/text-tokens.js +6 -3
  106. package/out/common/text-tokens.js.map +1 -1
  107. package/out/common/useBuildInfo.d.ts +27 -0
  108. package/out/common/useBuildInfo.d.ts.map +1 -0
  109. package/out/common/useBuildInfo.js +32 -0
  110. package/out/common/useBuildInfo.js.map +1 -0
  111. package/out/common/useClipboard.d.ts +1 -3
  112. package/out/common/useClipboard.d.ts.map +1 -1
  113. package/out/common/useClipboard.js +2 -11
  114. package/out/common/useClipboard.js.map +1 -1
  115. package/out/common.d.ts +22 -0
  116. package/out/common.d.ts.map +1 -0
  117. package/out/common.js +15 -0
  118. package/out/common.js.map +1 -0
  119. package/out/index.d.ts +26 -16
  120. package/out/index.d.ts.map +1 -1
  121. package/out/index.js +21 -15
  122. package/out/index.js.map +1 -1
  123. package/out/map/AppIdentity.d.ts +46 -0
  124. package/out/map/AppIdentity.d.ts.map +1 -0
  125. package/out/map/AppIdentity.js +16 -0
  126. package/out/map/AppIdentity.js.map +1 -0
  127. package/out/map/CompareToggle.d.ts +8 -31
  128. package/out/map/CompareToggle.d.ts.map +1 -1
  129. package/out/map/CompareToggle.js +4 -1
  130. package/out/map/CompareToggle.js.map +1 -1
  131. package/out/map/DebugInfo.d.ts +27 -0
  132. package/out/map/DebugInfo.d.ts.map +1 -0
  133. package/out/map/DebugInfo.js +36 -0
  134. package/out/map/DebugInfo.js.map +1 -0
  135. package/out/map/Geocoder.d.ts +41 -30
  136. package/out/map/Geocoder.d.ts.map +1 -1
  137. package/out/map/Geocoder.js +23 -37
  138. package/out/map/Geocoder.js.map +1 -1
  139. package/out/map/GeocoderControls.d.ts +15 -44
  140. package/out/map/GeocoderControls.d.ts.map +1 -1
  141. package/out/map/GeocoderControls.js +250 -39
  142. package/out/map/GeocoderControls.js.map +1 -1
  143. package/out/map/GraticuleLayer.d.ts +30 -0
  144. package/out/map/GraticuleLayer.d.ts.map +1 -0
  145. package/out/map/GraticuleLayer.js +25 -0
  146. package/out/map/GraticuleLayer.js.map +1 -0
  147. package/out/map/MapCanvas.d.ts +26 -13
  148. package/out/map/MapCanvas.d.ts.map +1 -1
  149. package/out/map/MapCanvas.js +5 -2
  150. package/out/map/MapCanvas.js.map +1 -1
  151. package/out/map/MapChipRow.d.ts +4 -8
  152. package/out/map/MapChipRow.d.ts.map +1 -1
  153. package/out/map/MapChipRow.js.map +1 -1
  154. package/out/map/MapCompass.d.ts +14 -21
  155. package/out/map/MapCompass.d.ts.map +1 -1
  156. package/out/map/MapCompass.js +5 -1
  157. package/out/map/MapCompass.js.map +1 -1
  158. package/out/map/MapControlStack.d.ts +11 -10
  159. package/out/map/MapControlStack.d.ts.map +1 -1
  160. package/out/map/MapControlStack.js +4 -1
  161. package/out/map/MapControlStack.js.map +1 -1
  162. package/out/map/MapFooter.d.ts +5 -16
  163. package/out/map/MapFooter.d.ts.map +1 -1
  164. package/out/map/MapFooter.js +0 -14
  165. package/out/map/MapFooter.js.map +1 -1
  166. package/out/map/MapProgressBar.d.ts +10 -13
  167. package/out/map/MapProgressBar.d.ts.map +1 -1
  168. package/out/map/MapProgressBar.js.map +1 -1
  169. package/out/map/MapSearchBar.d.ts +16 -17
  170. package/out/map/MapSearchBar.d.ts.map +1 -1
  171. package/out/map/MapSearchBar.js +6 -0
  172. package/out/map/MapSearchBar.js.map +1 -1
  173. package/out/map/MapSheet.d.ts +15 -12
  174. package/out/map/MapSheet.d.ts.map +1 -1
  175. package/out/map/MapSheet.js +8 -12
  176. package/out/map/MapSheet.js.map +1 -1
  177. package/out/map/OverlayLayers.d.ts +8 -2
  178. package/out/map/OverlayLayers.d.ts.map +1 -1
  179. package/out/map/OverlayLayers.js +3 -3
  180. package/out/map/OverlayLayers.js.map +1 -1
  181. package/out/map/PlaceAutocomplete.d.ts +12 -21
  182. package/out/map/PlaceAutocomplete.d.ts.map +1 -1
  183. package/out/map/PlaceAutocomplete.js +2 -1
  184. package/out/map/PlaceAutocomplete.js.map +1 -1
  185. package/out/map/PlaceMarker.d.ts +10 -4
  186. package/out/map/PlaceMarker.d.ts.map +1 -1
  187. package/out/map/PlaceMarker.js +3 -1
  188. package/out/map/PlaceMarker.js.map +1 -1
  189. package/out/map/ResolvedPlaceLayers.d.ts +14 -15
  190. package/out/map/ResolvedPlaceLayers.d.ts.map +1 -1
  191. package/out/map/ResolvedPlaceLayers.js +1 -1
  192. package/out/map/ResolvedPlaceLayers.js.map +1 -1
  193. package/out/map/ResultCamera.d.ts +10 -31
  194. package/out/map/ResultCamera.d.ts.map +1 -1
  195. package/out/map/ResultCamera.js +3 -28
  196. package/out/map/ResultCamera.js.map +1 -1
  197. package/out/map/ResultOverlay.d.ts +14 -16
  198. package/out/map/ResultOverlay.d.ts.map +1 -1
  199. package/out/map/ResultOverlay.js +1 -2
  200. package/out/map/ResultOverlay.js.map +1 -1
  201. package/out/map/ResultPanel.d.ts +1 -20
  202. package/out/map/ResultPanel.d.ts.map +1 -1
  203. package/out/map/ResultPanel.js.map +1 -1
  204. package/out/map/VersionPicker.d.ts +7 -19
  205. package/out/map/VersionPicker.d.ts.map +1 -1
  206. package/out/map/VersionPicker.js +5 -2
  207. package/out/map/VersionPicker.js.map +1 -1
  208. package/out/map/fake-runtime.d.ts +9 -11
  209. package/out/map/fake-runtime.d.ts.map +1 -1
  210. package/out/map/fake-runtime.js +45 -19
  211. package/out/map/fake-runtime.js.map +1 -1
  212. package/out/map/geometry.d.ts +13 -34
  213. package/out/map/geometry.d.ts.map +1 -1
  214. package/out/map/geometry.js +9 -34
  215. package/out/map/geometry.js.map +1 -1
  216. package/out/map/graticule.d.ts +17 -0
  217. package/out/map/graticule.d.ts.map +1 -0
  218. package/out/map/graticule.js +67 -0
  219. package/out/map/graticule.js.map +1 -0
  220. package/out/map/map-debug.d.ts +4 -2
  221. package/out/map/map-debug.d.ts.map +1 -1
  222. package/out/map/map-debug.js +1 -1
  223. package/out/map/map-debug.js.map +1 -1
  224. package/out/map/place-render.d.ts +25 -45
  225. package/out/map/place-render.d.ts.map +1 -1
  226. package/out/map/place-render.js +4 -39
  227. package/out/map/place-render.js.map +1 -1
  228. package/out/map/types.d.ts +57 -113
  229. package/out/map/types.d.ts.map +1 -1
  230. package/out/map/types.js +1 -10
  231. package/out/map/types.js.map +1 -1
  232. package/out/map/useCompareState.d.ts +5 -4
  233. package/out/map/useCompareState.d.ts.map +1 -1
  234. package/out/map/useCompareState.js +3 -3
  235. package/out/map/useCompareState.js.map +1 -1
  236. package/out/map/useGeocode.d.ts +5 -3
  237. package/out/map/useGeocode.d.ts.map +1 -1
  238. package/out/map/useGeocode.js +4 -4
  239. package/out/map/useGeocode.js.map +1 -1
  240. package/out/map/useMapBearing.d.ts +11 -11
  241. package/out/map/useMapBearing.d.ts.map +1 -1
  242. package/out/map/useMapBearing.js +6 -11
  243. package/out/map/useMapBearing.js.map +1 -1
  244. package/out/map/useMapLabelPick.d.ts +4 -14
  245. package/out/map/useMapLabelPick.d.ts.map +1 -1
  246. package/out/map/useMapLabelPick.js +21 -30
  247. package/out/map/useMapLabelPick.js.map +1 -1
  248. package/out/map/useMapPlaceRender.d.ts +3 -7
  249. package/out/map/useMapPlaceRender.d.ts.map +1 -1
  250. package/out/map/useMapPlaceRender.js +2 -6
  251. package/out/map/useMapPlaceRender.js.map +1 -1
  252. package/out/map/usePlaceAutocomplete.d.ts +30 -28
  253. package/out/map/usePlaceAutocomplete.d.ts.map +1 -1
  254. package/out/map/usePlaceAutocomplete.js +6 -33
  255. package/out/map/usePlaceAutocomplete.js.map +1 -1
  256. package/out/map.d.ts +67 -0
  257. package/out/map.d.ts.map +1 -0
  258. package/out/map.js +45 -0
  259. package/out/map.js.map +1 -0
  260. package/out/pipeline/CandidatePicker.d.ts +12 -2
  261. package/out/pipeline/CandidatePicker.d.ts.map +1 -1
  262. package/out/pipeline/CandidatePicker.js +1 -1
  263. package/out/pipeline/CandidatePicker.js.map +1 -1
  264. package/out/pipeline/ConfidenceCell.d.ts +2 -2
  265. package/out/pipeline/ConfidenceCell.d.ts.map +1 -1
  266. package/out/pipeline/ConfidenceCell.js +1 -1
  267. package/out/pipeline/FailureDiagnostic.d.ts +8 -2
  268. package/out/pipeline/FailureDiagnostic.d.ts.map +1 -1
  269. package/out/pipeline/FailureDiagnostic.js +3 -0
  270. package/out/pipeline/FailureDiagnostic.js.map +1 -1
  271. package/out/pipeline/PipelineExplorer.d.ts +6 -2
  272. package/out/pipeline/PipelineExplorer.d.ts.map +1 -1
  273. package/out/pipeline/PipelineExplorer.js +1 -1
  274. package/out/pipeline/PipelineExplorer.js.map +1 -1
  275. package/out/pipeline/QueryForm.d.ts +5 -2
  276. package/out/pipeline/QueryForm.d.ts.map +1 -1
  277. package/out/pipeline/QueryForm.js.map +1 -1
  278. package/out/pipeline/ResolvedPlace.d.ts +1 -1
  279. package/out/pipeline/ResolvedPlace.d.ts.map +1 -1
  280. package/out/pipeline/SpanHighlight.d.ts +4 -3
  281. package/out/pipeline/SpanHighlight.d.ts.map +1 -1
  282. package/out/pipeline/SpanHighlight.js +3 -2
  283. package/out/pipeline/SpanHighlight.js.map +1 -1
  284. package/out/pipeline/TimingPanel.d.ts +1 -1
  285. package/out/pipeline/TreeView.d.ts +1 -1
  286. package/out/pipeline/TreeView.js.map +1 -1
  287. package/out/pipeline/copy.d.ts +1 -1
  288. package/out/pipeline/copy.d.ts.map +1 -1
  289. package/out/pipeline/copy.js +4 -3
  290. package/out/pipeline/copy.js.map +1 -1
  291. package/out/pipeline/presets.d.ts +5 -4
  292. package/out/pipeline/presets.d.ts.map +1 -1
  293. package/out/pipeline/presets.js +15 -13
  294. package/out/pipeline/presets.js.map +1 -1
  295. package/out/pipeline/types.d.ts +20 -17
  296. package/out/pipeline/types.d.ts.map +1 -1
  297. package/out/pipeline/types.js +2 -4
  298. package/out/pipeline/types.js.map +1 -1
  299. package/out/pipeline/useParsePipeline.d.ts +14 -9
  300. package/out/pipeline/useParsePipeline.d.ts.map +1 -1
  301. package/out/pipeline/useParsePipeline.js +7 -8
  302. package/out/pipeline/useParsePipeline.js.map +1 -1
  303. package/out/pipeline.d.ts +33 -0
  304. package/out/pipeline.d.ts.map +1 -0
  305. package/out/pipeline.js +21 -0
  306. package/out/pipeline.js.map +1 -0
  307. package/out/poi/AbstainPanel.d.ts +1 -1
  308. package/out/poi/LiveResultsBlock.d.ts +2 -1
  309. package/out/poi/LiveResultsBlock.d.ts.map +1 -1
  310. package/out/poi/LiveResultsBlock.js.map +1 -1
  311. package/out/poi/POIExplorer.d.ts +22 -12
  312. package/out/poi/POIExplorer.d.ts.map +1 -1
  313. package/out/poi/POIExplorer.js +11 -10
  314. package/out/poi/POIExplorer.js.map +1 -1
  315. package/out/poi/QueryInput.d.ts +1 -1
  316. package/out/poi/SubjectPanel.d.ts +2 -2
  317. package/out/poi/SubjectPanel.d.ts.map +1 -1
  318. package/out/poi/SubjectPanel.js +2 -1
  319. package/out/poi/SubjectPanel.js.map +1 -1
  320. package/out/poi/runtime.d.ts +5 -14
  321. package/out/poi/runtime.d.ts.map +1 -1
  322. package/out/poi/runtime.js +14 -16
  323. package/out/poi/runtime.js.map +1 -1
  324. package/out/poi/types.d.ts +53 -45
  325. package/out/poi/types.d.ts.map +1 -1
  326. package/out/poi/types.js +2 -4
  327. package/out/poi/types.js.map +1 -1
  328. package/out/poi/usePOISearch.d.ts +30 -17
  329. package/out/poi/usePOISearch.d.ts.map +1 -1
  330. package/out/poi/usePOISearch.js +48 -64
  331. package/out/poi/usePOISearch.js.map +1 -1
  332. package/out/poi.d.ts +23 -0
  333. package/out/poi.d.ts.map +1 -0
  334. package/out/poi.js +15 -0
  335. package/out/poi.js.map +1 -0
  336. package/out/runtime/useReleaseRuntime.d.ts +63 -74
  337. package/out/runtime/useReleaseRuntime.d.ts.map +1 -1
  338. package/out/runtime/useReleaseRuntime.js +31 -32
  339. package/out/runtime/useReleaseRuntime.js.map +1 -1
  340. package/out/{runtime/index.d.ts → runtime.d.ts} +1 -1
  341. package/out/runtime.d.ts.map +1 -0
  342. package/out/{runtime/index.js → runtime.js} +1 -1
  343. package/out/runtime.js.map +1 -0
  344. package/out/search/SearchModal.d.ts +40 -0
  345. package/out/search/SearchModal.d.ts.map +1 -0
  346. package/out/search/SearchModal.js +255 -0
  347. package/out/search/SearchModal.js.map +1 -0
  348. package/out/search/types.d.ts +65 -0
  349. package/out/search/types.d.ts.map +1 -0
  350. package/out/search/types.js +13 -0
  351. package/out/search/types.js.map +1 -0
  352. package/out/search.d.ts +11 -0
  353. package/out/search.d.ts.map +1 -0
  354. package/out/search.js +9 -0
  355. package/out/search.js.map +1 -0
  356. package/package.json +73 -165
  357. package/styles.css +482 -266
  358. package/tokens/index.css +24 -6
  359. package/tokens/tokens.json +114 -6
  360. package/lib/common/index.ts +0 -22
  361. package/lib/map/BackendControl.tsx +0 -51
  362. package/lib/map/index.ts +0 -95
  363. package/lib/pipeline/index.ts +0 -36
  364. package/lib/poi/index.ts +0 -39
  365. package/out/common/index.d.ts +0 -22
  366. package/out/common/index.d.ts.map +0 -1
  367. package/out/common/index.js +0 -15
  368. package/out/common/index.js.map +0 -1
  369. package/out/map/BackendControl.d.ts +0 -32
  370. package/out/map/BackendControl.d.ts.map +0 -1
  371. package/out/map/BackendControl.js +0 -8
  372. package/out/map/BackendControl.js.map +0 -1
  373. package/out/map/index.d.ts +0 -69
  374. package/out/map/index.d.ts.map +0 -1
  375. package/out/map/index.js +0 -46
  376. package/out/map/index.js.map +0 -1
  377. package/out/pipeline/index.d.ts +0 -33
  378. package/out/pipeline/index.d.ts.map +0 -1
  379. package/out/pipeline/index.js +0 -21
  380. package/out/pipeline/index.js.map +0 -1
  381. package/out/poi/index.d.ts +0 -23
  382. package/out/poi/index.d.ts.map +0 -1
  383. package/out/poi/index.js +0 -15
  384. package/out/poi/index.js.map +0 -1
  385. package/out/runtime/index.d.ts.map +0 -1
  386. package/out/runtime/index.js.map +0 -1
  387. package/styleframe.config.ts +0 -329
  388. package/vitest.node.config.ts +0 -20
  389. /package/lib/{runtime/index.ts → runtime.ts} +0 -0
@@ -2,18 +2,10 @@
2
2
  * @copyright Sister Software
3
3
  * @license AGPL-3.0
4
4
  * @author Teffen Ellis, et al.
5
- *
6
- * The geocoder's chrome over the map: a search pill on top, example chips on one line beneath it, a control column
7
- * down the right edge, and a result sheet that rises from the bottom when there is something to show.
8
- *
9
- * It replaces a 400px full-height panel that carried every control at once. The layout is the one the reference
10
- * map apps use, and the reason is the map: chrome that floats over it leaves the map the page, where a slab makes
11
- * the map the leftover third.
12
- *
13
- * NODE-SAFE: pure React + the shared units, no maplibre.
14
5
  */
15
6
 
16
- import { type ReactNode, useCallback, useState } from "react"
7
+ import type React from "react"
8
+ import { type ReactNode, useCallback, useEffect, useRef, useState } from "react"
17
9
  import type { MapInstance } from "react-map-gl/maplibre"
18
10
 
19
11
  import type { GeocoderPanels, GeocoderRuntime } from "#map/types"
@@ -25,81 +17,74 @@ import type { UsePlaceAutocomplete } from "#map/usePlaceAutocomplete"
25
17
 
26
18
  import { LoadingIndicator } from "../common/LoadingIndicator.tsx"
27
19
  import type { Preset } from "../common/PresetChips.tsx"
28
- import { BackendControl } from "./BackendControl.tsx"
29
20
  import { CompareToggle } from "./CompareToggle.tsx"
21
+ import { DebugInfo } from "./DebugInfo.tsx"
30
22
  import { MapChipRow } from "./MapChipRow.tsx"
31
23
  import { MapCompass } from "./MapCompass.tsx"
32
24
  import { MapControlButton, MapControlGroup, MapControlStack } from "./MapControlStack.tsx"
33
25
  import { MapProgressBar } from "./MapProgressBar.tsx"
34
- import { MapSearchBar } from "./MapSearchBar.tsx"
35
- import { MapSheet } from "./MapSheet.tsx"
26
+ import { MapSearchBar, SearchGlyph } from "./MapSearchBar.tsx"
27
+ import { MapSheet, SheetClose } from "./MapSheet.tsx"
36
28
  import { PlaceAutocomplete } from "./PlaceAutocomplete.tsx"
37
29
  import { ResultPanel } from "./ResultPanel.tsx"
38
30
  import { VersionPicker } from "./VersionPicker.tsx"
39
31
 
32
+ /**
33
+ * Props for {@linkcode GeocoderControls}.
34
+ */
40
35
  export interface GeocoderControlsProps {
41
- /**
42
- * The injected geocoder runtime.
43
- */
44
36
  runtime: GeocoderRuntime
45
- /**
46
- * The parse+resolve state machine.
47
- */
48
37
  geocode: UseGeocode
49
- /**
50
- * The place-autocomplete combobox state.
51
- */
52
38
  autocomplete: UsePlaceAutocomplete
53
- /**
54
- * The compare-mode state.
55
- */
56
39
  compare: UseCompareState
57
40
  /**
58
- * Host-injected panels (about, release blurb, compare, permalink, extras, failure).
41
+ * A query from the URL to run once the runtime is ready.
59
42
  */
43
+ initialQuery?: string | null
60
44
  panels: GeocoderPanels
61
- /**
62
- * Example chips.
63
- */
64
45
  presets: ReadonlyArray<Preset>
65
46
  /**
66
- * Input placeholder.
47
+ * The input placeholder.
48
+ *
49
+ * The field also selects its value on focus while it still equals this text.
67
50
  */
68
51
  placeholder: string
69
- /**
70
- * The live map, for the controls that read it: the compass takes its bearing, the host's layer control reads its
71
- * style. `null` until react-map-gl instantiates the map.
72
- */
73
52
  map?: MapInstance | null
74
53
  /**
75
- * Fired with the query whenever one is submitted, before the parse starts. The host writes it into the URL; this
76
- * package never touches `location`, because the address bar is the app's state, not a control's.
54
+ * Called before each user-initiated query, so the host can write the query to its URL.
77
55
  */
78
56
  onSubmitQuery?: (query: string) => void
79
- /**
80
- * Select a model version (the composed geocoder also clears a now-colliding compare selection).
81
- */
82
57
  onSelectVersion: (version: string) => void
83
58
  /**
84
- * Toggle the forced WASM backend.
85
- */
86
- onForceWASMChange: (forceWASM: boolean) => void
87
- /**
88
- * Open the developer panel on mount. The model version, the backend readout and compare live inside it: they read on
89
- * the model rather than on an address, and above the query field they were the first thing every visitor met.
59
+ * Whether the developer panel starts open.
90
60
  *
91
- * @default false
61
+ * @defaultValue `false`
92
62
  */
93
63
  developer?: boolean
94
64
  }
95
65
 
66
+ type SheetName = "about" | "layers" | "developer" | null
67
+
96
68
  /**
97
- * Which side sheet is open. At most one, because they share an edge and a phone gives each the whole panel.
69
+ * The media query for the bottom-drawer layout.
70
+ * It must match the breakpoint in `styles.css`.
98
71
  */
99
- type SheetName = "about" | "layers" | "developer" | null
72
+ const DRAWER_LAYOUT = "(max-width: 600px)"
100
73
 
101
74
  /**
102
- * The chrome. Everything positioned here floats over the map; nothing occupies a column of the page.
75
+ * The pointer travel, in px, that turns a press on the header into a drag instead of a tap.
76
+ */
77
+ const DRAG_TRAVEL_PX = 3
78
+
79
+ /**
80
+ * The downward travel, in px, that turns a pull at the top of the panel's scroll into a drawer drag.
81
+ */
82
+ const OVERSCROLL_PROMOTE_PX = 8
83
+
84
+ /**
85
+ * Renders the search and result panels, map control rail and side sheets over the map.
86
+ *
87
+ * On narrow screens, users can drag the search panel between bottom-drawer detents.
103
88
  */
104
89
  export function GeocoderControls({
105
90
  runtime,
@@ -110,9 +95,9 @@ export function GeocoderControls({
110
95
  presets,
111
96
  placeholder,
112
97
  map = null,
98
+ initialQuery = null,
113
99
  onSubmitQuery,
114
100
  onSelectVersion,
115
- onForceWASMChange,
116
101
  developer = false,
117
102
  }: GeocoderControlsProps): ReactNode {
118
103
  const versions = runtime.availableVersions ?? []
@@ -120,24 +105,270 @@ export function GeocoderControls({
120
105
  const loading = runtime.loading
121
106
  const errorMessage = geocode.parseError ?? runtime.errorMessage ?? null
122
107
 
123
- // One sheet at a time. Two open at once stack on the same edge, and on a phone each is the full panel.
108
+ // Only one side sheet is open at a time, because two would stack on the same edge.
124
109
  const [openSheet, setOpenSheet] = useState<SheetName>(developer ? "developer" : null)
110
+
111
+ // This flag hides the result panel until the next query resets it.
112
+ const [resultDismissed, setResultDismissed] = useState(false)
113
+
114
+ // The drawer height is in px.
115
+ // A `null` height leaves the size to the stylesheet.
116
+ const sheetRef = useRef<HTMLElement>(null)
117
+ const [sheetHeight, setSheetHeight] = useState<number | null>(null)
118
+ const sheetDragRef = useRef<{ startY: number; startHeight: number; moved: boolean } | null>(null)
119
+
120
+ // The detents are computed on each call, so a rotated or resized viewport needs no resize listener.
121
+ const sheetDetents = useCallback(() => {
122
+ const viewport = window.innerHeight
123
+
124
+ return {
125
+ medium: viewport * 0.52,
126
+ large: viewport * 0.88,
127
+ floor: viewport * 0.15,
128
+ // A drag released below this height dismisses the result.
129
+ dismissBelow: viewport * 0.28,
130
+ // At or below this height, the drawer counts as collapsed.
131
+ // The control rail then returns.
132
+ collapsedBelow: viewport * 0.2,
133
+ }
134
+ }, [])
135
+
136
+ // This ref holds a pull that started at the top of the panel's scroll.
137
+ // It becomes a drag after `OVERSCROLL_PROMOTE_PX` of downward travel.
138
+ const overscrollRef = useRef<{ startY: number; pointerId: number } | null>(null)
139
+
140
+ // Drag gestures apply only in the drawer layout.
141
+ // The desktop column is sized by its content.
142
+ const isDrawerLayout = () => globalThis.window !== undefined && globalThis.matchMedia(DRAWER_LAYOUT).matches
143
+
144
+ const beginSheetDrag = useCallback((clientY: number, pointerId: number) => {
145
+ const sheet = sheetRef.current
146
+
147
+ if (!sheet) return
148
+
149
+ sheet.setPointerCapture(pointerId)
150
+ sheetDragRef.current = { startY: clientY, startHeight: sheet.getBoundingClientRect().height, moved: false }
151
+ }, [])
152
+
153
+ // The whole header is the grab target, except the search input and the close button.
154
+ const onHeaderPointerDown = useCallback(
155
+ (event: React.PointerEvent<HTMLDivElement>) => {
156
+ if (!isDrawerLayout()) return
157
+
158
+ if (event.target instanceof Element && event.target.closest("input, .mw-map-sheet__close")) return
159
+
160
+ beginSheetDrag(event.clientY, event.pointerId)
161
+ },
162
+ [beginSheetDrag]
163
+ )
164
+
165
+ const onGripPointerMove = useCallback(
166
+ (event: React.PointerEvent<HTMLElement>) => {
167
+ const drag = sheetDragRef.current
168
+
169
+ if (!drag) return
170
+
171
+ // An upward drag makes the drawer taller, so the delta is inverted against the pointer's y.
172
+ const delta = drag.startY - event.clientY
173
+
174
+ if (Math.abs(delta) > DRAG_TRAVEL_PX) {
175
+ drag.moved = true
176
+ }
177
+
178
+ const { floor, large } = sheetDetents()
179
+
180
+ setSheetHeight(Math.min(large, Math.max(floor, drag.startHeight + delta)))
181
+ },
182
+ [sheetDetents]
183
+ )
184
+
185
+ // A tap, keyboard activation of the handle and the handle's `aria-expanded` state
186
+ // all use this midpoint to select the drawer detent.
187
+ const detentMidpoint = useCallback(() => {
188
+ const { medium, large } = sheetDetents()
189
+
190
+ return (medium + large) / 2
191
+ }, [sheetDetents])
192
+
193
+ const atLargeDetent = () => sheetHeight !== null && sheetHeight > detentMidpoint()
194
+
195
+ const toggleDetent = useCallback(() => {
196
+ const { medium, large } = sheetDetents()
197
+ const current = sheetRef.current?.getBoundingClientRect().height ?? medium
198
+
199
+ setSheetHeight(current > detentMidpoint() ? medium : large)
200
+ }, [sheetDetents, detentMidpoint])
201
+
202
+ const onGripPointerUp = useCallback(() => {
203
+ const drag = sheetDragRef.current
204
+
205
+ if (!drag) return
206
+
207
+ sheetDragRef.current = null
208
+
209
+ const { medium, large, dismissBelow } = sheetDetents()
210
+ const midpoint = (medium + large) / 2
211
+ const current = sheetRef.current?.getBoundingClientRect().height ?? medium
212
+
213
+ // A press without travel counts as a tap.
214
+ // It toggles between the two detents.
215
+ if (!drag.moved) {
216
+ toggleDetent()
217
+
218
+ return
219
+ }
220
+
221
+ if (current < dismissBelow) {
222
+ setResultDismissed(true)
223
+ setSheetHeight(null)
224
+
225
+ return
226
+ }
227
+
228
+ setSheetHeight(current > midpoint ? large : medium)
229
+ }, [sheetDetents, toggleDetent])
230
+
231
+ // A downward pull on a panel scrolled to its top drags the drawer down.
232
+ // This handler only arms the pull; `onPanelPointerMove` promotes it, so taps
233
+ // and upward scrolls are unaffected.
234
+ const onPanelPointerDown = useCallback((event: React.PointerEvent<HTMLElement>) => {
235
+ if (!isDrawerLayout()) return
236
+
237
+ if (sheetDragRef.current) return
238
+
239
+ const sheet = sheetRef.current
240
+
241
+ if (!sheet || sheet.scrollTop > 0) return
242
+
243
+ if (event.target instanceof Element && event.target.closest(".mw-map-panel__header")) return
244
+
245
+ overscrollRef.current = { startY: event.clientY, pointerId: event.pointerId }
246
+ }, [])
247
+
248
+ const onPanelPointerMove = useCallback(
249
+ (event: React.PointerEvent<HTMLElement>) => {
250
+ if (sheetDragRef.current) {
251
+ onGripPointerMove(event)
252
+
253
+ return
254
+ }
255
+
256
+ const armed = overscrollRef.current
257
+
258
+ if (!armed || armed.pointerId !== event.pointerId) return
259
+
260
+ const sheet = sheetRef.current
261
+
262
+ if (!sheet) return
263
+
264
+ if (sheet.scrollTop > 0) {
265
+ overscrollRef.current = null
266
+
267
+ return
268
+ }
269
+
270
+ if (event.clientY - armed.startY < OVERSCROLL_PROMOTE_PX) return
271
+
272
+ overscrollRef.current = null
273
+ // The drag starts from the pull's origin, so the drawer does not jump by the threshold.
274
+ sheetDragRef.current = { startY: armed.startY, startHeight: sheet.getBoundingClientRect().height, moved: true }
275
+ sheet.setPointerCapture(event.pointerId)
276
+ onGripPointerMove(event)
277
+ },
278
+ [onGripPointerMove]
279
+ )
280
+
281
+ const onPanelPointerUp = useCallback(() => {
282
+ overscrollRef.current = null
283
+ onGripPointerUp()
284
+ }, [onGripPointerUp])
285
+
125
286
  const { bearing, resetNorth } = useMapBearing(map)
126
287
 
127
288
  const toggleSheet = (name: Exclude<SheetName, null>) => setOpenSheet((current) => (current === name ? null : name))
128
289
  const closeSheet = useCallback(() => setOpenSheet(null), [])
129
290
 
130
- // Every path that starts a query goes through here, so the URL is written in exactly one place — the event that
131
- // caused it, rather than an effect watching the result after the fact.
291
+ // Every user-initiated query goes through this function, so `onSubmitQuery` fires from one place.
132
292
  const runQuery = useCallback(
133
293
  (query: string) => {
294
+ // A new query shows the result panel again at its default height.
295
+ setResultDismissed(false)
296
+ setSheetHeight(null)
134
297
  onSubmitQuery?.(query)
135
298
  void geocode.submit(query)
136
299
  },
137
300
  [onSubmitQuery, geocode]
138
301
  )
139
302
 
140
- // A label on the map is a search a visitor already typed by pointing at it.
303
+ // The initial query waits for `runtime.ready` because the parse pipeline drops
304
+ // a submit made before the model loads.
305
+ // The ref makes it run once, so a later render cannot overwrite the user's own query.
306
+ const autoRanInitialQuery = useRef(false)
307
+
308
+ useEffect(() => {
309
+ if (autoRanInitialQuery.current) return
310
+
311
+ if (!initialQuery || !runtime.ready) return
312
+
313
+ autoRanInitialQuery.current = true
314
+ // This calls `geocode.submit` directly because the query is already in the URL.
315
+ void geocode.submit(initialQuery)
316
+ }, [initialQuery, runtime.ready, geocode])
317
+
318
+ // A drawer-breakpoint crossing clears any dragged height.
319
+ // The height could clip the desktop column.
320
+ useEffect(() => {
321
+ const query = globalThis.matchMedia(DRAWER_LAYOUT)
322
+ const onChange = () => setSheetHeight(null)
323
+
324
+ query.addEventListener("change", onChange)
325
+
326
+ return () => query.removeEventListener("change", onChange)
327
+ }, [])
328
+
329
+ // A user pan, zoom, or rotate shrinks the drawer to its floor detent without dismissing the result.
330
+ // Programmatic camera moves, such as the fly-to after a query, have no `originalEvent` and are ignored.
331
+ useEffect(() => {
332
+ if (!map) return
333
+
334
+ const collapse = (event: { originalEvent?: unknown }) => {
335
+ if (!event.originalEvent) return
336
+
337
+ if (!isDrawerLayout()) return
338
+
339
+ const { floor } = sheetDetents()
340
+
341
+ setSheetHeight((current) => (current !== null && current <= floor ? current : floor))
342
+ }
343
+
344
+ map.on("dragstart", collapse)
345
+ map.on("zoomstart", collapse)
346
+ map.on("rotatestart", collapse)
347
+
348
+ return () => {
349
+ map.off("dragstart", collapse)
350
+ map.off("zoomstart", collapse)
351
+ map.off("rotatestart", collapse)
352
+ }
353
+ }, [map, sheetDetents])
354
+
355
+ // Escape dismisses the result only while no side sheet is open.
356
+ // `MapSheet` also listens for Escape on the document, so one keystroke would otherwise close both.
357
+ useEffect(() => {
358
+ if (openSheet) return
359
+
360
+ const onKeyDown = (event: KeyboardEvent) => {
361
+ if (event.key === "Escape") {
362
+ setResultDismissed(true)
363
+ }
364
+ }
365
+
366
+ document.addEventListener("keydown", onKeyDown)
367
+
368
+ return () => document.removeEventListener("keydown", onKeyDown)
369
+ }, [openSheet])
370
+
371
+ // A map-label click searches for its name.
141
372
  const pickLabel = useCallback(
142
373
  (name: string) => {
143
374
  geocode.setText(name)
@@ -150,48 +381,105 @@ export function GeocoderControls({
150
381
  useMapLabelPick(map, pickLabel)
151
382
 
152
383
  const chips = presets.map((preset) => ({ label: preset.label, value: preset.value }))
153
- // The bundle load no longer opens the result sheet: it reports on the bar at the top of the viewport and in the
154
- // footer, so an empty sheet does not sit over the map for the length of a 38 MB download.
155
- const showSheet = Boolean(busy || result || errorMessage)
384
+ // The model download does not open the result panel.
385
+ // The progress bar reports it instead.
386
+ const showSheet = Boolean(busy || result || errorMessage) && !resultDismissed
387
+
388
+ // The control rail hides while the drawer is raised above its collapsed height.
389
+ const drawerRaised =
390
+ showSheet &&
391
+ (sheetHeight === null || (globalThis.window !== undefined && sheetHeight > sheetDetents().collapsedBelow))
156
392
 
157
393
  const bundleLoading = Boolean(loading && !runtime.ready)
158
394
  const steps = loading?.stepLabels.length ?? 0
159
395
 
396
+ // The model downloads during the first step.
397
+ // While bytes arrive, the bar follows the byte fraction scaled into the first step's share,
398
+ // so it does not move backwards when step progress takes over.
399
+ const stepFraction = steps ? ((loading?.stepIndex ?? 0) + 1) / steps : null
400
+ const byteFraction = loading?.byteFraction
401
+
402
+ const fraction =
403
+ typeof byteFraction === "number" && steps ? (byteFraction * 1) / steps : (byteFraction ?? stepFraction)
404
+
160
405
  return (
161
406
  <>
162
- <MapProgressBar
163
- active={bundleLoading}
164
- fraction={steps ? ((loading?.stepIndex ?? 0) + 1) / steps : null}
165
- label="Loading the geocoder"
166
- />
167
-
168
- <div className="mw-map-chrome mw-map-chrome--top">
169
- <form
170
- className="mw-map-chrome__search"
171
- onSubmit={(event) => {
172
- event.preventDefault()
173
- runQuery(geocode.text)
174
- }}
175
- >
176
- {/*
177
- * `type="search"` brings its own clear button, so the pill carries no trailing slot: a second cross
178
- * beside the native one is two controls for one job, and the spinner that used to live there changed
179
- * the field's height on every submit.
180
- */}
181
- <MapSearchBar label="Search addresses" leading={<span aria-hidden="true">⌕</span>} busy={busy}>
182
- <input
183
- id="mw-pipeline-input"
184
- type="search"
185
- aria-label="Address"
186
- value={geocode.text}
187
- onChange={(event) => geocode.setText(event.target.value)}
188
- disabled={!runtime.ready}
189
- placeholder={placeholder}
190
- onKeyDown={autocomplete.onInputKeyDown}
191
- {...autocomplete.inputProps}
192
- />
193
- </MapSearchBar>
194
- </form>
407
+ <MapProgressBar active={bundleLoading} fraction={fraction} label="Loading the geocoder" />
408
+
409
+ {/* One panel holds the search, examples and result. On desktop it is a left column sized to its content.
410
+ On narrow screens it is a bottom drawer. */}
411
+ <section
412
+ className="mw-map-panel"
413
+ aria-label="Search and results"
414
+ ref={sheetRef}
415
+ onPointerDown={onPanelPointerDown}
416
+ onPointerMove={onPanelPointerMove}
417
+ onPointerUp={onPanelPointerUp}
418
+ onPointerCancel={onPanelPointerUp}
419
+ {...(sheetHeight === null ? {} : { style: { maxHeight: `${Math.round(sheetHeight)}px` } })}
420
+ >
421
+ {/*
422
+ * The header holds the grab bar and search field.
423
+ * It stays pinned while the result scrolls.
424
+ */}
425
+ <div className="mw-map-panel__header" onPointerDown={onHeaderPointerDown}>
426
+ <div className="mw-map-panel__grip">
427
+ {/* The handle appears only with a result, because there is no content to expand without one. */}
428
+ {showSheet ? (
429
+ <button
430
+ type="button"
431
+ className="mw-map-sheet__handle"
432
+ aria-label="Resize the panel"
433
+ aria-expanded={atLargeDetent()}
434
+ // The header handles pointer gestures.
435
+ // A click with `detail === 0` comes from the keyboard,
436
+ // so only that toggles the detent here.
437
+ onClick={(event) => {
438
+ if (event.detail !== 0) return
439
+
440
+ toggleDetent()
441
+ }}
442
+ />
443
+ ) : null}
444
+
445
+ {showSheet ? (
446
+ <SheetClose
447
+ label="Close the result"
448
+ className="mw-map-sheet__close--floating"
449
+ onClose={() => setResultDismissed(true)}
450
+ />
451
+ ) : null}
452
+ </div>
453
+
454
+ <form
455
+ className="mw-map-panel__search"
456
+ onSubmit={(event) => {
457
+ event.preventDefault()
458
+ runQuery(geocode.text)
459
+ }}
460
+ >
461
+ {/* The search bar has no trailing control because `type="search"` provides a native clear button. */}
462
+ <MapSearchBar label="Search addresses" leading={<SearchGlyph />} busy={busy}>
463
+ <input
464
+ id="mw-pipeline-input"
465
+ type="search"
466
+ aria-label="Address"
467
+ value={geocode.text}
468
+ onChange={(event) => geocode.setText(event.target.value)}
469
+ // Focus selects the value only while it equals the placeholder, so typing replaces it.
470
+ onFocus={(event) => {
471
+ if (placeholder && event.currentTarget.value === placeholder) {
472
+ event.currentTarget.select()
473
+ }
474
+ }}
475
+ disabled={!runtime.ready}
476
+ placeholder={placeholder}
477
+ onKeyDown={autocomplete.onInputKeyDown}
478
+ {...autocomplete.inputProps}
479
+ />
480
+ </MapSearchBar>
481
+ </form>
482
+ </div>
195
483
 
196
484
  <PlaceAutocomplete
197
485
  suggestions={autocomplete.suggestions}
@@ -215,14 +503,49 @@ export function GeocoderControls({
215
503
  />
216
504
 
217
505
  {panels.bias}
218
- </div>
219
-
220
- {/*
221
- * Every floating control lives in this one column, so nothing can land on top of anything else: the layer
222
- * control joins the capsule rather than sitting in the left column, and the compass takes its own capsule
223
- * below because it comes and goes and would otherwise resize the one above it.
224
- */}
225
- <MapControlStack label="Map controls">
506
+
507
+ {showSheet ? (
508
+ <div className="mw-map-panel__result">
509
+ {errorMessage ? <p className="mw-error">{errorMessage}</p> : null}
510
+
511
+ {panels.aboveResult ? panels.aboveResult({ result }) : null}
512
+
513
+ {busy ? (
514
+ <div className="mw-result">
515
+ <LoadingIndicator mode="staged" steps={runtime.parseStageLabels} activeStep={geocode.parseStage} />
516
+ </div>
517
+ ) : result ? (
518
+ panels.result ? (
519
+ panels.result({
520
+ result,
521
+ selectedCandidate,
522
+ selectedCandidateIndex: geocode.selectedCandidateIndex,
523
+ onSelectCandidate: geocode.selectCandidate,
524
+ })
525
+ ) : (
526
+ <ResultPanel
527
+ result={result}
528
+ selectedCandidate={selectedCandidate}
529
+ selectedCandidateIndex={geocode.selectedCandidateIndex}
530
+ onSelectCandidate={geocode.selectCandidate}
531
+ extras={panels.extras}
532
+ failure={panels.failure}
533
+ />
534
+ )
535
+ ) : null}
536
+
537
+ {panels.compare
538
+ ? panels.compare({ result, compareMode: compare.compareMode, compareVersion: compare.compareVersion })
539
+ : null}
540
+
541
+ {panels.permalink ? <div className="mw-map-sheet__actions">{panels.permalink(geocode.text)}</div> : null}
542
+ </div>
543
+ ) : null}
544
+ </section>
545
+
546
+ {/* Every floating control uses this rail to prevent overlap. The compass has its own group because it appears
547
+ and disappears. The rail hides while the drawer is raised. */}
548
+ <MapControlStack label="Map controls" className={drawerRaised ? "mw-map-control-stack--drawer-open" : undefined}>
226
549
  <MapControlGroup>
227
550
  <MapControlButton
228
551
  label="About this geocoder"
@@ -283,10 +606,11 @@ export function GeocoderControls({
283
606
  </div>
284
607
 
285
608
  <div className="mw-map-sheet__row">
286
- <BackendControl
609
+ <span className="mw-map-sheet__label">Debug info</span>
610
+ <DebugInfo
287
611
  activeBackend={runtime.activeBackend}
288
- forceWASM={runtime.forceWASM ?? false}
289
- onForceWASMChange={onForceWASMChange}
612
+ selectedVersion={runtime.selectedVersion}
613
+ ready={runtime.ready}
290
614
  />
291
615
  </div>
292
616
 
@@ -306,46 +630,6 @@ export function GeocoderControls({
306
630
  </MapSheet>
307
631
  ) : null}
308
632
 
309
- {showSheet ? (
310
- <section className="mw-map-sheet mw-map-sheet--bottom" aria-label="Result">
311
- <div className="mw-map-sheet__handle" aria-hidden="true" />
312
-
313
- {errorMessage ? <p className="mw-error">{errorMessage}</p> : null}
314
-
315
- {panels.aboveResult ? panels.aboveResult({ result }) : null}
316
-
317
- {busy ? (
318
- <div className="mw-result">
319
- <LoadingIndicator mode="staged" steps={runtime.parseStageLabels} activeStep={geocode.parseStage} />
320
- </div>
321
- ) : result ? (
322
- panels.result ? (
323
- panels.result({
324
- result,
325
- selectedCandidate,
326
- selectedCandidateIndex: geocode.selectedCandidateIndex,
327
- onSelectCandidate: geocode.selectCandidate,
328
- })
329
- ) : (
330
- <ResultPanel
331
- result={result}
332
- selectedCandidate={selectedCandidate}
333
- selectedCandidateIndex={geocode.selectedCandidateIndex}
334
- onSelectCandidate={geocode.selectCandidate}
335
- extras={panels.extras}
336
- failure={panels.failure}
337
- />
338
- )
339
- ) : null}
340
-
341
- {panels.compare
342
- ? panels.compare({ result, compareMode: compare.compareMode, compareVersion: compare.compareVersion })
343
- : null}
344
-
345
- {panels.permalink ? <div className="mw-map-sheet__actions">{panels.permalink(geocode.text)}</div> : null}
346
- </section>
347
- ) : null}
348
-
349
633
  {panels.footer}
350
634
  </>
351
635
  )