@mailwoman/react 10.0.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 +7 -7
- 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 -17
- package/lib/map/AppIdentity.tsx +85 -0
- package/lib/map/BackendControl.tsx +9 -10
- package/lib/map/CompareToggle.tsx +8 -31
- package/lib/map/DebugInfo.tsx +88 -0
- package/lib/map/Geocoder.tsx +60 -46
- package/lib/map/GeocoderControls.tsx +430 -134
- package/lib/map/GraticuleLayer.tsx +54 -0
- package/lib/map/MapCanvas.tsx +23 -12
- package/lib/map/MapChipRow.tsx +4 -8
- package/lib/map/MapCompass.tsx +15 -24
- package/lib/map/MapControlStack.tsx +8 -9
- package/lib/map/MapFooter.tsx +2 -16
- package/lib/map/MapProgressBar.tsx +10 -13
- package/lib/map/MapSearchBar.tsx +31 -17
- package/lib/map/MapSheet.tsx +24 -15
- 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 +58 -104
- package/lib/map/useCompareState.ts +5 -4
- package/lib/map/useGeocode.ts +7 -5
- package/lib/map/useMapBearing.ts +13 -13
- package/lib/map/useMapLabelPick.ts +21 -30
- 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 +16 -13
- 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/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 +5 -5
- package/out/common/About.d.ts.map +1 -1
- package/out/common/About.js +1 -1
- 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 -16
- package/out/index.d.ts.map +1 -1
- package/out/index.js +21 -15
- 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 +1 -1
- 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 +4 -1
- 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 +41 -30
- package/out/map/Geocoder.d.ts.map +1 -1
- package/out/map/Geocoder.js +20 -36
- package/out/map/Geocoder.js.map +1 -1
- package/out/map/GeocoderControls.d.ts +14 -42
- package/out/map/GeocoderControls.d.ts.map +1 -1
- package/out/map/GeocoderControls.js +250 -38
- 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 +4 -8
- package/out/map/MapChipRow.d.ts.map +1 -1
- package/out/map/MapChipRow.js.map +1 -1
- package/out/map/MapCompass.d.ts +11 -20
- package/out/map/MapCompass.d.ts.map +1 -1
- package/out/map/MapCompass.js +5 -1
- package/out/map/MapCompass.js.map +1 -1
- package/out/map/MapControlStack.d.ts +8 -9
- package/out/map/MapControlStack.d.ts.map +1 -1
- package/out/map/MapControlStack.js +4 -1
- package/out/map/MapControlStack.js.map +1 -1
- package/out/map/MapFooter.d.ts +2 -15
- package/out/map/MapFooter.d.ts.map +1 -1
- package/out/map/MapFooter.js +0 -14
- package/out/map/MapFooter.js.map +1 -1
- package/out/map/MapProgressBar.d.ts +10 -13
- package/out/map/MapProgressBar.d.ts.map +1 -1
- package/out/map/MapProgressBar.js.map +1 -1
- package/out/map/MapSearchBar.d.ts +16 -17
- package/out/map/MapSearchBar.d.ts.map +1 -1
- package/out/map/MapSearchBar.js +6 -0
- package/out/map/MapSearchBar.js.map +1 -1
- package/out/map/MapSheet.d.ts +15 -12
- package/out/map/MapSheet.d.ts.map +1 -1
- package/out/map/MapSheet.js +8 -12
- package/out/map/MapSheet.js.map +1 -1
- package/out/map/OverlayLayers.d.ts +1 -1
- package/out/map/OverlayLayers.js +1 -1
- 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 +1 -1
- 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.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.js +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 +58 -102
- 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 +11 -11
- package/out/map/useMapBearing.d.ts.map +1 -1
- package/out/map/useMapBearing.js +6 -11
- package/out/map/useMapBearing.js.map +1 -1
- package/out/map/useMapLabelPick.d.ts +4 -14
- package/out/map/useMapLabelPick.d.ts.map +1 -1
- package/out/map/useMapLabelPick.js +21 -30
- package/out/map/useMapLabelPick.js.map +1 -1
- 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 +1 -1
- 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/QueryForm.d.ts +5 -2
- package/out/pipeline/QueryForm.d.ts.map +1 -1
- package/out/pipeline/QueryForm.js.map +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/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 +14 -9
- package/out/pipeline/useParsePipeline.d.ts.map +1 -1
- package/out/pipeline/useParsePipeline.js +7 -8
- 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.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 +9 -8
- 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 +2 -1
- 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/index.d.ts → runtime.d.ts} +1 -1
- package/out/runtime.d.ts.map +1 -0
- package/out/{runtime/index.js → runtime.js} +1 -1
- 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 +60 -142
- package/styles.css +482 -266
- package/tokens/index.css +24 -6
- package/tokens/tokens.json +114 -6
- package/lib/common/index.ts +0 -22
- package/lib/map/index.ts +0 -95
- package/lib/pipeline/index.ts +0 -36
- package/lib/poi/index.ts +0 -39
- package/out/common/index.d.ts +0 -22
- package/out/common/index.d.ts.map +0 -1
- package/out/common/index.js +0 -15
- package/out/common/index.js.map +0 -1
- package/out/map/index.d.ts +0 -69
- package/out/map/index.d.ts.map +0 -1
- package/out/map/index.js +0 -46
- package/out/map/index.js.map +0 -1
- package/out/pipeline/index.d.ts +0 -33
- package/out/pipeline/index.d.ts.map +0 -1
- package/out/pipeline/index.js +0 -21
- package/out/pipeline/index.js.map +0 -1
- package/out/poi/index.d.ts +0 -23
- package/out/poi/index.d.ts.map +0 -1
- package/out/poi/index.js +0 -15
- package/out/poi/index.js.map +0 -1
- package/out/runtime/index.d.ts.map +0 -1
- package/out/runtime/index.js.map +0 -1
- package/styleframe.config.ts +0 -329
- package/vitest.node.config.ts +0 -20
- /package/lib/{runtime/index.ts → runtime.ts} +0 -0
|
@@ -3,43 +3,31 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* A label on a map looks like a link, so a visitor clicks it. Without this the click reaches the map's pan handler
|
|
9
|
-
* and nothing happens, which reads as the label being decoration.
|
|
10
|
-
*
|
|
11
|
-
* It reads the label's OWN name from the rendered feature rather than reverse-geocoding the click point: the name
|
|
12
|
-
* is what the visitor pointed at, and a lookup by coordinate answers whatever is nearest instead, which on a dense
|
|
13
|
-
* basemap is regularly not the thing under the cursor.
|
|
14
|
-
*
|
|
15
|
-
* THE QUERY IS SCOPED, AND THE HOVER QUERY IS THROTTLED. `queryRenderedFeatures` with no `layers` walks the whole
|
|
16
|
-
* style: measured at 64.3 ms per call returning 4,819 features over the 79-layer basemap at zoom 14 in Manhattan,
|
|
17
|
-
* against 4.9 ms and 44 features scoped to that style's 11 label layers. At one call per pointer move the unscoped
|
|
18
|
-
* form is the map's whole frame budget, so the layer list is resolved once per style and the hover query runs at
|
|
19
|
-
* most once per animation frame. The click query is not throttled — there is one of those per click.
|
|
6
|
+
* A place-label click searches for its own name. The hook resolves the layer list once per style and throttles the hover query to one per animation frame.
|
|
20
7
|
*/
|
|
21
8
|
|
|
22
9
|
import { useEffect, useEffectEvent } from "react"
|
|
23
10
|
import type { MapInstance, MapLayerMouseEvent } from "react-map-gl/maplibre"
|
|
24
11
|
|
|
25
12
|
/**
|
|
26
|
-
* The
|
|
27
|
-
*
|
|
13
|
+
* The protomaps label layers (`<theme>_label`, `places_*`); other layers are geometry
|
|
14
|
+
* rather than readable place names.
|
|
28
15
|
*/
|
|
29
16
|
const LABEL_LAYER = /_label|^places_/
|
|
30
17
|
|
|
31
18
|
/**
|
|
32
|
-
* Properties a label
|
|
33
|
-
*
|
|
19
|
+
* Properties that may contain a label's text, in trust order.
|
|
20
|
+
*
|
|
21
|
+
* `name` comes from Protomaps.
|
|
22
|
+
* Localized variants appear in script-specific styles.
|
|
34
23
|
*/
|
|
35
24
|
const NAME_KEYS = ["name", "name:en", "name_en"] as const
|
|
36
25
|
|
|
37
26
|
/**
|
|
38
|
-
* The style's label
|
|
27
|
+
* The style's label layer ids.
|
|
39
28
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* between "this style has no labels" and "query everything" is the 64 ms this hook exists to avoid.
|
|
29
|
+
* An empty result must stay unqueried, since an empty `layers` option is not the
|
|
30
|
+
* same as an absent one in `queryRenderedFeatures`.
|
|
43
31
|
*/
|
|
44
32
|
function labelLayerIDs(map: MapInstance): string[] {
|
|
45
33
|
const layers = map.getStyle()?.layers ?? []
|
|
@@ -50,8 +38,7 @@ function labelLayerIDs(map: MapInstance): string[] {
|
|
|
50
38
|
function labelNameAt(map: MapInstance, point: MapLayerMouseEvent["point"], layers: string[]): string | null {
|
|
51
39
|
if (!layers.length) return null
|
|
52
40
|
|
|
53
|
-
// `queryRenderedFeatures` answers
|
|
54
|
-
// and therefore the one a click landed on.
|
|
41
|
+
// `queryRenderedFeatures` answers topmost-first, so the first feature with a label is the one the click landed on.
|
|
55
42
|
for (const feature of map.queryRenderedFeatures(point, { layers })) {
|
|
56
43
|
for (const key of NAME_KEYS) {
|
|
57
44
|
const value = feature.properties?.[key]
|
|
@@ -63,17 +50,20 @@ function labelNameAt(map: MapInstance, point: MapLayerMouseEvent["point"], layer
|
|
|
63
50
|
return null
|
|
64
51
|
}
|
|
65
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Routes clicks on map labels to `onPick` with the clicked label's own name.
|
|
55
|
+
*/
|
|
66
56
|
export function useMapLabelPick(map: MapInstance | null, onPick: (name: string) => void): void {
|
|
67
|
-
// The subscription depends on the
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
// handler that always sees the latest props without being a reactive dependency.
|
|
57
|
+
// The subscription depends only on the map.
|
|
58
|
+
// `onPick` changes on every render.
|
|
59
|
+
// `useEffectEvent` reads the latest callback without adding it as a reactive dependency.
|
|
71
60
|
const pick = useEffectEvent((name: string) => onPick(name))
|
|
72
61
|
|
|
73
62
|
useEffect(() => {
|
|
74
63
|
if (!map) return
|
|
75
64
|
|
|
76
|
-
// Recomputed when the style swaps
|
|
65
|
+
// Recomputed when the style swaps.
|
|
66
|
+
// Pointer moves read the cached list.
|
|
77
67
|
let layers = labelLayerIDs(map)
|
|
78
68
|
|
|
79
69
|
const readLayers = () => {
|
|
@@ -98,7 +88,8 @@ export function useMapLabelPick(map: MapInstance | null, onPick: (name: string)
|
|
|
98
88
|
|
|
99
89
|
const canvas = map.getCanvas()
|
|
100
90
|
|
|
101
|
-
// The drag cursor belongs to the pan gesture
|
|
91
|
+
// The drag cursor belongs to the pan gesture.
|
|
92
|
+
// An override mid-drag would fight the map for the pointer.
|
|
102
93
|
if (canvas.style.cursor === "grabbing") return
|
|
103
94
|
|
|
104
95
|
canvas.style.cursor = labelNameAt(map, event.point, layers) ? "pointer" : ""
|
|
@@ -3,11 +3,7 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* recomputes the render spec only when the resolved place changes, so the declarative overlays render a
|
|
8
|
-
* stable spec object. The MATH lives in `place-render.ts` (pure, node-tested); this file adds nothing
|
|
9
|
-
* but memoization, so it stays trivially correct. A `null` place (no result yet, or a result with no
|
|
10
|
-
* candidate) yields `null` — the overlays render nothing.
|
|
6
|
+
* The thin React memo wrapper over the pure {@link computeMapPlaceRenderSpec}; a `null` place yields `null`.
|
|
11
7
|
*/
|
|
12
8
|
|
|
13
9
|
import { useMemo } from "react"
|
|
@@ -16,7 +12,7 @@ import { computeMapPlaceRenderSpec } from "#map/place-render"
|
|
|
16
12
|
import type { MapPlaceRenderSpec, ResolvedMapPlace } from "#map/place-render"
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
|
-
* Memoize the render spec for a resolved place; `null` in → `null` out (
|
|
15
|
+
* Memoize the render spec for a resolved place; `null` in → `null` out (no geometry to draw).
|
|
20
16
|
*/
|
|
21
17
|
export function useMapPlaceRender(place: ResolvedMapPlace | null | undefined): MapPlaceRenderSpec | null {
|
|
22
18
|
return useMemo(() => (place ? computeMapPlaceRenderSpec(place) : null), [place])
|
|
@@ -2,17 +2,6 @@
|
|
|
2
2
|
* @copyright Sister Software
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
|
-
*
|
|
6
|
-
* `usePlaceAutocomplete` — the headless "Did you mean" combobox lifted from the demo god component
|
|
7
|
-
* (`_app.tsx:820-900`). It walks the host-supplied `autocomplete` over the locality segment the visitor
|
|
8
|
-
* is typing (the text after the last comma), owns the suggestion list + keyboard-highlighted active
|
|
9
|
-
* descendant, and rewrites the input on pick (replacing just that segment). No FST, no fetch of its own
|
|
10
|
-
* — the `autocomplete` fetcher is INJECTED (the package never imports `@mailwoman/resolver-wof-sqlite`),
|
|
11
|
-
* so this stays node-safe and testable with a synchronous fake.
|
|
12
|
-
*
|
|
13
|
-
* Returns the `<input>` aria/combobox props to spread onto the reused {@link QueryForm} input plus an
|
|
14
|
-
* `onInputKeyDown` (↑/↓ move the highlight, Enter accepts it AND suppresses submit, Esc dismisses), and
|
|
15
|
-
* the presentational {@link PlaceAutocomplete} listbox renders `suggestions` / `activeIndex`.
|
|
16
5
|
*/
|
|
17
6
|
|
|
18
7
|
import { type KeyboardEvent, useCallback, useEffect, useState } from "react"
|
|
@@ -20,31 +9,41 @@ import { type KeyboardEvent, useCallback, useEffect, useState } from "react"
|
|
|
20
9
|
import { useDebouncedValue } from "#common/useDebouncedValue"
|
|
21
10
|
import type { Suggestion } from "#map/types"
|
|
22
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Options for {@linkcode usePlaceAutocomplete}.
|
|
14
|
+
*/
|
|
23
15
|
export interface UsePlaceAutocompleteOptions {
|
|
24
16
|
/**
|
|
25
|
-
* The
|
|
17
|
+
* The controlled input text.
|
|
18
|
+
* The segment after the last comma is the query.
|
|
26
19
|
*/
|
|
27
20
|
text: string
|
|
21
|
+
|
|
28
22
|
/**
|
|
29
|
-
*
|
|
23
|
+
* Sets the input text.
|
|
24
|
+
* A pick calls it with the last segment replaced.
|
|
30
25
|
*/
|
|
31
26
|
setText: (text: string) => void
|
|
27
|
+
|
|
32
28
|
/**
|
|
33
|
-
*
|
|
29
|
+
* Fetches suggestions for a query.
|
|
30
|
+
* Without it the hook never suggests anything.
|
|
34
31
|
*/
|
|
35
32
|
autocomplete?: (query: string) => Promise<Suggestion[]>
|
|
33
|
+
|
|
36
34
|
/**
|
|
37
|
-
*
|
|
35
|
+
* @default 2
|
|
38
36
|
*/
|
|
39
37
|
minChars?: number
|
|
38
|
+
|
|
40
39
|
/**
|
|
41
|
-
*
|
|
40
|
+
* @default 150
|
|
42
41
|
*/
|
|
43
42
|
debounceMs?: number
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
/**
|
|
47
|
-
* The combobox
|
|
46
|
+
* The combobox ARIA props to spread onto the input.
|
|
48
47
|
*/
|
|
49
48
|
export interface AutocompleteInputProps {
|
|
50
49
|
role: "combobox"
|
|
@@ -55,68 +54,73 @@ export interface AutocompleteInputProps {
|
|
|
55
54
|
autoComplete: "off"
|
|
56
55
|
}
|
|
57
56
|
|
|
57
|
+
/**
|
|
58
|
+
* The state and handlers that {@linkcode usePlaceAutocomplete} returns.
|
|
59
|
+
*/
|
|
58
60
|
export interface UsePlaceAutocomplete {
|
|
59
61
|
/**
|
|
60
|
-
* The current suggestions
|
|
62
|
+
* The current suggestions, empty when none match or the visitor dismissed the list.
|
|
61
63
|
*/
|
|
62
64
|
suggestions: Suggestion[]
|
|
65
|
+
|
|
63
66
|
/**
|
|
64
|
-
* The
|
|
67
|
+
* The index highlighted by the keyboard, or `-1` for none.
|
|
65
68
|
*/
|
|
66
69
|
activeIndex: number
|
|
70
|
+
|
|
67
71
|
/**
|
|
68
|
-
*
|
|
72
|
+
* Sets the highlighted index, such as when the pointer enters an option.
|
|
69
73
|
*/
|
|
70
74
|
setActiveIndex: (index: number) => void
|
|
75
|
+
|
|
71
76
|
/**
|
|
72
|
-
*
|
|
77
|
+
* Handles input keys: arrows move the highlight, Enter picks instead of submitting and Escape dismisses.
|
|
73
78
|
*/
|
|
74
79
|
onInputKeyDown: (event: KeyboardEvent<HTMLInputElement>) => void
|
|
80
|
+
|
|
75
81
|
/**
|
|
76
|
-
*
|
|
82
|
+
* Replaces the text after the last comma with the suggestion and closes the list.
|
|
77
83
|
*/
|
|
78
84
|
pick: (value: string) => void
|
|
85
|
+
|
|
79
86
|
/**
|
|
80
|
-
*
|
|
87
|
+
* Closes the list without picking until the query changes.
|
|
81
88
|
*/
|
|
82
89
|
dismiss: () => void
|
|
90
|
+
|
|
83
91
|
/**
|
|
84
|
-
*
|
|
92
|
+
* The combobox ARIA props to spread onto the input.
|
|
85
93
|
*/
|
|
86
94
|
inputProps: AutocompleteInputProps
|
|
95
|
+
|
|
87
96
|
/**
|
|
88
|
-
* The listbox element
|
|
97
|
+
* The listbox element ID.
|
|
98
|
+
* It matches `inputProps["aria-controls"]`.
|
|
89
99
|
*/
|
|
90
100
|
listboxID: string
|
|
91
|
-
|
|
92
|
-
* Build the option element id for suggestion `index`.
|
|
93
|
-
*/
|
|
101
|
+
|
|
94
102
|
optionID: (index: number) => string
|
|
95
103
|
}
|
|
96
104
|
|
|
97
105
|
const LISTBOX_ID = "mw-demo-suggest-list"
|
|
98
106
|
|
|
99
|
-
/**
|
|
100
|
-
* Stable empty list for the derived no-suggestions state — a fresh `[]` per render would churn the identity every
|
|
101
|
-
* consumer and every useCallback dependency list sees.
|
|
102
|
-
*/
|
|
103
107
|
const NO_SUGGESTIONS: Suggestion[] = []
|
|
104
108
|
const optionID = (index: number) => `mw-demo-suggest-${index}`
|
|
105
109
|
|
|
106
|
-
/**
|
|
107
|
-
* Extract the locality segment being typed — the text after the last comma, trimmed.
|
|
108
|
-
*/
|
|
109
110
|
function localitySegment(text: string): string {
|
|
110
111
|
return (text.includes(",") ? text.slice(text.lastIndexOf(",") + 1) : text).trim()
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
/**
|
|
114
|
-
* Replace the locality segment (after the last comma) with `name`, preserving the address prefix.
|
|
115
|
-
*/
|
|
116
114
|
function replaceSegment(current: string, name: string): string {
|
|
117
115
|
return current.includes(",") ? `${current.slice(0, current.lastIndexOf(",") + 1)} ${name}` : name
|
|
118
116
|
}
|
|
119
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Fetches debounced place suggestions for the text after the input's last comma.
|
|
120
|
+
*
|
|
121
|
+
* A pick replaces only that segment.
|
|
122
|
+
* A digit-leading segment never reaches `autocomplete`.
|
|
123
|
+
*/
|
|
120
124
|
export function usePlaceAutocomplete({
|
|
121
125
|
text,
|
|
122
126
|
setText,
|
|
@@ -124,18 +128,8 @@ export function usePlaceAutocomplete({
|
|
|
124
128
|
minChars = 2,
|
|
125
129
|
debounceMs = 150,
|
|
126
130
|
}: UsePlaceAutocompleteOptions): UsePlaceAutocomplete {
|
|
127
|
-
/**
|
|
128
|
-
* The last COMPLETED fetch, keyed by the query that produced it. Visibility is DERIVED from this during render rather
|
|
129
|
-
* than pushed through sync setStates in the effect — the effect's only job is the async fetch, so every state write
|
|
130
|
-
* in it happens after an await and the react(set-state-in-effect) rule is satisfied by structure, not by exception.
|
|
131
|
-
*/
|
|
132
131
|
const [fetched, setFetched] = useState<{ query: string; suggestions: Suggestion[] } | null>(null)
|
|
133
|
-
|
|
134
|
-
* The query whose list was dismissed or picked. Suppression as DATA, replacing the one-shot ref flag: a pick stores
|
|
135
|
-
* the query the rewritten text will produce, so the post-pick fetch is skipped and the place just chosen is never
|
|
136
|
-
* re-suggested. One divergence from the ref version, accepted and small: after Esc, retyping the exact same string
|
|
137
|
-
* keeps the list hidden until the query changes.
|
|
138
|
-
*/
|
|
132
|
+
|
|
139
133
|
const [dismissed, setDismissed] = useState<string | null>(null)
|
|
140
134
|
const [activeIndex, setActiveIndex] = useState(-1)
|
|
141
135
|
|
|
@@ -178,7 +172,7 @@ export function usePlaceAutocomplete({
|
|
|
178
172
|
const next = replaceSegment(text, value)
|
|
179
173
|
|
|
180
174
|
setText(next)
|
|
181
|
-
|
|
175
|
+
|
|
182
176
|
setDismissed(localitySegment(next))
|
|
183
177
|
setFetched(null)
|
|
184
178
|
setActiveIndex(-1)
|
package/lib/map.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright Sister Software
|
|
3
|
+
* @license AGPL-3.0
|
|
4
|
+
* @author Teffen Ellis, et al.
|
|
5
|
+
*
|
|
6
|
+
* `@mailwoman/react/map` — the geocoder map surface, kept behind its own subpath so `maplibre-gl`
|
|
7
|
+
* / `react-map-gl` (WebGL + DOM at import) never enter the package-root graph. This subpath import
|
|
8
|
+
* pulls the map deps. importing `@mailwoman/react` (root) does not. Consumers who only want the
|
|
9
|
+
* parse/POI explorers never pay for maplibre.
|
|
10
|
+
*
|
|
11
|
+
* The host must supply `maplibre-gl` + `react-map-gl` (peer deps) and import
|
|
12
|
+
* `maplibre-gl/dist/maplibre-gl.css` + `@mailwoman/react/styles.css` itself.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export { MapCanvas } from "./map/MapCanvas.tsx"
|
|
16
|
+
export type { MapCanvasExtraProps, MapCanvasProps, MapCanvasStyle } from "./map/MapCanvas.tsx"
|
|
17
|
+
|
|
18
|
+
// ── Map chrome (node-safe presentation. The host supplies the input and reads its own map) ──
|
|
19
|
+
export { MapChipRow } from "./map/MapChipRow.tsx"
|
|
20
|
+
export type { MapChip, MapChipRowProps } from "./map/MapChipRow.tsx"
|
|
21
|
+
export { MapCompass } from "./map/MapCompass.tsx"
|
|
22
|
+
export type { MapCompassProps } from "./map/MapCompass.tsx"
|
|
23
|
+
export { useMapBearing } from "./map/useMapBearing.ts"
|
|
24
|
+
export type { UseMapBearing } from "./map/useMapBearing.ts"
|
|
25
|
+
export { useMapLabelPick } from "./map/useMapLabelPick.ts"
|
|
26
|
+
export { MapControlButton, MapControlGroup, MapControlStack } from "./map/MapControlStack.tsx"
|
|
27
|
+
export type { MapControlButtonProps, MapControlGroupProps, MapControlStackProps } from "./map/MapControlStack.tsx"
|
|
28
|
+
export { MapFooter } from "./map/MapFooter.tsx"
|
|
29
|
+
export type { MapFooterProps } from "./map/MapFooter.tsx"
|
|
30
|
+
export { MapProgressBar } from "./map/MapProgressBar.tsx"
|
|
31
|
+
export type { MapProgressBarProps } from "./map/MapProgressBar.tsx"
|
|
32
|
+
export { MapSheet, SheetClose } from "./map/MapSheet.tsx"
|
|
33
|
+
export type { MapSheetProps } from "./map/MapSheet.tsx"
|
|
34
|
+
export { MapSearchBar, SearchGlyph } from "./map/MapSearchBar.tsx"
|
|
35
|
+
export type { MapSearchBarProps } from "./map/MapSearchBar.tsx"
|
|
36
|
+
|
|
37
|
+
export type {
|
|
38
|
+
CompareContext,
|
|
39
|
+
GeocoderPanels,
|
|
40
|
+
GeocoderRuntime,
|
|
41
|
+
InferenceBackend,
|
|
42
|
+
LngLatTuple,
|
|
43
|
+
MapBias,
|
|
44
|
+
OverlaySpec,
|
|
45
|
+
ResultContext,
|
|
46
|
+
Suggestion,
|
|
47
|
+
VersionOption,
|
|
48
|
+
} from "#map/types"
|
|
49
|
+
|
|
50
|
+
// ── Pure geometry + render spec (node-safe. No react-map-gl at runtime) ──────
|
|
51
|
+
export { approxCircleGeometry, bboxToBounds, geomBounds, radiusCircleGeometry } from "#map/geometry"
|
|
52
|
+
export type { BoundsTuple, PlaceGeometry } from "#map/geometry"
|
|
53
|
+
export { cameraToViewState, computeMapPlaceRenderSpec } from "#map/place-render"
|
|
54
|
+
export type { LngLat, MapCameraTarget, MapPlaceRenderSpec, PlaceTier, ResolvedMapPlace } from "#map/place-render"
|
|
55
|
+
export { useMapPlaceRender } from "#map/useMapPlaceRender"
|
|
56
|
+
|
|
57
|
+
// ── Declarative overlays (react-map-gl `<Marker>`/`<Source>`/`<Layer>`) ──────
|
|
58
|
+
export { OverlayLayers } from "./map/OverlayLayers.tsx"
|
|
59
|
+
export type { OverlayLayersProps } from "./map/OverlayLayers.tsx"
|
|
60
|
+
export { PlaceMarker } from "./map/PlaceMarker.tsx"
|
|
61
|
+
export type { PlaceMarkerProps } from "./map/PlaceMarker.tsx"
|
|
62
|
+
export { ResolvedPlaceLayers } from "./map/ResolvedPlaceLayers.tsx"
|
|
63
|
+
export type { ResolvedPlaceLayersProps } from "./map/ResolvedPlaceLayers.tsx"
|
|
64
|
+
export { ResultCamera } from "./map/ResultCamera.tsx"
|
|
65
|
+
export type { ResultCameraProps } from "./map/ResultCamera.tsx"
|
|
66
|
+
export { ResultOverlay } from "./map/ResultOverlay.tsx"
|
|
67
|
+
export type { ResultOverlayProps } from "./map/ResultOverlay.tsx"
|
|
68
|
+
|
|
69
|
+
// ── Geocoder controls + the composed geocoder ────────────────────────────────
|
|
70
|
+
export { BackendControl } from "./map/BackendControl.tsx"
|
|
71
|
+
export type { BackendControlProps } from "./map/BackendControl.tsx"
|
|
72
|
+
export { CompareToggle } from "./map/CompareToggle.tsx"
|
|
73
|
+
export type { CompareToggleProps } from "./map/CompareToggle.tsx"
|
|
74
|
+
export { Geocoder } from "./map/Geocoder.tsx"
|
|
75
|
+
export type { GeocoderProps } from "./map/Geocoder.tsx"
|
|
76
|
+
export { GeocoderControls } from "./map/GeocoderControls.tsx"
|
|
77
|
+
export type { GeocoderControlsProps } from "./map/GeocoderControls.tsx"
|
|
78
|
+
export { PlaceAutocomplete } from "./map/PlaceAutocomplete.tsx"
|
|
79
|
+
export type { PlaceAutocompleteProps } from "./map/PlaceAutocomplete.tsx"
|
|
80
|
+
export { ResultPanel } from "./map/ResultPanel.tsx"
|
|
81
|
+
export type { ResultPanelProps } from "./map/ResultPanel.tsx"
|
|
82
|
+
export { useCompareState } from "#map/useCompareState"
|
|
83
|
+
export type { UseCompareState } from "#map/useCompareState"
|
|
84
|
+
export { useGeocode } from "#map/useGeocode"
|
|
85
|
+
export type { UseGeocode, UseGeocodeOptions } from "#map/useGeocode"
|
|
86
|
+
export { usePlaceAutocomplete } from "#map/usePlaceAutocomplete"
|
|
87
|
+
|
|
88
|
+
export type {
|
|
89
|
+
AutocompleteInputProps,
|
|
90
|
+
UsePlaceAutocomplete,
|
|
91
|
+
UsePlaceAutocompleteOptions,
|
|
92
|
+
} from "#map/usePlaceAutocomplete"
|
|
93
|
+
|
|
94
|
+
export { VersionPicker } from "./map/VersionPicker.tsx"
|
|
95
|
+
export type { VersionPickerProps } from "./map/VersionPicker.tsx"
|
|
@@ -3,8 +3,18 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* `CandidatePicker` — the ranked list of alternate gazetteer candidates
|
|
7
|
-
* Presentational
|
|
6
|
+
* `CandidatePicker` — the ranked list of alternate gazetteer candidates. clicking one selects it.
|
|
7
|
+
* Presentational. the selected index is owned by the caller (the pipeline hook).
|
|
8
|
+
*
|
|
9
|
+
* The row shows the rank rather than the score. `ResolvedPlaceView.score` is implementation-defined. Its type specifies
|
|
10
|
+
* "callers should treat as ordinal" — and the backends do not agree on a scale: the FTS regime is a negated bm25
|
|
11
|
+
* plus additive tiers, while the candidate regime is exactly `log10(population + 1)`. Both regimes put
|
|
12
|
+
* "New York · 6.95" (log10 of 8.9M) directly under "350 5th Ave · 1.00" from a bounded blend. That reads as one
|
|
13
|
+
* number beating another by a factor of seven rather than as two numbers that never shared a scale.
|
|
14
|
+
* `docs/records/reviews/2026-08-04-resolver-score-abstention.md` measures this in full.
|
|
15
|
+
*
|
|
16
|
+
* The raw value stays on the button's `title`, at full precision, because it is exactly the right thing to have
|
|
17
|
+
* when you are debugging a ranking and exactly the wrong thing to put in a visitor's eyeline.
|
|
8
18
|
*/
|
|
9
19
|
|
|
10
20
|
import type { ResolvedPlaceView } from "@mailwoman/core/pipeline/client-result"
|
|
@@ -33,9 +43,7 @@ export function CandidatePicker({ candidates, selectedIndex, onSelect }: Candida
|
|
|
33
43
|
>
|
|
34
44
|
<span className="mw-candidates__rank">#{i + 1}</span>
|
|
35
45
|
<span className="mw-candidates__name">{candidate.name}</span>
|
|
36
|
-
<span className="mw-candidates__meta">
|
|
37
|
-
{candidate.placetype} · {candidate.score.toFixed(2)}
|
|
38
|
-
</span>
|
|
46
|
+
<span className="mw-candidates__meta">{candidate.placetype}</span>
|
|
39
47
|
</button>
|
|
40
48
|
</li>
|
|
41
49
|
))}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
* `ConfidenceCell` — a compact confidence bar + value for the component table. The fill
|
|
6
|
+
* `ConfidenceCell` — a compact confidence bar + value for the component table. The fill color follows the shared
|
|
7
7
|
* confidence tiers. Presentational.
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -3,17 +3,23 @@
|
|
|
3
3
|
* @license AGPL-3.0
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Explains a query that resolved to no place and suggests which component, such as a city or a
|
|
7
|
+
* state, the visitor could add.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { ParsedComponent } from "@mailwoman/core/pipeline/client-result"
|
|
11
11
|
import type { ReactNode } from "react"
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Props for {@link FailureDiagnostic}.
|
|
15
|
+
*/
|
|
13
16
|
export interface FailureDiagnosticProps {
|
|
14
17
|
nodes: ParsedComponent[]
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Renders hints based on which components the parser found.
|
|
22
|
+
*/
|
|
17
23
|
export function FailureDiagnostic({ nodes }: FailureDiagnosticProps): ReactNode {
|
|
18
24
|
const hasLocality = nodes.some((n) => n.tag === "locality")
|
|
19
25
|
const hasPostcode = nodes.some((n) => n.tag === "postcode")
|
|
@@ -19,11 +19,14 @@ export interface QueryFormProps {
|
|
|
19
19
|
busy?: boolean
|
|
20
20
|
placeholder?: string
|
|
21
21
|
/**
|
|
22
|
-
* Keydown on the input — the hook a combobox (the demo's place-autocomplete)
|
|
22
|
+
* Keydown on the input — the hook a combobox (the demo's place-autocomplete)
|
|
23
|
+
* needs for arrow/Enter/Esc nav.
|
|
23
24
|
*/
|
|
24
25
|
onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void
|
|
25
26
|
/**
|
|
26
|
-
* Override for extra `<input>` attributes (aria-combobox props, `autoComplete`, …).
|
|
27
|
+
* Override for extra `<input>` attributes (aria-combobox props, `autoComplete`, …).
|
|
28
|
+
*
|
|
29
|
+
* Merged last.
|
|
27
30
|
*/
|
|
28
31
|
inputProps?: InputHTMLAttributes<HTMLInputElement>
|
|
29
32
|
}
|
|
@@ -25,7 +25,7 @@ export function ResolvedPlace({ place, dualRoles }: ResolvedPlaceProps): ReactNo
|
|
|
25
25
|
<dt>placetype</dt>
|
|
26
26
|
<dd>{place.placetype}</dd>
|
|
27
27
|
{place.tier ? (
|
|
28
|
-
// A street-tier answer is not a gazetteer place: it
|
|
28
|
+
// A street-tier answer is not a gazetteer place: it has a precision instead of an id.
|
|
29
29
|
<>
|
|
30
30
|
<dt>precision</dt>
|
|
31
31
|
<dd>
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
6
|
* `SpanHighlight` — a displaCy-style span ribbon over the raw input: each tagged span is a column with the text on
|
|
7
|
-
* top, tinted by its confidence tier,
|
|
8
|
-
* plain text, so a dropped span is visible as a literal break in the
|
|
7
|
+
* top, tinted by its confidence tier, with the tag beneath. Gaps (delimiters, unparsed characters) fall through as
|
|
8
|
+
* plain text, so a dropped span is visible as a literal break in the color.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { ParsedComponent } from "@mailwoman/core/pipeline/client-result"
|
|
@@ -20,7 +20,8 @@ export interface SpanHighlightProps {
|
|
|
20
20
|
*/
|
|
21
21
|
input: string
|
|
22
22
|
/**
|
|
23
|
-
* Flattened parse nodes
|
|
23
|
+
* Flattened parse nodes.
|
|
24
|
+
* Only those with numeric `start`/`end` are rendered.
|
|
24
25
|
*/
|
|
25
26
|
nodes: ParsedComponent[]
|
|
26
27
|
}
|
|
@@ -47,8 +48,9 @@ export function SpanHighlight({ input, nodes }: SpanHighlightProps): ReactNode {
|
|
|
47
48
|
|
|
48
49
|
if (!spans.length) return null
|
|
49
50
|
|
|
50
|
-
// Per-character owner: the most specific (shortest) span covering it,
|
|
51
|
-
//
|
|
51
|
+
// Per-character owner: the most specific (shortest) span covering it,
|
|
52
|
+
// the same rule the word-level panels apply per word.
|
|
53
|
+
// The leaf always wins whatever nesting the tree hands over, so every character renders once.
|
|
52
54
|
const characters = Array.from({ length: input.length }, (_, i) => ({ start: i, end: i + 1 }))
|
|
53
55
|
const owner = shortestSpanOwners(characters, spans)
|
|
54
56
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
6
|
* `TimingPanel` — the per-stage timing breakdown: a stacked bar sized by each stage's wall-clock, plus a legend.
|
|
7
|
-
* Stage
|
|
7
|
+
* Stage colors are distinct hues, never the confidence red/amber/green, because they encode the pipeline stage and
|
|
8
8
|
* not quality.
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
6
|
* `TreeView` — the parse's containment tree as nested lists with a guide rail per level. The tag tint follows the
|
|
7
|
-
* confidence tier
|
|
7
|
+
* confidence tier. the value and score render in monospace.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { ReactNode } from "react"
|
package/lib/pipeline/copy.ts
CHANGED
|
@@ -4,34 +4,31 @@
|
|
|
4
4
|
* @author Teffen Ellis, et al.
|
|
5
5
|
*
|
|
6
6
|
* Builds the "Copy JSON" payload for a parse result — the components + the selected resolved place,
|
|
7
|
-
* pretty-printed. Pure
|
|
7
|
+
* pretty-printed. Pure. shared by the explorer's copy button.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { prettyJSON } from "@mailwoman/core/json"
|
|
10
11
|
import type { ParseResult, ResolvedPlaceView } from "@mailwoman/core/pipeline/client-result"
|
|
11
12
|
|
|
12
13
|
export function buildParsePayload(result: ParseResult, selected: ResolvedPlaceView | null): string {
|
|
13
|
-
return
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
},
|
|
34
|
-
null,
|
|
35
|
-
2
|
|
36
|
-
)
|
|
14
|
+
return prettyJSON({
|
|
15
|
+
input: result.input,
|
|
16
|
+
components: result.nodes.map((node) => ({
|
|
17
|
+
tag: node.tag,
|
|
18
|
+
value: node.value ?? null,
|
|
19
|
+
confidence: node.confidence ?? null,
|
|
20
|
+
start: node.start ?? null,
|
|
21
|
+
end: node.end ?? null,
|
|
22
|
+
})),
|
|
23
|
+
resolved: selected
|
|
24
|
+
? {
|
|
25
|
+
name: selected.name,
|
|
26
|
+
placetype: selected.placetype,
|
|
27
|
+
id: selected.id,
|
|
28
|
+
lat: selected.lat,
|
|
29
|
+
lon: selected.lon,
|
|
30
|
+
score: selected.score,
|
|
31
|
+
}
|
|
32
|
+
: null,
|
|
33
|
+
})
|
|
37
34
|
}
|