@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.
- package/README.md +17 -16
- package/fonts.css +97 -22
- package/lib/common/About.tsx +87 -71
- package/lib/common/ClientOnly.tsx +2 -14
- package/lib/common/CopyButton.tsx +3 -1
- package/lib/common/LoadingIndicator.tsx +4 -2
- package/lib/common/PresetChips.tsx +9 -8
- package/lib/common/confidence-tiers.ts +7 -6
- package/lib/common/cx.ts +3 -1
- package/lib/common/text-tokens.ts +7 -4
- package/lib/common/useBuildInfo.ts +54 -0
- package/lib/common/useClipboard.ts +2 -11
- package/lib/common.ts +22 -0
- package/lib/index.ts +27 -112
- package/lib/map/AppIdentity.tsx +85 -0
- package/lib/map/BackendControl.tsx +16 -13
- package/lib/map/CompareToggle.tsx +11 -33
- package/lib/map/DebugInfo.tsx +88 -0
- package/lib/map/Geocoder.tsx +92 -58
- package/lib/map/GeocoderControls.tsx +582 -127
- package/lib/map/GraticuleLayer.tsx +54 -0
- package/lib/map/MapCanvas.tsx +23 -12
- package/lib/map/MapChipRow.tsx +73 -0
- package/lib/map/MapCompass.tsx +68 -0
- package/lib/map/MapControlStack.tsx +97 -0
- package/lib/map/MapFooter.tsx +101 -0
- package/lib/map/MapProgressBar.tsx +55 -0
- package/lib/map/MapSearchBar.tsx +82 -0
- package/lib/map/MapSheet.tsx +71 -0
- package/lib/map/OverlayLayers.tsx +1 -1
- package/lib/map/PlaceAutocomplete.tsx +10 -21
- package/lib/map/PlaceMarker.tsx +4 -2
- package/lib/map/ResolvedPlaceLayers.tsx +5 -14
- package/lib/map/ResultCamera.tsx +8 -31
- package/lib/map/ResultOverlay.tsx +2 -12
- package/lib/map/ResultPanel.tsx +1 -20
- package/lib/map/VersionPicker.tsx +10 -19
- package/lib/map/fake-runtime.ts +10 -12
- package/lib/map/geometry.ts +14 -47
- package/lib/map/graticule.ts +80 -0
- package/lib/map/map-debug.ts +1 -1
- package/lib/map/place-render.ts +28 -60
- package/lib/map/types.ts +65 -97
- package/lib/map/useCompareState.ts +5 -4
- package/lib/map/useGeocode.ts +7 -5
- package/lib/map/useMapBearing.ts +56 -0
- package/lib/map/useMapLabelPick.ts +114 -0
- package/lib/map/useMapPlaceRender.ts +2 -6
- package/lib/map/usePlaceAutocomplete.ts +44 -50
- package/lib/map.ts +95 -0
- package/lib/pipeline/CandidatePicker.tsx +13 -5
- package/lib/pipeline/ConfidenceCell.tsx +1 -1
- package/lib/pipeline/FailureDiagnostic.tsx +8 -2
- package/lib/pipeline/QueryForm.tsx +5 -2
- package/lib/pipeline/ResolvedPlace.tsx +1 -1
- package/lib/pipeline/SpanHighlight.tsx +7 -5
- package/lib/pipeline/TimingPanel.tsx +1 -1
- package/lib/pipeline/TreeView.tsx +1 -1
- package/lib/pipeline/copy.ts +22 -25
- package/lib/pipeline/presets.ts +15 -13
- package/lib/pipeline/types.ts +21 -14
- package/lib/pipeline/useParsePipeline.ts +38 -24
- package/lib/pipeline.ts +36 -0
- package/lib/poi/AbstainPanel.tsx +1 -1
- package/lib/poi/LiveResultsBlock.tsx +2 -1
- package/lib/poi/POIExplorer.tsx +24 -15
- package/lib/poi/QueryInput.tsx +1 -1
- package/lib/poi/SubjectPanel.tsx +4 -3
- package/lib/poi/runtime.ts +5 -16
- package/lib/poi/types.ts +39 -27
- package/lib/poi/usePOISearch.ts +39 -44
- package/lib/poi.ts +39 -0
- package/lib/runtime/useReleaseRuntime.ts +125 -74
- package/lib/runtime.ts +16 -0
- package/lib/search/SearchModal.tsx +317 -0
- package/lib/search/search.css +144 -0
- package/lib/search/types.ts +67 -0
- package/lib/search.ts +11 -0
- package/out/common/About.d.ts +13 -4
- package/out/common/About.d.ts.map +1 -1
- package/out/common/About.js +5 -2
- package/out/common/About.js.map +1 -1
- package/out/common/ClientOnly.d.ts +2 -11
- package/out/common/ClientOnly.d.ts.map +1 -1
- package/out/common/ClientOnly.js +1 -13
- package/out/common/ClientOnly.js.map +1 -1
- package/out/common/CopyButton.d.ts +3 -1
- package/out/common/CopyButton.d.ts.map +1 -1
- package/out/common/CopyButton.js.map +1 -1
- package/out/common/LoadingIndicator.d.ts +4 -2
- package/out/common/LoadingIndicator.d.ts.map +1 -1
- package/out/common/LoadingIndicator.js.map +1 -1
- package/out/common/PresetChips.d.ts +9 -8
- package/out/common/PresetChips.d.ts.map +1 -1
- package/out/common/PresetChips.js.map +1 -1
- package/out/common/confidence-tiers.d.ts +7 -6
- package/out/common/confidence-tiers.d.ts.map +1 -1
- package/out/common/confidence-tiers.js +7 -6
- package/out/common/confidence-tiers.js.map +1 -1
- package/out/common/cx.d.ts +3 -1
- package/out/common/cx.d.ts.map +1 -1
- package/out/common/cx.js +3 -1
- package/out/common/cx.js.map +1 -1
- package/out/common/text-tokens.d.ts +4 -3
- package/out/common/text-tokens.d.ts.map +1 -1
- package/out/common/text-tokens.js +6 -3
- package/out/common/text-tokens.js.map +1 -1
- package/out/common/useBuildInfo.d.ts +27 -0
- package/out/common/useBuildInfo.d.ts.map +1 -0
- package/out/common/useBuildInfo.js +32 -0
- package/out/common/useBuildInfo.js.map +1 -0
- package/out/common/useClipboard.d.ts +1 -3
- package/out/common/useClipboard.d.ts.map +1 -1
- package/out/common/useClipboard.js +2 -11
- package/out/common/useClipboard.js.map +1 -1
- package/out/common.d.ts +22 -0
- package/out/common.d.ts.map +1 -0
- package/out/common.js +15 -0
- package/out/common.js.map +1 -0
- package/out/index.d.ts +26 -71
- package/out/index.d.ts.map +1 -1
- package/out/index.js +21 -47
- package/out/index.js.map +1 -1
- package/out/map/AppIdentity.d.ts +42 -0
- package/out/map/AppIdentity.d.ts.map +1 -0
- package/out/map/AppIdentity.js +16 -0
- package/out/map/AppIdentity.js.map +1 -0
- package/out/map/BackendControl.d.ts +9 -10
- package/out/map/BackendControl.d.ts.map +1 -1
- package/out/map/BackendControl.js +2 -2
- package/out/map/BackendControl.js.map +1 -1
- package/out/map/CompareToggle.d.ts +8 -31
- package/out/map/CompareToggle.d.ts.map +1 -1
- package/out/map/CompareToggle.js +5 -2
- package/out/map/CompareToggle.js.map +1 -1
- package/out/map/DebugInfo.d.ts +34 -0
- package/out/map/DebugInfo.d.ts.map +1 -0
- package/out/map/DebugInfo.js +39 -0
- package/out/map/DebugInfo.js.map +1 -0
- package/out/map/Geocoder.d.ts +47 -26
- package/out/map/Geocoder.d.ts.map +1 -1
- package/out/map/Geocoder.js +34 -50
- package/out/map/Geocoder.js.map +1 -1
- package/out/map/GeocoderControls.d.ts +23 -33
- package/out/map/GeocoderControls.d.ts.map +1 -1
- package/out/map/GeocoderControls.js +277 -16
- package/out/map/GeocoderControls.js.map +1 -1
- package/out/map/GraticuleLayer.d.ts +28 -0
- package/out/map/GraticuleLayer.d.ts.map +1 -0
- package/out/map/GraticuleLayer.js +25 -0
- package/out/map/GraticuleLayer.js.map +1 -0
- package/out/map/MapCanvas.d.ts +23 -12
- package/out/map/MapCanvas.d.ts.map +1 -1
- package/out/map/MapCanvas.js +5 -2
- package/out/map/MapCanvas.js.map +1 -1
- package/out/map/MapChipRow.d.ts +43 -0
- package/out/map/MapChipRow.d.ts.map +1 -0
- package/out/map/MapChipRow.js +12 -0
- package/out/map/MapChipRow.js.map +1 -0
- package/out/map/MapCompass.d.ts +33 -0
- package/out/map/MapCompass.d.ts.map +1 -0
- package/out/map/MapCompass.js +15 -0
- package/out/map/MapCompass.js.map +1 -0
- package/out/map/MapControlStack.d.ts +56 -0
- package/out/map/MapControlStack.d.ts.map +1 -0
- package/out/map/MapControlStack.js +18 -0
- package/out/map/MapControlStack.js.map +1 -0
- package/out/map/MapFooter.d.ts +27 -0
- package/out/map/MapFooter.d.ts.map +1 -0
- package/out/map/MapFooter.js +37 -0
- package/out/map/MapFooter.js.map +1 -0
- package/out/map/MapProgressBar.d.ts +31 -0
- package/out/map/MapProgressBar.d.ts.map +1 -0
- package/out/map/MapProgressBar.js +8 -0
- package/out/map/MapProgressBar.js.map +1 -0
- package/out/map/MapSearchBar.d.ts +48 -0
- package/out/map/MapSearchBar.d.ts.map +1 -0
- package/out/map/MapSearchBar.js +12 -0
- package/out/map/MapSearchBar.js.map +1 -0
- package/out/map/MapSheet.d.ts +35 -0
- package/out/map/MapSheet.d.ts.map +1 -0
- package/out/map/MapSheet.js +29 -0
- package/out/map/MapSheet.js.map +1 -0
- package/out/map/OverlayLayers.d.ts +1 -1
- package/out/map/OverlayLayers.js +2 -2
- package/out/map/PlaceAutocomplete.d.ts +10 -21
- package/out/map/PlaceAutocomplete.d.ts.map +1 -1
- package/out/map/PlaceAutocomplete.js +2 -1
- package/out/map/PlaceAutocomplete.js.map +1 -1
- package/out/map/PlaceMarker.d.ts +4 -2
- package/out/map/PlaceMarker.d.ts.map +1 -1
- package/out/map/PlaceMarker.js +3 -1
- package/out/map/PlaceMarker.js.map +1 -1
- package/out/map/ResolvedPlaceLayers.d.ts +5 -14
- package/out/map/ResolvedPlaceLayers.d.ts.map +1 -1
- package/out/map/ResolvedPlaceLayers.js +2 -2
- package/out/map/ResolvedPlaceLayers.js.map +1 -1
- package/out/map/ResultCamera.d.ts +8 -31
- package/out/map/ResultCamera.d.ts.map +1 -1
- package/out/map/ResultCamera.js +3 -28
- package/out/map/ResultCamera.js.map +1 -1
- package/out/map/ResultOverlay.d.ts +2 -12
- package/out/map/ResultOverlay.d.ts.map +1 -1
- package/out/map/ResultOverlay.js +1 -2
- package/out/map/ResultOverlay.js.map +1 -1
- package/out/map/ResultPanel.d.ts +1 -20
- package/out/map/ResultPanel.d.ts.map +1 -1
- package/out/map/ResultPanel.js +1 -1
- package/out/map/ResultPanel.js.map +1 -1
- package/out/map/VersionPicker.d.ts +7 -19
- package/out/map/VersionPicker.d.ts.map +1 -1
- package/out/map/VersionPicker.js +5 -2
- package/out/map/VersionPicker.js.map +1 -1
- package/out/map/fake-runtime.d.ts +9 -11
- package/out/map/fake-runtime.d.ts.map +1 -1
- package/out/map/fake-runtime.js +9 -12
- package/out/map/fake-runtime.js.map +1 -1
- package/out/map/geometry.d.ts +13 -34
- package/out/map/geometry.d.ts.map +1 -1
- package/out/map/geometry.js +9 -34
- package/out/map/geometry.js.map +1 -1
- package/out/map/graticule.d.ts +17 -0
- package/out/map/graticule.d.ts.map +1 -0
- package/out/map/graticule.js +67 -0
- package/out/map/graticule.js.map +1 -0
- package/out/map/map-debug.d.ts +1 -1
- package/out/map/map-debug.d.ts.map +1 -1
- package/out/map/map-debug.js +1 -1
- package/out/map/map-debug.js.map +1 -1
- package/out/map/place-render.d.ts +23 -43
- package/out/map/place-render.d.ts.map +1 -1
- package/out/map/place-render.js +4 -39
- package/out/map/place-render.js.map +1 -1
- package/out/map/types.d.ts +67 -95
- package/out/map/types.d.ts.map +1 -1
- package/out/map/types.js +1 -10
- package/out/map/types.js.map +1 -1
- package/out/map/useCompareState.d.ts +5 -4
- package/out/map/useCompareState.d.ts.map +1 -1
- package/out/map/useCompareState.js +3 -3
- package/out/map/useCompareState.js.map +1 -1
- package/out/map/useGeocode.d.ts +5 -3
- package/out/map/useGeocode.d.ts.map +1 -1
- package/out/map/useGeocode.js +4 -4
- package/out/map/useGeocode.js.map +1 -1
- package/out/map/useMapBearing.d.ts +28 -0
- package/out/map/useMapBearing.d.ts.map +1 -0
- package/out/map/useMapBearing.js +30 -0
- package/out/map/useMapBearing.js.map +1 -0
- package/out/map/useMapLabelPick.d.ts +13 -0
- package/out/map/useMapLabelPick.d.ts.map +1 -0
- package/out/map/useMapLabelPick.js +95 -0
- package/out/map/useMapLabelPick.js.map +1 -0
- package/out/map/useMapPlaceRender.d.ts +2 -6
- package/out/map/useMapPlaceRender.d.ts.map +1 -1
- package/out/map/useMapPlaceRender.js +2 -6
- package/out/map/useMapPlaceRender.js.map +1 -1
- package/out/map/usePlaceAutocomplete.d.ts +30 -28
- package/out/map/usePlaceAutocomplete.d.ts.map +1 -1
- package/out/map/usePlaceAutocomplete.js +6 -33
- package/out/map/usePlaceAutocomplete.js.map +1 -1
- package/out/map.d.ts +69 -0
- package/out/map.d.ts.map +1 -0
- package/out/map.js +46 -0
- package/out/map.js.map +1 -0
- package/out/pipeline/CandidatePicker.d.ts +12 -2
- package/out/pipeline/CandidatePicker.d.ts.map +1 -1
- package/out/pipeline/CandidatePicker.js +2 -2
- package/out/pipeline/CandidatePicker.js.map +1 -1
- package/out/pipeline/ConfidenceCell.d.ts +1 -1
- package/out/pipeline/ConfidenceCell.js +1 -1
- package/out/pipeline/FailureDiagnostic.d.ts +8 -2
- package/out/pipeline/FailureDiagnostic.d.ts.map +1 -1
- package/out/pipeline/FailureDiagnostic.js +3 -0
- package/out/pipeline/FailureDiagnostic.js.map +1 -1
- package/out/pipeline/PipelineExplorer.js +1 -1
- package/out/pipeline/QueryForm.d.ts +5 -2
- package/out/pipeline/QueryForm.d.ts.map +1 -1
- package/out/pipeline/QueryForm.js +1 -1
- package/out/pipeline/QueryForm.js.map +1 -1
- package/out/pipeline/ResolvedPlace.js +1 -1
- package/out/pipeline/SpanHighlight.d.ts +4 -3
- package/out/pipeline/SpanHighlight.d.ts.map +1 -1
- package/out/pipeline/SpanHighlight.js +3 -2
- package/out/pipeline/SpanHighlight.js.map +1 -1
- package/out/pipeline/TimingPanel.d.ts +1 -1
- package/out/pipeline/TimingPanel.js +1 -1
- package/out/pipeline/TreeView.d.ts +1 -1
- package/out/pipeline/copy.d.ts +1 -1
- package/out/pipeline/copy.d.ts.map +1 -1
- package/out/pipeline/copy.js +4 -3
- package/out/pipeline/copy.js.map +1 -1
- package/out/pipeline/presets.d.ts +5 -4
- package/out/pipeline/presets.d.ts.map +1 -1
- package/out/pipeline/presets.js +15 -13
- package/out/pipeline/presets.js.map +1 -1
- package/out/pipeline/types.d.ts +21 -14
- package/out/pipeline/types.d.ts.map +1 -1
- package/out/pipeline/types.js +2 -4
- package/out/pipeline/types.js.map +1 -1
- package/out/pipeline/useParsePipeline.d.ts +15 -7
- package/out/pipeline/useParsePipeline.d.ts.map +1 -1
- package/out/pipeline/useParsePipeline.js +10 -7
- package/out/pipeline/useParsePipeline.js.map +1 -1
- package/out/pipeline.d.ts +33 -0
- package/out/pipeline.d.ts.map +1 -0
- package/out/pipeline.js +21 -0
- package/out/pipeline.js.map +1 -0
- package/out/poi/AbstainPanel.d.ts +1 -1
- package/out/poi/LiveResultsBlock.d.ts +2 -1
- package/out/poi/LiveResultsBlock.d.ts.map +1 -1
- package/out/poi/LiveResultsBlock.js +1 -1
- package/out/poi/LiveResultsBlock.js.map +1 -1
- package/out/poi/POIExplorer.d.ts +22 -12
- package/out/poi/POIExplorer.d.ts.map +1 -1
- package/out/poi/POIExplorer.js +10 -9
- package/out/poi/POIExplorer.js.map +1 -1
- package/out/poi/QueryInput.d.ts +1 -1
- package/out/poi/SubjectPanel.d.ts +2 -2
- package/out/poi/SubjectPanel.d.ts.map +1 -1
- package/out/poi/SubjectPanel.js +3 -2
- package/out/poi/SubjectPanel.js.map +1 -1
- package/out/poi/runtime.d.ts +5 -14
- package/out/poi/runtime.d.ts.map +1 -1
- package/out/poi/runtime.js +5 -16
- package/out/poi/runtime.js.map +1 -1
- package/out/poi/types.d.ts +39 -27
- package/out/poi/types.d.ts.map +1 -1
- package/out/poi/types.js +2 -4
- package/out/poi/types.js.map +1 -1
- package/out/poi/usePOISearch.d.ts +28 -17
- package/out/poi/usePOISearch.d.ts.map +1 -1
- package/out/poi/usePOISearch.js +6 -32
- package/out/poi/usePOISearch.js.map +1 -1
- package/out/poi.d.ts +23 -0
- package/out/poi.d.ts.map +1 -0
- package/out/poi.js +15 -0
- package/out/poi.js.map +1 -0
- package/out/runtime/useReleaseRuntime.d.ts +64 -65
- package/out/runtime/useReleaseRuntime.d.ts.map +1 -1
- package/out/runtime/useReleaseRuntime.js +31 -26
- package/out/runtime/useReleaseRuntime.js.map +1 -1
- package/out/runtime.d.ts +9 -0
- package/out/runtime.d.ts.map +1 -0
- package/out/runtime.js +8 -0
- package/out/runtime.js.map +1 -0
- package/out/search/SearchModal.d.ts +33 -0
- package/out/search/SearchModal.d.ts.map +1 -0
- package/out/search/SearchModal.js +187 -0
- package/out/search/SearchModal.js.map +1 -0
- package/out/search/types.d.ts +64 -0
- package/out/search/types.d.ts.map +1 -0
- package/out/search/types.js +13 -0
- package/out/search/types.js.map +1 -0
- package/out/search.d.ts +11 -0
- package/out/search.d.ts.map +1 -0
- package/out/search.js +9 -0
- package/out/search.js.map +1 -0
- package/package.json +132 -133
- package/styles.css +1340 -202
- package/tokens/index.css +26 -7
- package/tokens/tokens.json +121 -6
- package/tokens/tokens.resolver.json +1 -1
- package/lib/map/index.ts +0 -76
- package/out/map/index.d.ts +0 -52
- package/out/map/index.d.ts.map +0 -1
- package/out/map/index.js +0 -36
- package/out/map/index.js.map +0 -1
- package/styleframe.config.ts +0 -321
- 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
|
+
}
|
package/lib/map/MapCanvas.tsx
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
12
|
-
* + DOM at import) at module scope, so it is reachable
|
|
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
|
|
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
|
|
35
|
-
* `maplibreLogo`).
|
|
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.
|
|
49
|
+
* Uncontrolled initial camera.
|
|
50
|
+
*
|
|
51
|
+
* Use this or `viewState`, not both.
|
|
48
52
|
*/
|
|
49
53
|
initialViewState?: MapProps["initialViewState"]
|
|
50
54
|
/**
|
|
51
|
-
* Controlled camera.
|
|
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
|
|
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
|
-
*
|
|
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.
|
|
88
|
-
*
|
|
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.
|
|
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
|
|