@mailwoman/react 9.4.0 → 10.1.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 (370) hide show
  1. package/README.md +17 -16
  2. package/fonts.css +97 -22
  3. package/lib/common/About.tsx +87 -71
  4. package/lib/common/ClientOnly.tsx +2 -14
  5. package/lib/common/CopyButton.tsx +3 -1
  6. package/lib/common/LoadingIndicator.tsx +4 -2
  7. package/lib/common/PresetChips.tsx +9 -8
  8. package/lib/common/confidence-tiers.ts +7 -6
  9. package/lib/common/cx.ts +3 -1
  10. package/lib/common/text-tokens.ts +7 -4
  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 -112
  15. package/lib/map/AppIdentity.tsx +85 -0
  16. package/lib/map/BackendControl.tsx +16 -13
  17. package/lib/map/CompareToggle.tsx +11 -33
  18. package/lib/map/DebugInfo.tsx +88 -0
  19. package/lib/map/Geocoder.tsx +92 -58
  20. package/lib/map/GeocoderControls.tsx +582 -127
  21. package/lib/map/GraticuleLayer.tsx +54 -0
  22. package/lib/map/MapCanvas.tsx +23 -12
  23. package/lib/map/MapChipRow.tsx +73 -0
  24. package/lib/map/MapCompass.tsx +68 -0
  25. package/lib/map/MapControlStack.tsx +97 -0
  26. package/lib/map/MapFooter.tsx +101 -0
  27. package/lib/map/MapProgressBar.tsx +55 -0
  28. package/lib/map/MapSearchBar.tsx +82 -0
  29. package/lib/map/MapSheet.tsx +71 -0
  30. package/lib/map/OverlayLayers.tsx +1 -1
  31. package/lib/map/PlaceAutocomplete.tsx +10 -21
  32. package/lib/map/PlaceMarker.tsx +4 -2
  33. package/lib/map/ResolvedPlaceLayers.tsx +5 -14
  34. package/lib/map/ResultCamera.tsx +8 -31
  35. package/lib/map/ResultOverlay.tsx +2 -12
  36. package/lib/map/ResultPanel.tsx +1 -20
  37. package/lib/map/VersionPicker.tsx +10 -19
  38. package/lib/map/fake-runtime.ts +10 -12
  39. package/lib/map/geometry.ts +14 -47
  40. package/lib/map/graticule.ts +80 -0
  41. package/lib/map/map-debug.ts +1 -1
  42. package/lib/map/place-render.ts +28 -60
  43. package/lib/map/types.ts +65 -97
  44. package/lib/map/useCompareState.ts +5 -4
  45. package/lib/map/useGeocode.ts +7 -5
  46. package/lib/map/useMapBearing.ts +56 -0
  47. package/lib/map/useMapLabelPick.ts +114 -0
  48. package/lib/map/useMapPlaceRender.ts +2 -6
  49. package/lib/map/usePlaceAutocomplete.ts +44 -50
  50. package/lib/map.ts +95 -0
  51. package/lib/pipeline/CandidatePicker.tsx +13 -5
  52. package/lib/pipeline/ConfidenceCell.tsx +1 -1
  53. package/lib/pipeline/FailureDiagnostic.tsx +8 -2
  54. package/lib/pipeline/QueryForm.tsx +5 -2
  55. package/lib/pipeline/ResolvedPlace.tsx +1 -1
  56. package/lib/pipeline/SpanHighlight.tsx +7 -5
  57. package/lib/pipeline/TimingPanel.tsx +1 -1
  58. package/lib/pipeline/TreeView.tsx +1 -1
  59. package/lib/pipeline/copy.ts +22 -25
  60. package/lib/pipeline/presets.ts +15 -13
  61. package/lib/pipeline/types.ts +21 -14
  62. package/lib/pipeline/useParsePipeline.ts +38 -24
  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 +24 -15
  67. package/lib/poi/QueryInput.tsx +1 -1
  68. package/lib/poi/SubjectPanel.tsx +4 -3
  69. package/lib/poi/runtime.ts +5 -16
  70. package/lib/poi/types.ts +39 -27
  71. package/lib/poi/usePOISearch.ts +39 -44
  72. package/lib/poi.ts +39 -0
  73. package/lib/runtime/useReleaseRuntime.ts +125 -74
  74. package/lib/runtime.ts +16 -0
  75. package/lib/search/SearchModal.tsx +317 -0
  76. package/lib/search/search.css +144 -0
  77. package/lib/search/types.ts +67 -0
  78. package/lib/search.ts +11 -0
  79. package/out/common/About.d.ts +13 -4
  80. package/out/common/About.d.ts.map +1 -1
  81. package/out/common/About.js +5 -2
  82. package/out/common/About.js.map +1 -1
  83. package/out/common/ClientOnly.d.ts +2 -11
  84. package/out/common/ClientOnly.d.ts.map +1 -1
  85. package/out/common/ClientOnly.js +1 -13
  86. package/out/common/ClientOnly.js.map +1 -1
  87. package/out/common/CopyButton.d.ts +3 -1
  88. package/out/common/CopyButton.d.ts.map +1 -1
  89. package/out/common/CopyButton.js.map +1 -1
  90. package/out/common/LoadingIndicator.d.ts +4 -2
  91. package/out/common/LoadingIndicator.d.ts.map +1 -1
  92. package/out/common/LoadingIndicator.js.map +1 -1
  93. package/out/common/PresetChips.d.ts +9 -8
  94. package/out/common/PresetChips.d.ts.map +1 -1
  95. package/out/common/PresetChips.js.map +1 -1
  96. package/out/common/confidence-tiers.d.ts +7 -6
  97. package/out/common/confidence-tiers.d.ts.map +1 -1
  98. package/out/common/confidence-tiers.js +7 -6
  99. package/out/common/confidence-tiers.js.map +1 -1
  100. package/out/common/cx.d.ts +3 -1
  101. package/out/common/cx.d.ts.map +1 -1
  102. package/out/common/cx.js +3 -1
  103. package/out/common/cx.js.map +1 -1
  104. package/out/common/text-tokens.d.ts +4 -3
  105. package/out/common/text-tokens.d.ts.map +1 -1
  106. package/out/common/text-tokens.js +6 -3
  107. package/out/common/text-tokens.js.map +1 -1
  108. package/out/common/useBuildInfo.d.ts +27 -0
  109. package/out/common/useBuildInfo.d.ts.map +1 -0
  110. package/out/common/useBuildInfo.js +32 -0
  111. package/out/common/useBuildInfo.js.map +1 -0
  112. package/out/common/useClipboard.d.ts +1 -3
  113. package/out/common/useClipboard.d.ts.map +1 -1
  114. package/out/common/useClipboard.js +2 -11
  115. package/out/common/useClipboard.js.map +1 -1
  116. package/out/common.d.ts +22 -0
  117. package/out/common.d.ts.map +1 -0
  118. package/out/common.js +15 -0
  119. package/out/common.js.map +1 -0
  120. package/out/index.d.ts +26 -71
  121. package/out/index.d.ts.map +1 -1
  122. package/out/index.js +21 -47
  123. package/out/index.js.map +1 -1
  124. package/out/map/AppIdentity.d.ts +42 -0
  125. package/out/map/AppIdentity.d.ts.map +1 -0
  126. package/out/map/AppIdentity.js +16 -0
  127. package/out/map/AppIdentity.js.map +1 -0
  128. package/out/map/BackendControl.d.ts +9 -10
  129. package/out/map/BackendControl.d.ts.map +1 -1
  130. package/out/map/BackendControl.js +2 -2
  131. package/out/map/BackendControl.js.map +1 -1
  132. package/out/map/CompareToggle.d.ts +8 -31
  133. package/out/map/CompareToggle.d.ts.map +1 -1
  134. package/out/map/CompareToggle.js +5 -2
  135. package/out/map/CompareToggle.js.map +1 -1
  136. package/out/map/DebugInfo.d.ts +34 -0
  137. package/out/map/DebugInfo.d.ts.map +1 -0
  138. package/out/map/DebugInfo.js +39 -0
  139. package/out/map/DebugInfo.js.map +1 -0
  140. package/out/map/Geocoder.d.ts +47 -26
  141. package/out/map/Geocoder.d.ts.map +1 -1
  142. package/out/map/Geocoder.js +34 -50
  143. package/out/map/Geocoder.js.map +1 -1
  144. package/out/map/GeocoderControls.d.ts +23 -33
  145. package/out/map/GeocoderControls.d.ts.map +1 -1
  146. package/out/map/GeocoderControls.js +277 -16
  147. package/out/map/GeocoderControls.js.map +1 -1
  148. package/out/map/GraticuleLayer.d.ts +28 -0
  149. package/out/map/GraticuleLayer.d.ts.map +1 -0
  150. package/out/map/GraticuleLayer.js +25 -0
  151. package/out/map/GraticuleLayer.js.map +1 -0
  152. package/out/map/MapCanvas.d.ts +23 -12
  153. package/out/map/MapCanvas.d.ts.map +1 -1
  154. package/out/map/MapCanvas.js +5 -2
  155. package/out/map/MapCanvas.js.map +1 -1
  156. package/out/map/MapChipRow.d.ts +43 -0
  157. package/out/map/MapChipRow.d.ts.map +1 -0
  158. package/out/map/MapChipRow.js +12 -0
  159. package/out/map/MapChipRow.js.map +1 -0
  160. package/out/map/MapCompass.d.ts +33 -0
  161. package/out/map/MapCompass.d.ts.map +1 -0
  162. package/out/map/MapCompass.js +15 -0
  163. package/out/map/MapCompass.js.map +1 -0
  164. package/out/map/MapControlStack.d.ts +56 -0
  165. package/out/map/MapControlStack.d.ts.map +1 -0
  166. package/out/map/MapControlStack.js +18 -0
  167. package/out/map/MapControlStack.js.map +1 -0
  168. package/out/map/MapFooter.d.ts +27 -0
  169. package/out/map/MapFooter.d.ts.map +1 -0
  170. package/out/map/MapFooter.js +37 -0
  171. package/out/map/MapFooter.js.map +1 -0
  172. package/out/map/MapProgressBar.d.ts +31 -0
  173. package/out/map/MapProgressBar.d.ts.map +1 -0
  174. package/out/map/MapProgressBar.js +8 -0
  175. package/out/map/MapProgressBar.js.map +1 -0
  176. package/out/map/MapSearchBar.d.ts +48 -0
  177. package/out/map/MapSearchBar.d.ts.map +1 -0
  178. package/out/map/MapSearchBar.js +12 -0
  179. package/out/map/MapSearchBar.js.map +1 -0
  180. package/out/map/MapSheet.d.ts +35 -0
  181. package/out/map/MapSheet.d.ts.map +1 -0
  182. package/out/map/MapSheet.js +29 -0
  183. package/out/map/MapSheet.js.map +1 -0
  184. package/out/map/OverlayLayers.d.ts +1 -1
  185. package/out/map/OverlayLayers.js +2 -2
  186. package/out/map/PlaceAutocomplete.d.ts +10 -21
  187. package/out/map/PlaceAutocomplete.d.ts.map +1 -1
  188. package/out/map/PlaceAutocomplete.js +2 -1
  189. package/out/map/PlaceAutocomplete.js.map +1 -1
  190. package/out/map/PlaceMarker.d.ts +4 -2
  191. package/out/map/PlaceMarker.d.ts.map +1 -1
  192. package/out/map/PlaceMarker.js +3 -1
  193. package/out/map/PlaceMarker.js.map +1 -1
  194. package/out/map/ResolvedPlaceLayers.d.ts +5 -14
  195. package/out/map/ResolvedPlaceLayers.d.ts.map +1 -1
  196. package/out/map/ResolvedPlaceLayers.js +2 -2
  197. package/out/map/ResolvedPlaceLayers.js.map +1 -1
  198. package/out/map/ResultCamera.d.ts +8 -31
  199. package/out/map/ResultCamera.d.ts.map +1 -1
  200. package/out/map/ResultCamera.js +3 -28
  201. package/out/map/ResultCamera.js.map +1 -1
  202. package/out/map/ResultOverlay.d.ts +2 -12
  203. package/out/map/ResultOverlay.d.ts.map +1 -1
  204. package/out/map/ResultOverlay.js +1 -2
  205. package/out/map/ResultOverlay.js.map +1 -1
  206. package/out/map/ResultPanel.d.ts +1 -20
  207. package/out/map/ResultPanel.d.ts.map +1 -1
  208. package/out/map/ResultPanel.js +1 -1
  209. package/out/map/ResultPanel.js.map +1 -1
  210. package/out/map/VersionPicker.d.ts +7 -19
  211. package/out/map/VersionPicker.d.ts.map +1 -1
  212. package/out/map/VersionPicker.js +5 -2
  213. package/out/map/VersionPicker.js.map +1 -1
  214. package/out/map/fake-runtime.d.ts +9 -11
  215. package/out/map/fake-runtime.d.ts.map +1 -1
  216. package/out/map/fake-runtime.js +9 -12
  217. package/out/map/fake-runtime.js.map +1 -1
  218. package/out/map/geometry.d.ts +13 -34
  219. package/out/map/geometry.d.ts.map +1 -1
  220. package/out/map/geometry.js +9 -34
  221. package/out/map/geometry.js.map +1 -1
  222. package/out/map/graticule.d.ts +17 -0
  223. package/out/map/graticule.d.ts.map +1 -0
  224. package/out/map/graticule.js +67 -0
  225. package/out/map/graticule.js.map +1 -0
  226. package/out/map/map-debug.d.ts +1 -1
  227. package/out/map/map-debug.d.ts.map +1 -1
  228. package/out/map/map-debug.js +1 -1
  229. package/out/map/map-debug.js.map +1 -1
  230. package/out/map/place-render.d.ts +23 -43
  231. package/out/map/place-render.d.ts.map +1 -1
  232. package/out/map/place-render.js +4 -39
  233. package/out/map/place-render.js.map +1 -1
  234. package/out/map/types.d.ts +67 -95
  235. package/out/map/types.d.ts.map +1 -1
  236. package/out/map/types.js +1 -10
  237. package/out/map/types.js.map +1 -1
  238. package/out/map/useCompareState.d.ts +5 -4
  239. package/out/map/useCompareState.d.ts.map +1 -1
  240. package/out/map/useCompareState.js +3 -3
  241. package/out/map/useCompareState.js.map +1 -1
  242. package/out/map/useGeocode.d.ts +5 -3
  243. package/out/map/useGeocode.d.ts.map +1 -1
  244. package/out/map/useGeocode.js +4 -4
  245. package/out/map/useGeocode.js.map +1 -1
  246. package/out/map/useMapBearing.d.ts +28 -0
  247. package/out/map/useMapBearing.d.ts.map +1 -0
  248. package/out/map/useMapBearing.js +30 -0
  249. package/out/map/useMapBearing.js.map +1 -0
  250. package/out/map/useMapLabelPick.d.ts +13 -0
  251. package/out/map/useMapLabelPick.d.ts.map +1 -0
  252. package/out/map/useMapLabelPick.js +95 -0
  253. package/out/map/useMapLabelPick.js.map +1 -0
  254. package/out/map/useMapPlaceRender.d.ts +2 -6
  255. package/out/map/useMapPlaceRender.d.ts.map +1 -1
  256. package/out/map/useMapPlaceRender.js +2 -6
  257. package/out/map/useMapPlaceRender.js.map +1 -1
  258. package/out/map/usePlaceAutocomplete.d.ts +30 -28
  259. package/out/map/usePlaceAutocomplete.d.ts.map +1 -1
  260. package/out/map/usePlaceAutocomplete.js +6 -33
  261. package/out/map/usePlaceAutocomplete.js.map +1 -1
  262. package/out/map.d.ts +69 -0
  263. package/out/map.d.ts.map +1 -0
  264. package/out/map.js +46 -0
  265. package/out/map.js.map +1 -0
  266. package/out/pipeline/CandidatePicker.d.ts +12 -2
  267. package/out/pipeline/CandidatePicker.d.ts.map +1 -1
  268. package/out/pipeline/CandidatePicker.js +2 -2
  269. package/out/pipeline/CandidatePicker.js.map +1 -1
  270. package/out/pipeline/ConfidenceCell.d.ts +1 -1
  271. package/out/pipeline/ConfidenceCell.js +1 -1
  272. package/out/pipeline/FailureDiagnostic.d.ts +8 -2
  273. package/out/pipeline/FailureDiagnostic.d.ts.map +1 -1
  274. package/out/pipeline/FailureDiagnostic.js +3 -0
  275. package/out/pipeline/FailureDiagnostic.js.map +1 -1
  276. package/out/pipeline/PipelineExplorer.js +1 -1
  277. package/out/pipeline/QueryForm.d.ts +5 -2
  278. package/out/pipeline/QueryForm.d.ts.map +1 -1
  279. package/out/pipeline/QueryForm.js +1 -1
  280. package/out/pipeline/QueryForm.js.map +1 -1
  281. package/out/pipeline/ResolvedPlace.js +1 -1
  282. package/out/pipeline/SpanHighlight.d.ts +4 -3
  283. package/out/pipeline/SpanHighlight.d.ts.map +1 -1
  284. package/out/pipeline/SpanHighlight.js +3 -2
  285. package/out/pipeline/SpanHighlight.js.map +1 -1
  286. package/out/pipeline/TimingPanel.d.ts +1 -1
  287. package/out/pipeline/TimingPanel.js +1 -1
  288. package/out/pipeline/TreeView.d.ts +1 -1
  289. package/out/pipeline/copy.d.ts +1 -1
  290. package/out/pipeline/copy.d.ts.map +1 -1
  291. package/out/pipeline/copy.js +4 -3
  292. package/out/pipeline/copy.js.map +1 -1
  293. package/out/pipeline/presets.d.ts +5 -4
  294. package/out/pipeline/presets.d.ts.map +1 -1
  295. package/out/pipeline/presets.js +15 -13
  296. package/out/pipeline/presets.js.map +1 -1
  297. package/out/pipeline/types.d.ts +21 -14
  298. package/out/pipeline/types.d.ts.map +1 -1
  299. package/out/pipeline/types.js +2 -4
  300. package/out/pipeline/types.js.map +1 -1
  301. package/out/pipeline/useParsePipeline.d.ts +15 -7
  302. package/out/pipeline/useParsePipeline.d.ts.map +1 -1
  303. package/out/pipeline/useParsePipeline.js +10 -7
  304. package/out/pipeline/useParsePipeline.js.map +1 -1
  305. package/out/pipeline.d.ts +33 -0
  306. package/out/pipeline.d.ts.map +1 -0
  307. package/out/pipeline.js +21 -0
  308. package/out/pipeline.js.map +1 -0
  309. package/out/poi/AbstainPanel.d.ts +1 -1
  310. package/out/poi/LiveResultsBlock.d.ts +2 -1
  311. package/out/poi/LiveResultsBlock.d.ts.map +1 -1
  312. package/out/poi/LiveResultsBlock.js +1 -1
  313. package/out/poi/LiveResultsBlock.js.map +1 -1
  314. package/out/poi/POIExplorer.d.ts +22 -12
  315. package/out/poi/POIExplorer.d.ts.map +1 -1
  316. package/out/poi/POIExplorer.js +10 -9
  317. package/out/poi/POIExplorer.js.map +1 -1
  318. package/out/poi/QueryInput.d.ts +1 -1
  319. package/out/poi/SubjectPanel.d.ts +2 -2
  320. package/out/poi/SubjectPanel.d.ts.map +1 -1
  321. package/out/poi/SubjectPanel.js +3 -2
  322. package/out/poi/SubjectPanel.js.map +1 -1
  323. package/out/poi/runtime.d.ts +5 -14
  324. package/out/poi/runtime.d.ts.map +1 -1
  325. package/out/poi/runtime.js +5 -16
  326. package/out/poi/runtime.js.map +1 -1
  327. package/out/poi/types.d.ts +39 -27
  328. package/out/poi/types.d.ts.map +1 -1
  329. package/out/poi/types.js +2 -4
  330. package/out/poi/types.js.map +1 -1
  331. package/out/poi/usePOISearch.d.ts +28 -17
  332. package/out/poi/usePOISearch.d.ts.map +1 -1
  333. package/out/poi/usePOISearch.js +6 -32
  334. package/out/poi/usePOISearch.js.map +1 -1
  335. package/out/poi.d.ts +23 -0
  336. package/out/poi.d.ts.map +1 -0
  337. package/out/poi.js +15 -0
  338. package/out/poi.js.map +1 -0
  339. package/out/runtime/useReleaseRuntime.d.ts +64 -65
  340. package/out/runtime/useReleaseRuntime.d.ts.map +1 -1
  341. package/out/runtime/useReleaseRuntime.js +31 -26
  342. package/out/runtime/useReleaseRuntime.js.map +1 -1
  343. package/out/runtime.d.ts +9 -0
  344. package/out/runtime.d.ts.map +1 -0
  345. package/out/runtime.js +8 -0
  346. package/out/runtime.js.map +1 -0
  347. package/out/search/SearchModal.d.ts +33 -0
  348. package/out/search/SearchModal.d.ts.map +1 -0
  349. package/out/search/SearchModal.js +187 -0
  350. package/out/search/SearchModal.js.map +1 -0
  351. package/out/search/types.d.ts +64 -0
  352. package/out/search/types.d.ts.map +1 -0
  353. package/out/search/types.js +13 -0
  354. package/out/search/types.js.map +1 -0
  355. package/out/search.d.ts +11 -0
  356. package/out/search.d.ts.map +1 -0
  357. package/out/search.js +9 -0
  358. package/out/search.js.map +1 -0
  359. package/package.json +132 -133
  360. package/styles.css +1340 -202
  361. package/tokens/index.css +26 -7
  362. package/tokens/tokens.json +121 -6
  363. package/tokens/tokens.resolver.json +1 -1
  364. package/lib/map/index.ts +0 -76
  365. package/out/map/index.d.ts +0 -52
  366. package/out/map/index.d.ts.map +0 -1
  367. package/out/map/index.js +0 -36
  368. package/out/map/index.js.map +0 -1
  369. package/styleframe.config.ts +0 -321
  370. package/vitest.node.config.ts +0 -20
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Draws a grid of meridians and parallels under the basemap so that the globe is readable while
7
+ * tiles load. Loaded tiles cover the grid.
8
+ */
9
+
10
+ import { type ReactNode, useMemo } from "react"
11
+ import { Layer, Source } from "react-map-gl/maplibre"
12
+
13
+ import { buildGraticule } from "./graticule.ts"
14
+
15
+ /**
16
+ * Props for {@link GraticuleLayer}.
17
+ */
18
+ export interface GraticuleLayerProps {
19
+ /**
20
+ * The basemap layer to draw the grid beneath.
21
+ * Without it, the grid is drawn on top.
22
+ */
23
+ beforeID?: string
24
+ /**
25
+ * Hides the grid. @default false
26
+ */
27
+ hidden?: boolean
28
+ }
29
+
30
+ /**
31
+ * Renders the graticule source and line layer.
32
+ */
33
+ export function GraticuleLayer({ beforeID, hidden = false }: GraticuleLayerProps): ReactNode {
34
+ const data = useMemo(() => buildGraticule(), [])
35
+
36
+ return (
37
+ <>
38
+ <Source id="mw-graticule" type="geojson" data={data} />
39
+ <Layer
40
+ id="mw-graticule-line"
41
+ type="line"
42
+ source="mw-graticule"
43
+ {...(beforeID ? { beforeId: beforeID } : {})}
44
+ layout={{ visibility: hidden ? "none" : "visible", "line-cap": "round" }}
45
+ paint={{
46
+ "line-color": "#8aa0c8",
47
+ // The grid fades out by zoom 8, where map detail makes it unnecessary.
48
+ "line-opacity": ["interpolate", ["linear"], ["zoom"], 0, 0.3, 3, 0.2, 6, 0.09, 8, 0],
49
+ "line-width": ["interpolate", ["linear"], ["zoom"], 0, 0.5, 4, 0.75],
50
+ }}
51
+ />
52
+ </>
53
+ )
54
+ }
@@ -5,18 +5,18 @@
5
5
  *
6
6
  * `<MapCanvas>` — the declarative map shell for the geocoder demo, over `react-map-gl/maplibre` (v8).
7
7
  * Phase 1 is the shell only: a `<Map>` that takes a host-composed `mapStyle`, an initial (or
8
- * controlled) view state, and a `children` slot for the overlays/markers that land in later phases.
8
+ * controlled) view state and a `children` slot for overlays/markers from later phases.
9
9
  * No overlay/marker/camera logic lives here yet.
10
10
  *
11
- * NODE-IMPORT SAFETY: this module imports `react-map-gl/maplibre` (which pulls `maplibre-gl` — WebGL
12
- * + DOM at import) at module scope, so it is reachable ONLY through the `@mailwoman/react/map`
11
+ * node-import safety: this module imports `react-map-gl/maplibre` (which pulls `maplibre-gl` — WebGL
12
+ * + DOM at import) at module scope, so it is reachable only through the `@mailwoman/react/map`
13
13
  * subpath, never from the package root (`index.ts`). The bare `import("@mailwoman/react")` a node
14
14
  * consumer (or the CI smoke IMPORT_CHECK) runs must never transitively load this file. Keep it out of
15
15
  * the root barrel.
16
16
  *
17
17
  * CSS: the package imports no CSS from its modules (the node-safe invariant), so a host that renders
18
18
  * `<MapCanvas>` must import `maplibre-gl/dist/maplibre-gl.css` itself (plus `@mailwoman/react/styles.css`
19
- * for the `.mw-demo-map` container). `<MapCanvas>` is intrinsically a client component — a host that
19
+ * for the `.mw-demo-map` container). `<MapCanvas>` is intrinsically a client component. A host that
20
20
  * server-renders should wrap it in a client boundary (the package's `ClientOnly`), exactly as the
21
21
  * composed `Geocoder` will in a later phase.
22
22
  */
@@ -31,8 +31,10 @@ import type { MapProps, MapRef, ViewStateChangeEvent } from "react-map-gl/maplib
31
31
  export type MapCanvasStyle = NonNullable<MapProps["mapStyle"]>
32
32
 
33
33
  /**
34
- * Override for the `<Map>` props `<MapCanvas>` does not surface explicitly (e.g. `minZoom`, `attributionControl`,
35
- * `maplibreLogo`). The controlled fields MapCanvas owns are omitted so they can't be set twice.
34
+ * Override for the `<Map>` props `<MapCanvas>` does not surface explicitly
35
+ * (e.g. `minZoom`, `attributionControl`, `maplibreLogo`).
36
+ *
37
+ * The controlled fields MapCanvas owns are omitted so they can't be set twice.
36
38
  */
37
39
  export type MapCanvasExtraProps = Partial<
38
40
  Omit<MapProps, "mapStyle" | "initialViewState" | "viewState" | "onMove" | "children" | "style" | "ref">
@@ -44,11 +46,15 @@ export interface MapCanvasProps {
44
46
  */
45
47
  mapStyle: MapCanvasStyle
46
48
  /**
47
- * Uncontrolled initial camera. Use this OR `viewState`, not both.
49
+ * Uncontrolled initial camera.
50
+ *
51
+ * Use this or `viewState`, not both.
48
52
  */
49
53
  initialViewState?: MapProps["initialViewState"]
50
54
  /**
51
- * Controlled camera. Pair with `onMove` to persist it.
55
+ * Controlled camera.
56
+ *
57
+ * Pair with `onMove` to persist it.
52
58
  */
53
59
  viewState?: MapProps["viewState"]
54
60
  /**
@@ -64,7 +70,9 @@ export interface MapCanvasProps {
64
70
  */
65
71
  mapRef?: Ref<MapRef>
66
72
  /**
67
- * Overlays, markers, and controls — rendered as `<Map>` children. Empty in phase 1.
73
+ * Overlays, markers and controls — rendered as `<Map>` children.
74
+ *
75
+ * Empty in phase 1.
68
76
  */
69
77
  children?: ReactNode
70
78
  /**
@@ -76,7 +84,7 @@ export interface MapCanvasProps {
76
84
  */
77
85
  style?: CSSProperties
78
86
  /**
79
- * Remaining `<Map>` props MapCanvas does not surface explicitly.
87
+ * Other `<Map>` props that MapCanvas does not surface explicitly.
80
88
  */
81
89
  mapProps?: MapCanvasExtraProps
82
90
  }
@@ -84,8 +92,11 @@ export interface MapCanvasProps {
84
92
  const FILL: CSSProperties = { width: "100%", height: "100%" }
85
93
 
86
94
  /**
87
- * The controlled-viewport map shell. Renders a sized wrapper around a `react-map-gl/maplibre` `<Map>`; everything
88
- * host-specific (the composed `mapStyle`, the initial center) is injected, and overlays ride in as `children`.
95
+ * The controlled-viewport map shell.
96
+ *
97
+ * Renders a sized wrapper around a `react-map-gl/maplibre` `<Map>`; everything
98
+ * host-specific (the composed `mapStyle`, the initial center) is injected.
99
+ * Overlays arrive as `children`.
89
100
  */
90
101
  export function MapCanvas({
91
102
  mapStyle,
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Render example-query chips in a single horizontally scrollable row.
7
+ */
8
+
9
+ import type { ReactNode } from "react"
10
+
11
+ import { cx } from "#common/cx"
12
+
13
+ export interface MapChip {
14
+ /**
15
+ * What the chip reads.
16
+ */
17
+ label: string
18
+ /**
19
+ * The query the chip stands for, handed back on press.
20
+ *
21
+ * Defaults to the label.
22
+ */
23
+ value?: string
24
+ /**
25
+ * Rendered before the label — a category mark.
26
+ */
27
+ icon?: ReactNode
28
+ }
29
+
30
+ export interface MapChipRowProps {
31
+ chips: MapChip[]
32
+ /**
33
+ * Fired with the chip's `value` (or its label) when one is pressed.
34
+ */
35
+ onPick: (value: string) => void
36
+ /**
37
+ * The chip matching this value reads as pressed.
38
+ */
39
+ activeValue?: string
40
+ disabled?: boolean
41
+ className?: string
42
+ /**
43
+ * Accessible name for the row, e.g. "Example addresses".
44
+ */
45
+ label?: string
46
+ }
47
+
48
+ export function MapChipRow({ chips, onPick, activeValue, disabled, className, label }: MapChipRowProps): ReactNode {
49
+ if (!chips.length) return null
50
+
51
+ return (
52
+ <div className={cx("mw-map-chiprow", className)} role="group" aria-label={label}>
53
+ {chips.map((chip) => {
54
+ const value = chip.value ?? chip.label
55
+ const active = value === activeValue
56
+
57
+ return (
58
+ <button
59
+ key={value}
60
+ type="button"
61
+ className={cx("mw-map-chip", active && "mw-map-chip--active")}
62
+ aria-pressed={active}
63
+ disabled={disabled}
64
+ onClick={() => onPick(value)}
65
+ >
66
+ {chip.icon ? <span className="mw-map-chip__icon">{chip.icon}</span> : null}
67
+ {chip.label}
68
+ </button>
69
+ )
70
+ })}
71
+ </div>
72
+ )
73
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Shows a compass that rotates with the map bearing and hides when the map faces north. A press
7
+ * it asks the host to reset the bearing.
8
+ */
9
+
10
+ import type { ReactNode } from "react"
11
+
12
+ import { cx } from "#common/cx"
13
+
14
+ /**
15
+ * The compass counts as facing north below this bearing.
16
+ * It fades out below the same threshold.
17
+ */
18
+ const HIDE_BELOW_DEGREES = 0.5
19
+
20
+ /**
21
+ * Props for {@link MapCompass}.
22
+ */
23
+ export interface MapCompassProps {
24
+ /**
25
+ * The map's bearing in degrees as MapLibre reports it, where 0 is north.
26
+ */
27
+ bearing: number
28
+ /**
29
+ * Called when the compass is pressed.
30
+ * The host should reset its map to north.
31
+ */
32
+ onResetNorth: () => void
33
+ /**
34
+ * The control's accessible name. @default "Reset bearing to north"
35
+ */
36
+ label?: string
37
+ className?: string
38
+ }
39
+
40
+ /**
41
+ * Renders the compass button.
42
+ */
43
+ export function MapCompass({ bearing, onResetNorth, label, className }: MapCompassProps): ReactNode {
44
+ const facingNorth = Math.abs(bearing) < HIDE_BELOW_DEGREES
45
+
46
+ return (
47
+ <button
48
+ type="button"
49
+ className={cx("mw-map-compass", facingNorth && "mw-map-compass--north", className)}
50
+ aria-label={label ?? "Reset bearing to north"}
51
+ title={label ?? "Reset bearing to north"}
52
+ // The hidden compass leaves the tab order and the accessibility tree.
53
+ aria-hidden={facingNorth}
54
+ tabIndex={facingNorth ? -1 : 0}
55
+ onClick={onResetNorth}
56
+ >
57
+ <svg viewBox="0 0 32 32" width="26" height="26" aria-hidden="true" focusable="false">
58
+ <g style={{ transform: `rotate(${-bearing}deg)`, transformOrigin: "16px 16px" }}>
59
+ <circle cx="16" cy="16" r="12.5" className="mw-map-compass__dial" />
60
+
61
+ {/* The north needle, then the south needle. */}
62
+ <path d="M16 5.5 L20.5 16 L16 16 Z M16 5.5 L11.5 16 L16 16 Z" className="mw-map-compass__north" />
63
+ <path d="M16 26.5 L20.5 16 L16 16 Z M16 26.5 L11.5 16 L16 16 Z" className="mw-map-compass__south" />
64
+ </g>
65
+ </svg>
66
+ </button>
67
+ )
68
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Provide accessible map-control buttons arranged in grouped capsules.
7
+ */
8
+
9
+ import type { ReactNode } from "react"
10
+
11
+ import { cx } from "#common/cx"
12
+
13
+ export interface MapControlButtonProps {
14
+ /**
15
+ * The mark inside the control — an SVG icon or a single glyph.
16
+ */
17
+ children: ReactNode
18
+ /**
19
+ * The control's accessible name.
20
+ *
21
+ * Required: the visible content is a mark rather than a word.
22
+ */
23
+ label: string
24
+ onPress: () => void
25
+ /**
26
+ * Reads as active, for a control that toggles a layer or a mode.
27
+ */
28
+ active?: boolean
29
+ disabled?: boolean
30
+ className?: string
31
+ }
32
+
33
+ export function MapControlButton({
34
+ children,
35
+ label,
36
+ onPress,
37
+ active,
38
+ disabled,
39
+ className,
40
+ }: MapControlButtonProps): ReactNode {
41
+ return (
42
+ <button
43
+ type="button"
44
+ className={cx("mw-map-control", active && "mw-map-control--active", className)}
45
+ aria-label={label}
46
+ aria-pressed={active}
47
+ title={label}
48
+ disabled={disabled}
49
+ onClick={onPress}
50
+ >
51
+ {children}
52
+ </button>
53
+ )
54
+ }
55
+
56
+ export interface MapControlGroupProps {
57
+ children: ReactNode
58
+ className?: string
59
+ }
60
+
61
+ /**
62
+ * One capsule.
63
+ *
64
+ * Put related controls in the same group.
65
+ * Give an unrelated one its own.
66
+ */
67
+ export function MapControlGroup({ children, className }: MapControlGroupProps): ReactNode {
68
+ return <div className={cx("mw-map-control-group", className)}>{children}</div>
69
+ }
70
+
71
+ export interface MapControlStackProps {
72
+ /**
73
+ * Groups, top to bottom.
74
+ */
75
+ children: ReactNode
76
+ /**
77
+ * Which edge the column sits against. @default "right"
78
+ */
79
+ side?: "left" | "right"
80
+ className?: string
81
+ /**
82
+ * Accessible name for the column, e.g. "Map controls".
83
+ */
84
+ label?: string
85
+ }
86
+
87
+ export function MapControlStack({ children, side = "right", className, label }: MapControlStackProps): ReactNode {
88
+ return (
89
+ <div
90
+ className={cx("mw-map-control-stack", `mw-map-control-stack--${side}`, className)}
91
+ role="group"
92
+ aria-label={label}
93
+ >
94
+ {children}
95
+ </div>
96
+ )
97
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ */
6
+
7
+ import { type ReactNode, useEffect, useId, useRef, useState } from "react"
8
+
9
+ import { cx } from "#common/cx"
10
+
11
+ export interface MapFooterProps {
12
+ /**
13
+ * The left side: a wordmark, a body name, a line saying what the page does.
14
+ */
15
+ identity?: ReactNode
16
+ /**
17
+ * What is loading right now, beside the identity.
18
+ */
19
+ status?: ReactNode
20
+ /**
21
+ * The credits, one entry per source, shown in the popover the attribution button opens.
22
+ */
23
+ attribution?: ReactNode[]
24
+ /**
25
+ * What the attribution button reads. @default "Sources"
26
+ */
27
+ attributionLabel?: string
28
+ className?: string
29
+ }
30
+
31
+ export function MapFooter({
32
+ identity,
33
+ status,
34
+ attribution,
35
+ attributionLabel = "Sources",
36
+ className,
37
+ }: MapFooterProps): ReactNode {
38
+ const [open, setOpen] = useState(false)
39
+ const popoverID = useId()
40
+ const root = useRef<HTMLElement>(null)
41
+
42
+ useEffect(() => {
43
+ if (!open) return
44
+
45
+ const onPointerDown = (event: PointerEvent) => {
46
+ if (!root.current?.contains(event.target as Node)) {
47
+ setOpen(false)
48
+ }
49
+ }
50
+
51
+ const onKeyDown = (event: KeyboardEvent) => {
52
+ if (event.key === "Escape") {
53
+ setOpen(false)
54
+ }
55
+ }
56
+
57
+ document.addEventListener("pointerdown", onPointerDown)
58
+ document.addEventListener("keydown", onKeyDown)
59
+
60
+ return () => {
61
+ document.removeEventListener("pointerdown", onPointerDown)
62
+ document.removeEventListener("keydown", onKeyDown)
63
+ }
64
+ }, [open])
65
+
66
+ if (!identity && !status && !attribution?.length) return null
67
+
68
+ return (
69
+ <footer className={cx("mw-map-footer", className)} ref={root}>
70
+ <div className="mw-map-footer__identity">
71
+ {identity}
72
+ {status ? <span className="mw-map-footer__status">{status}</span> : null}
73
+ </div>
74
+
75
+ {attribution?.length ? (
76
+ <div className="mw-map-footer__sources">
77
+ {open ? (
78
+ <div className="mw-map-footer__popover" id={popoverID} role="group" aria-label="Map data sources">
79
+ {attribution.map((entry, index) => (
80
+ // The entries are a fixed, ordered credit list that never reorders, so position is the identity.
81
+ <span key={index} className="mw-map-footer__credit">
82
+ {entry}
83
+ </span>
84
+ ))}
85
+ </div>
86
+ ) : null}
87
+
88
+ <button
89
+ type="button"
90
+ className="mw-map-footer__attribution-button"
91
+ aria-expanded={open}
92
+ aria-controls={open ? popoverID : undefined}
93
+ onClick={() => setOpen((value) => !value)}
94
+ >
95
+ {attributionLabel}
96
+ </button>
97
+ </div>
98
+ ) : null}
99
+ </footer>
100
+ )
101
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Show loading progress at the top of the viewport.
7
+ * Use a determinate bar when progress is known, otherwise an indeterminate bar.
8
+ */
9
+
10
+ import type { ReactNode } from "react"
11
+
12
+ import { cx } from "#common/cx"
13
+
14
+ export interface MapProgressBarProps {
15
+ /**
16
+ * Whether the bar is showing at all.
17
+ *
18
+ * It stays mounted through its fade rather than unmounting on the transition.
19
+ */
20
+ active: boolean
21
+ /**
22
+ * Completed steps over total steps, in [0, 1].
23
+ *
24
+ * Omit for work whose length is unknown.
25
+ */
26
+ fraction?: number | null
27
+ /**
28
+ * The bar's accessible name.
29
+ * What is loading, in a few words.
30
+ */
31
+ label: string
32
+ className?: string
33
+ }
34
+
35
+ export function MapProgressBar({ active, fraction, label, className }: MapProgressBarProps): ReactNode {
36
+ const determinate = typeof fraction === "number" && Number.isFinite(fraction)
37
+ const clamped = determinate ? Math.min(1, Math.max(0, fraction)) : 0
38
+
39
+ return (
40
+ <div
41
+ className={cx("mw-map-progress", !active && "mw-map-progress--idle", className)}
42
+ role="progressbar"
43
+ aria-label={label}
44
+ aria-hidden={!active}
45
+ aria-valuemin={determinate ? 0 : undefined}
46
+ aria-valuemax={determinate ? 100 : undefined}
47
+ aria-valuenow={determinate ? Math.round(clamped * 100) : undefined}
48
+ >
49
+ <div
50
+ className={cx("mw-map-progress__fill", !determinate && "mw-map-progress__fill--indeterminate")}
51
+ style={determinate ? { transform: `scaleX(${clamped})` } : undefined}
52
+ />
53
+ </div>
54
+ )
55
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Presentational search pill for map UIs. The host supplies the input and icons.
7
+ * Busy state uses a bottom-edge indicator to avoid changing the pill layout.
8
+ */
9
+
10
+ import type { ReactNode } from "react"
11
+
12
+ import { cx } from "#common/cx"
13
+
14
+ /**
15
+ * Draw a consistently sized magnifier for the leading slot.
16
+ */
17
+ export function SearchGlyph(): ReactNode {
18
+ return (
19
+ <svg
20
+ viewBox="0 0 20 20"
21
+ width="18"
22
+ height="18"
23
+ aria-hidden="true"
24
+ focusable="false"
25
+ className="mw-map-searchbar__glyph"
26
+ >
27
+ <circle cx="8.75" cy="8.75" r="5.5" fill="none" stroke="currentColor" strokeWidth="1.8" />
28
+ <line x1="12.9" y1="12.9" x2="17" y2="17" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
29
+ </svg>
30
+ )
31
+ }
32
+
33
+ export interface MapSearchBarProps {
34
+ /**
35
+ * The input (or combobox) the host owns.
36
+ *
37
+ * Rendered between the two slots.
38
+ */
39
+ children: ReactNode
40
+ /**
41
+ * Rendered before the input — a magnifier, a body mark, a back arrow.
42
+ */
43
+ leading?: ReactNode
44
+ /**
45
+ * Rendered after the input — a microphone, a menu.
46
+ *
47
+ * Not a spinner: pass {@link busy} instead.
48
+ */
49
+ trailing?: ReactNode
50
+ /**
51
+ * A query is running.
52
+ *
53
+ * Draws a progress hairline along the pill's lower edge.
54
+ * The hairline takes no layout space.
55
+ */
56
+ busy?: boolean
57
+ /**
58
+ * Extra class on the pill.
59
+ */
60
+ className?: string
61
+ /**
62
+ * Accessible name for the surrounding region, when the bar is the app's primary search.
63
+ */
64
+ label?: string
65
+ }
66
+
67
+ export function MapSearchBar({ children, leading, trailing, busy, className, label }: MapSearchBarProps): ReactNode {
68
+ return (
69
+ <div
70
+ className={cx("mw-map-searchbar", busy && "mw-map-searchbar--busy", className)}
71
+ role={label ? "search" : undefined}
72
+ aria-label={label}
73
+ aria-busy={busy}
74
+ >
75
+ {leading ? <span className="mw-map-searchbar__slot mw-map-searchbar__slot--leading">{leading}</span> : null}
76
+
77
+ <span className="mw-map-searchbar__field">{children}</span>
78
+
79
+ {trailing ? <span className="mw-map-searchbar__slot mw-map-searchbar__slot--trailing">{trailing}</span> : null}
80
+ </div>
81
+ )
82
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @copyright Sister Software
3
+ * @license AGPL-3.0
4
+ * @author Teffen Ellis, et al.
5
+ *
6
+ * Side panel over the map with a heading, close button and Escape dismissal.
7
+ */
8
+
9
+ import { type ReactNode, useEffect } from "react"
10
+
11
+ import { cx } from "#common/cx"
12
+
13
+ export interface SheetCloseProps {
14
+ /**
15
+ * What is being closed, for the accessible name: "Close the result", "Close About".
16
+ */
17
+ label: string
18
+ onClose: () => void
19
+ className?: string
20
+ }
21
+
22
+ /**
23
+ * Accessible button for dismissing a sheet.
24
+ */
25
+ export function SheetClose({ label, onClose, className }: SheetCloseProps): ReactNode {
26
+ return (
27
+ <button type="button" className={cx("mw-map-sheet__close", className)} aria-label={label} onClick={onClose}>
28
+ <span aria-hidden="true">×</span>
29
+ </button>
30
+ )
31
+ }
32
+
33
+ export interface MapSheetProps {
34
+ /**
35
+ * The sheet's heading.
36
+ * It also supplies the accessible name.
37
+ */
38
+ title: string
39
+ /**
40
+ * Dismiss the sheet.
41
+ */
42
+ onClose: () => void
43
+ children: ReactNode
44
+ className?: string
45
+ }
46
+
47
+ export function MapSheet({ title, onClose, children, className }: MapSheetProps): ReactNode {
48
+ useEffect(() => {
49
+ const onKeyDown = (event: KeyboardEvent) => {
50
+ if (event.key === "Escape") {
51
+ onClose()
52
+ }
53
+ }
54
+
55
+ document.addEventListener("keydown", onKeyDown)
56
+
57
+ return () => document.removeEventListener("keydown", onKeyDown)
58
+ }, [onClose])
59
+
60
+ return (
61
+ <aside className={cx("mw-map-sheet mw-map-sheet--side", className)} aria-label={title}>
62
+ <div className="mw-map-sheet__header">
63
+ <h2 className="mw-map-sheet__title">{title}</h2>
64
+
65
+ <SheetClose label={`Close ${title}`} onClose={onClose} />
66
+ </div>
67
+
68
+ <div className="mw-map-sheet__body">{children}</div>
69
+ </aside>
70
+ )
71
+ }
@@ -9,7 +9,7 @@
9
9
  * the host composes the specs, the package renders them, react-map-gl owns the add/remove lifecycle.
10
10
  *
11
11
  * Each overlay's `visible` flag flips the layers' `visibility` layout property — the declarative control a
12
- * layer-toggle control drives in a later phase. NODE-IMPORT SAFETY: imports `react-map-gl/maplibre` —
12
+ * layer-toggle control drives in a later phase. node-import safety: imports `react-map-gl/maplibre` —
13
13
  * reachable only via `@mailwoman/react/map`.
14
14
  */
15
15