@goway.to/maps 0.0.0-stage → 0.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/CHANGELOG.md +24 -0
- package/LICENSE +676 -0
- package/NOTICE +25 -0
- package/README.md +59 -2
- package/app.plugin.js +19 -0
- package/dist/CameraBinding.web.d.ts +24 -0
- package/dist/CameraBinding.web.js +46 -0
- package/dist/DefaultMapMarker.d.ts +8 -0
- package/dist/DefaultMapMarker.js +30 -0
- package/dist/GeoJSONSource.d.ts +2 -0
- package/dist/GeoJSONSource.js +17 -0
- package/dist/GeoJSONSource.web.d.ts +2 -0
- package/dist/GeoJSONSource.web.js +68 -0
- package/dist/InteractionBinding.web.d.ts +7 -0
- package/dist/InteractionBinding.web.js +35 -0
- package/dist/Layer.d.ts +2 -0
- package/dist/Layer.js +21 -0
- package/dist/Layer.web.d.ts +2 -0
- package/dist/Layer.web.js +46 -0
- package/dist/LayerStyle.web.d.ts +10 -0
- package/dist/LayerStyle.web.js +45 -0
- package/dist/LightBinding.web.d.ts +5 -0
- package/dist/LightBinding.web.js +19 -0
- package/dist/MapAttribution.d.ts +27 -0
- package/dist/MapAttribution.js +149 -0
- package/dist/MapBrand.d.ts +22 -0
- package/dist/MapBrand.js +91 -0
- package/dist/MapErrorState.d.ts +9 -0
- package/dist/MapErrorState.js +58 -0
- package/dist/MapPlugin.d.ts +6 -0
- package/dist/MapPlugin.js +3 -0
- package/dist/MapPlugin.web.d.ts +2 -0
- package/dist/MapPlugin.web.js +43 -0
- package/dist/MapView.d.ts +2 -0
- package/dist/MapView.js +420 -0
- package/dist/MapView.web.d.ts +5 -0
- package/dist/MapView.web.js +413 -0
- package/dist/Marker.d.ts +13 -0
- package/dist/Marker.js +11 -0
- package/dist/Marker.web.d.ts +15 -0
- package/dist/Marker.web.js +51 -0
- package/dist/Markers.d.ts +2 -0
- package/dist/Markers.js +13 -0
- package/dist/Markers.web.d.ts +2 -0
- package/dist/Markers.web.js +13 -0
- package/dist/PluginProps.web.d.ts +7 -0
- package/dist/PluginProps.web.js +19 -0
- package/dist/RasterDEMSource.d.ts +2 -0
- package/dist/RasterDEMSource.js +7 -0
- package/dist/RasterDEMSource.web.d.ts +2 -0
- package/dist/RasterDEMSource.web.js +44 -0
- package/dist/SourceData.web.d.ts +6 -0
- package/dist/SourceData.web.js +14 -0
- package/dist/UserLocation.d.ts +2 -0
- package/dist/UserLocation.js +19 -0
- package/dist/UserLocation.web.d.ts +2 -0
- package/dist/UserLocation.web.js +21 -0
- package/dist/brand/BrandedChrome.d.ts +47 -0
- package/dist/brand/BrandedChrome.js +51 -0
- package/dist/brand/GowayLogo.d.ts +28 -0
- package/dist/brand/GowayLogo.js +50 -0
- package/dist/brand/artwork.d.ts +130 -0
- package/dist/brand/artwork.js +176 -0
- package/dist/cameraReports.web.d.ts +50 -0
- package/dist/cameraReports.web.js +117 -0
- package/dist/cameraTarget.d.ts +14 -0
- package/dist/cameraTarget.js +8 -0
- package/dist/chromeLift.d.ts +15 -0
- package/dist/chromeLift.js +15 -0
- package/dist/clusters.d.ts +57 -0
- package/dist/clusters.js +102 -0
- package/dist/commands.web.d.ts +16 -0
- package/dist/commands.web.js +158 -0
- package/dist/compactCredit.d.ts +52 -0
- package/dist/compactCredit.js +40 -0
- package/dist/creditHeight.d.ts +3 -0
- package/dist/creditHeight.js +12 -0
- package/dist/creditLabels.d.ts +30 -0
- package/dist/creditLabels.js +30 -0
- package/dist/creditRise.d.ts +28 -0
- package/dist/creditRise.js +33 -0
- package/dist/dragAxes.web.d.ts +65 -0
- package/dist/dragAxes.web.js +70 -0
- package/dist/engineContext.web.d.ts +13 -0
- package/dist/engineContext.web.js +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/index.web.d.ts +13 -0
- package/dist/index.web.js +13 -0
- package/dist/labels.d.ts +202 -0
- package/dist/labels.js +451 -0
- package/dist/loadedCamera.d.ts +64 -0
- package/dist/loadedCamera.js +56 -0
- package/dist/locationStore.d.ts +16 -0
- package/dist/locationStore.js +132 -0
- package/dist/mapRef.d.ts +4 -0
- package/dist/mapRef.js +13 -0
- package/dist/moveMarkers.d.ts +27 -0
- package/dist/moveMarkers.js +22 -0
- package/dist/nativeCameraReports.d.ts +44 -0
- package/dist/nativeCameraReports.js +73 -0
- package/dist/plugin.d.ts +63 -0
- package/dist/plugin.js +2 -0
- package/dist/queryLayers.d.ts +19 -0
- package/dist/queryLayers.js +54 -0
- package/dist/shared-exports.d.ts +17 -0
- package/dist/shared-exports.js +13 -0
- package/dist/shared.d.ts +170 -0
- package/dist/shared.js +337 -0
- package/dist/sourceContext.d.ts +2 -0
- package/dist/sourceContext.js +9 -0
- package/dist/styleUrl.d.ts +19 -0
- package/dist/styleUrl.js +22 -0
- package/dist/testing.d.ts +41 -0
- package/dist/testing.js +80 -0
- package/dist/types.d.ts +348 -0
- package/dist/types.js +1 -0
- package/dist/useMarkerSlots.d.ts +6 -0
- package/dist/useMarkerSlots.js +56 -0
- package/dist/useUserLocation.d.ts +44 -0
- package/dist/useUserLocation.js +247 -0
- package/dist/zoom.d.ts +2 -0
- package/dist/zoom.js +10 -0
- package/metro.cjs +70 -0
- package/package.json +115 -4
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The public shapes of `@goway.to/maps`.
|
|
3
|
+
*
|
|
4
|
+
* Coordinates are GoWay's `{ latitude, longitude }` and boxes its
|
|
5
|
+
* `{ west, south, east, north }` (`@goway.to/sdk/geo`), never positional
|
|
6
|
+
* `[lng, lat]` pairs: a transposed pair is a plausible point in the wrong
|
|
7
|
+
* hemisphere rather than an error, so the one place it becomes an engine's
|
|
8
|
+
* order is inside this package (`shared.ts`).
|
|
9
|
+
*/
|
|
10
|
+
import type { ReactNode } from 'react';
|
|
11
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
12
|
+
import type { MapLightPreset } from '@goway.to/sdk';
|
|
13
|
+
import type { GeoBoundingBox, GeoCoordinate } from '@goway.to/sdk/geo';
|
|
14
|
+
export type { GeoBoundingBox, GeoCoordinate };
|
|
15
|
+
/** Where the camera is. `bearing` and `pitch` are optional on input and always present on output. */
|
|
16
|
+
export interface MapViewport {
|
|
17
|
+
latitude: number;
|
|
18
|
+
longitude: number;
|
|
19
|
+
zoom: number;
|
|
20
|
+
bearing?: number;
|
|
21
|
+
pitch?: number;
|
|
22
|
+
}
|
|
23
|
+
/** A viewport as the engine reports it back — every field resolved. */
|
|
24
|
+
export interface ResolvedMapViewport extends MapViewport {
|
|
25
|
+
bearing: number;
|
|
26
|
+
pitch: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Who moved the camera.
|
|
30
|
+
*
|
|
31
|
+
* It travels WITH the event because nothing downstream can recover it: a
|
|
32
|
+
* viewport the reader dragged to and one the app framed are the same numbers,
|
|
33
|
+
* and "Search this area" must never arm itself over a frame the app chose.
|
|
34
|
+
*/
|
|
35
|
+
export type MapMoveSource = 'user' | 'programmatic';
|
|
36
|
+
export interface MapCameraChange {
|
|
37
|
+
viewport: ResolvedMapViewport;
|
|
38
|
+
bounds: GeoBoundingBox;
|
|
39
|
+
source: MapMoveSource;
|
|
40
|
+
/** `true` on the settled frame at the end of a gesture or an animation. */
|
|
41
|
+
isFinal: boolean;
|
|
42
|
+
}
|
|
43
|
+
/** One point drawn as a marker. */
|
|
44
|
+
export interface MapMarkerData {
|
|
45
|
+
id: string;
|
|
46
|
+
coordinate: GeoCoordinate;
|
|
47
|
+
/** Short text in the default pill: a price, a name. Absent, a compact dot-sized pill. */
|
|
48
|
+
label?: string;
|
|
49
|
+
/** Read aloud for the marker; defaults to `label`. */
|
|
50
|
+
accessibilityLabel?: string;
|
|
51
|
+
/** An open string a `renderMarker` may branch on; the default marker ignores it. */
|
|
52
|
+
kind?: string;
|
|
53
|
+
}
|
|
54
|
+
/** A cluster of markers, as `<Markers cluster>` draws and reports it. */
|
|
55
|
+
export interface MapMarkerCluster {
|
|
56
|
+
/** Stable while the cluster's members and zoom are. */
|
|
57
|
+
id: string;
|
|
58
|
+
coordinate: GeoCoordinate;
|
|
59
|
+
count: number;
|
|
60
|
+
/** The ids of every marker it holds. */
|
|
61
|
+
markerIds: readonly string[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* What a marker body is drawn from: one point, or a cluster of them, with the
|
|
65
|
+
* state Bloom's map markers draw (`active` = selected, `visited` = pressed
|
|
66
|
+
* before on this map).
|
|
67
|
+
*/
|
|
68
|
+
export type MapMarkerItem = {
|
|
69
|
+
kind: 'point';
|
|
70
|
+
marker: MapMarkerData;
|
|
71
|
+
state: 'default' | 'active' | 'visited';
|
|
72
|
+
} | {
|
|
73
|
+
kind: 'cluster';
|
|
74
|
+
cluster: MapMarkerCluster;
|
|
75
|
+
state: 'default' | 'active';
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Basemap label kinds a press can resolve to:
|
|
79
|
+
* `poi` (a thing at a point with a door), `road`, `water`, `area`.
|
|
80
|
+
*/
|
|
81
|
+
export type MapLabelKind = 'poi' | 'road' | 'water' | 'area';
|
|
82
|
+
/**
|
|
83
|
+
* A label the basemap drew, as the press saw it. Thin on purpose — a name, a
|
|
84
|
+
* point, and what the tiles said it was. Its `id` joins to nothing.
|
|
85
|
+
*/
|
|
86
|
+
export interface MapLabelFeature {
|
|
87
|
+
/** Stable for as long as the loaded tiles are. Not a GoWay Place ID. */
|
|
88
|
+
id: string;
|
|
89
|
+
/** The name as the basemap draws it — `name:latin`, else `name`. */
|
|
90
|
+
name: string;
|
|
91
|
+
kind: MapLabelKind;
|
|
92
|
+
/** Its own point, the nearest point on its line, or where the reader tapped. */
|
|
93
|
+
coordinate: GeoCoordinate;
|
|
94
|
+
/** `false` when `coordinate` is the tap rather than the feature's own position. */
|
|
95
|
+
anchored: boolean;
|
|
96
|
+
/** The tiles' own `class`, verbatim. */
|
|
97
|
+
category?: string;
|
|
98
|
+
/** The tiles' finer `subclass`, verbatim. */
|
|
99
|
+
subcategory?: string;
|
|
100
|
+
/** OpenMapTiles' importance ordering, where the tiles supply one. */
|
|
101
|
+
rank?: number;
|
|
102
|
+
}
|
|
103
|
+
export interface MapPressEvent {
|
|
104
|
+
coordinate: GeoCoordinate;
|
|
105
|
+
/** The basemap label under the press, when there was one. */
|
|
106
|
+
label?: MapLabelFeature;
|
|
107
|
+
}
|
|
108
|
+
/** Which interactions are enabled; each is independent, and all default to on. */
|
|
109
|
+
export interface MapInteractionOptions {
|
|
110
|
+
/** Drag to move the camera over the ground. */
|
|
111
|
+
pan?: boolean;
|
|
112
|
+
/** Scroll, pinch and double-tap zoom. */
|
|
113
|
+
zoom?: boolean;
|
|
114
|
+
/** Turn the camera (bearing). */
|
|
115
|
+
rotate?: boolean;
|
|
116
|
+
/** Tilt the camera (pitch). Off also clamps the maximum pitch to 0. */
|
|
117
|
+
pitch?: boolean;
|
|
118
|
+
}
|
|
119
|
+
/** Why the map is degraded: the style document, tiles, no renderer, or an engine throw. */
|
|
120
|
+
export type MapErrorReason = 'style' | 'tiles' | 'unsupported' | 'runtime';
|
|
121
|
+
export interface MapError {
|
|
122
|
+
reason: MapErrorReason;
|
|
123
|
+
message?: string;
|
|
124
|
+
}
|
|
125
|
+
/** A link in the map's credit. */
|
|
126
|
+
export interface MapCreditLink {
|
|
127
|
+
label: string;
|
|
128
|
+
href: string;
|
|
129
|
+
}
|
|
130
|
+
/** A line the credit adds while something it names is drawn (e.g. simulated content). */
|
|
131
|
+
export interface MapCreditDisclosure {
|
|
132
|
+
/** The full sentence, shown while the credit is open. */
|
|
133
|
+
text: string;
|
|
134
|
+
/** Read after `text` by a screen reader. */
|
|
135
|
+
detail: string;
|
|
136
|
+
/** A word or two, shown beside the folded credit's ⓘ. */
|
|
137
|
+
short: string;
|
|
138
|
+
}
|
|
139
|
+
export type MapPadding = number | {
|
|
140
|
+
top: number;
|
|
141
|
+
right: number;
|
|
142
|
+
bottom: number;
|
|
143
|
+
left: number;
|
|
144
|
+
};
|
|
145
|
+
export interface MapCameraOptions {
|
|
146
|
+
zoom?: number;
|
|
147
|
+
bearing?: number;
|
|
148
|
+
pitch?: number;
|
|
149
|
+
/** Animation duration in ms; `0` jumps. */
|
|
150
|
+
duration?: number;
|
|
151
|
+
/**
|
|
152
|
+
* One frame of an animation the caller drives: reported as in progress,
|
|
153
|
+
* never as a settled camera. End it with one ordinary move.
|
|
154
|
+
*/
|
|
155
|
+
transient?: boolean;
|
|
156
|
+
/** Vertical field of view, degrees (10–60). Web only; native ignores it. */
|
|
157
|
+
fieldOfView?: number;
|
|
158
|
+
}
|
|
159
|
+
export interface MapFitOptions {
|
|
160
|
+
padding?: MapPadding;
|
|
161
|
+
duration?: number;
|
|
162
|
+
/** Never zoom in past this, however tight the box is. */
|
|
163
|
+
maxZoom?: number;
|
|
164
|
+
}
|
|
165
|
+
/** One drawn frame (web only); `canvas` holds the pixels only during the callback. */
|
|
166
|
+
export interface MapFrame {
|
|
167
|
+
canvas: HTMLCanvasElement;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The map's imperative handle — for EVENT HANDLERS (a button that re-frames,
|
|
171
|
+
* a press that recentres). Anything a screen derives from state is a
|
|
172
|
+
* declarative prop instead: `<Camera bounds>`, `<Markers selectedId>`.
|
|
173
|
+
*/
|
|
174
|
+
export interface MapViewRef {
|
|
175
|
+
/**
|
|
176
|
+
* Move the camera. A bare coordinate keeps zoom, bearing and pitch. Answers
|
|
177
|
+
* whether a move was issued — `false` when there is no engine yet or the
|
|
178
|
+
* target is not drawable, so a caller waiting for its settle must not.
|
|
179
|
+
*/
|
|
180
|
+
moveTo(target: GeoCoordinate | MapViewport, options?: MapCameraOptions): boolean;
|
|
181
|
+
/** Frame an area. A box with no area moves to its centre at a street zoom. */
|
|
182
|
+
fitBounds(bounds: GeoBoundingBox, options?: MapFitOptions): void;
|
|
183
|
+
/** Frame a set of points. */
|
|
184
|
+
fitCoordinates(coordinates: readonly GeoCoordinate[], options?: MapFitOptions): void;
|
|
185
|
+
/** Rotate to a bearing, degrees clockwise from north. */
|
|
186
|
+
setBearing(bearing: number, options?: MapCameraOptions): void;
|
|
187
|
+
/** North up and the pitch level. */
|
|
188
|
+
resetNorth(options?: MapCameraOptions): void;
|
|
189
|
+
/** The camera now, or `null` before the map is ready. */
|
|
190
|
+
getViewport(): Promise<ResolvedMapViewport | null>;
|
|
191
|
+
/** The visible box now, or `null` before the map is ready. */
|
|
192
|
+
getBounds(): Promise<GeoBoundingBox | null>;
|
|
193
|
+
/**
|
|
194
|
+
* Web only: `draw` with every frame the engine draws, synchronously after it
|
|
195
|
+
* drew it, until the returned function is called. Absent on native.
|
|
196
|
+
*/
|
|
197
|
+
captureFrames?(draw: (frame: MapFrame) => void): () => void;
|
|
198
|
+
}
|
|
199
|
+
export interface MapViewProps {
|
|
200
|
+
/**
|
|
201
|
+
* The style: a `goway://styles/<id>` name (default `goway://styles/standard`),
|
|
202
|
+
* any MapLibre style URL, or a style document. A GoWay style follows the
|
|
203
|
+
* Bloom theme's light — day in light mode, night in dark — unless
|
|
204
|
+
* `lightPreset` names one.
|
|
205
|
+
*/
|
|
206
|
+
mapStyle?: string;
|
|
207
|
+
/** Pin a GoWay style's light: `day`, `dawn`, `dusk` or `night`. */
|
|
208
|
+
lightPreset?: MapLightPreset;
|
|
209
|
+
/**
|
|
210
|
+
* The camera on first render. Absent, the map opens on `camera`'s target, or
|
|
211
|
+
* on the whole world.
|
|
212
|
+
*/
|
|
213
|
+
initialViewState?: MapInitialViewState;
|
|
214
|
+
/** Where the camera should be, applied whenever its VALUE changes (`MapCameraTarget`). */
|
|
215
|
+
camera?: MapCameraTarget;
|
|
216
|
+
/** `globe` draws a sphere to ~z11 easing into mercator. Changing it rebuilds the engine. */
|
|
217
|
+
projection?: 'mercator' | 'globe';
|
|
218
|
+
/** The style's light (a MapLibre `LightSpecification`), e.g. aimed from the sun. */
|
|
219
|
+
light?: Readonly<Record<string, unknown>> | null;
|
|
220
|
+
interaction?: MapInteractionOptions;
|
|
221
|
+
/** The highest pitch the camera may take; default 60. */
|
|
222
|
+
maxPitch?: number;
|
|
223
|
+
/** Web only, read when the engine is built: the device pixel ratio cap. */
|
|
224
|
+
pixelRatio?: number;
|
|
225
|
+
/** Web only, read when the engine is built: multisample antialiasing. */
|
|
226
|
+
antialias?: boolean;
|
|
227
|
+
/** Credits owed for data the app draws over the basemap, beside the basemap's own. */
|
|
228
|
+
credits?: readonly MapCreditLink[];
|
|
229
|
+
/** A line the credit carries while something it names is drawn. */
|
|
230
|
+
creditDisclosure?: MapCreditDisclosure | null;
|
|
231
|
+
/** A press on the map that no marker took: the coordinate, and the basemap label under it. */
|
|
232
|
+
onPress?: (event: MapPressEvent) => void;
|
|
233
|
+
/** The camera, live while it moves (`isFinal: false`) and once when it settles. */
|
|
234
|
+
onCameraChanged?: (change: MapCameraChange) => void;
|
|
235
|
+
/** The basemap labels placed in the viewport, on every settled frame. */
|
|
236
|
+
onLabelsChange?: (labels: readonly MapLabelFeature[]) => void;
|
|
237
|
+
/** The style has loaded and the first frame is on screen. */
|
|
238
|
+
onReady?: () => void;
|
|
239
|
+
/** The map entered (`MapError`) or left (`null`) its degraded state. */
|
|
240
|
+
onError?: (error: MapError | null) => void;
|
|
241
|
+
style?: StyleProp<ViewStyle>;
|
|
242
|
+
testID?: string;
|
|
243
|
+
/**
|
|
244
|
+
* What is drawn ON the map: `<Markers>`, sources and layers,
|
|
245
|
+
* `<UserLocation>`, plugins. Chrome laid over the map (buttons, cards) goes
|
|
246
|
+
* beside the `MapView` in the screen's own layout.
|
|
247
|
+
*/
|
|
248
|
+
children?: ReactNode;
|
|
249
|
+
}
|
|
250
|
+
/** The camera a map opens at: a point and zoom, or a box to frame. */
|
|
251
|
+
export type MapInitialViewState = MapViewport | {
|
|
252
|
+
bounds: GeoBoundingBox;
|
|
253
|
+
padding?: MapPadding;
|
|
254
|
+
maxZoom?: number;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Where the camera should be, as a VALUE: applied whenever its numbers change
|
|
258
|
+
* — compared by the four numbers of the box (or the point and zoom), never by
|
|
259
|
+
* identity, so a re-render with an equal target never moves the camera under
|
|
260
|
+
* a reader who has panned. To frame the SAME target again after a pan (a
|
|
261
|
+
* "back to the searched area" button), call `fitBounds` on the map's ref from
|
|
262
|
+
* that button's handler.
|
|
263
|
+
*/
|
|
264
|
+
export interface MapCameraTarget {
|
|
265
|
+
/** The box to frame. A box with no area moves to its centre at street zoom. */
|
|
266
|
+
bounds?: GeoBoundingBox | null;
|
|
267
|
+
/** The centre to move to; ignored while `bounds` is set. */
|
|
268
|
+
center?: GeoCoordinate | null;
|
|
269
|
+
/** The zoom to move to with `center`. */
|
|
270
|
+
zoom?: number;
|
|
271
|
+
padding?: MapPadding;
|
|
272
|
+
/** Never zoom past this when framing `bounds`. */
|
|
273
|
+
maxZoom?: number;
|
|
274
|
+
/** Animation duration in ms; default 500, `0` jumps. */
|
|
275
|
+
duration?: number;
|
|
276
|
+
}
|
|
277
|
+
export interface MarkersProps {
|
|
278
|
+
data: readonly MapMarkerData[];
|
|
279
|
+
/**
|
|
280
|
+
* The marker drawn active. With `cluster`, it is never folded into a
|
|
281
|
+
* cluster: what the reader is looking at stays on the map.
|
|
282
|
+
*/
|
|
283
|
+
selectedId?: string | null;
|
|
284
|
+
/** Group nearby markers into Bloom cluster bubbles. */
|
|
285
|
+
cluster?: boolean;
|
|
286
|
+
/** Cluster radius in pixels; default 50 (Mapbox's). */
|
|
287
|
+
clusterRadius?: number;
|
|
288
|
+
/** Zoom from which markers are never clustered; default 14 (Mapbox's). */
|
|
289
|
+
clusterMaxZoom?: number;
|
|
290
|
+
/** Minimum points that form a cluster; default 2. */
|
|
291
|
+
clusterMinPoints?: number;
|
|
292
|
+
/**
|
|
293
|
+
* A custom body for a marker or cluster; call `onPress` from its own press
|
|
294
|
+
* so the marker is remembered as visited and a cluster opens. Default:
|
|
295
|
+
* Bloom's `MapPriceMarker` / `MapClusterMarker`.
|
|
296
|
+
*/
|
|
297
|
+
renderMarker?: (item: MapMarkerItem, onPress: () => void) => ReactNode;
|
|
298
|
+
/** A marker was pressed (it is also remembered as visited). */
|
|
299
|
+
onPress?: (marker: MapMarkerData) => void;
|
|
300
|
+
/**
|
|
301
|
+
* A cluster was pressed. The map zooms in to where it splits by itself; this
|
|
302
|
+
* only tells the screen which markers it held.
|
|
303
|
+
*/
|
|
304
|
+
onClusterPress?: (cluster: MapMarkerCluster) => void;
|
|
305
|
+
}
|
|
306
|
+
export interface GeoJSONSourceProps {
|
|
307
|
+
id: string;
|
|
308
|
+
data: GeoJSON.GeoJSON;
|
|
309
|
+
/** Cluster the points (MapLibre's own source clustering), for GL-drawn layers. */
|
|
310
|
+
cluster?: boolean;
|
|
311
|
+
clusterRadius?: number;
|
|
312
|
+
clusterMaxZoom?: number;
|
|
313
|
+
clusterMinPoints?: number;
|
|
314
|
+
clusterProperties?: Readonly<Record<string, unknown>>;
|
|
315
|
+
/** `<Layer>` children drawing this source. */
|
|
316
|
+
children?: ReactNode;
|
|
317
|
+
}
|
|
318
|
+
export interface RasterDEMSourceProps {
|
|
319
|
+
id: string;
|
|
320
|
+
tiles: readonly string[];
|
|
321
|
+
encoding?: 'mapbox' | 'terrarium';
|
|
322
|
+
tileSize?: number;
|
|
323
|
+
minzoom?: number;
|
|
324
|
+
maxzoom?: number;
|
|
325
|
+
children?: ReactNode;
|
|
326
|
+
}
|
|
327
|
+
export type MapLayerType = 'line' | 'fill' | 'circle' | 'symbol' | 'fill-extrusion' | 'hillshade';
|
|
328
|
+
export interface LayerProps {
|
|
329
|
+
id: string;
|
|
330
|
+
type: MapLayerType;
|
|
331
|
+
/** Style-spec paint properties (`line-color`, `fill-opacity`, …). */
|
|
332
|
+
paint?: Readonly<Record<string, unknown>>;
|
|
333
|
+
/** Style-spec layout properties. */
|
|
334
|
+
layout?: Readonly<Record<string, unknown>>;
|
|
335
|
+
filter?: readonly unknown[];
|
|
336
|
+
/** Draw under this layer (e.g. under the labels). Absent: on top. */
|
|
337
|
+
beforeId?: string;
|
|
338
|
+
minzoom?: number;
|
|
339
|
+
maxzoom?: number;
|
|
340
|
+
}
|
|
341
|
+
export interface UserLocationProps {
|
|
342
|
+
/**
|
|
343
|
+
* Draw the reader's position. Only ever against a permission ALREADY
|
|
344
|
+
* granted: opening a map never prompts. Ask with `useUserLocation()` when
|
|
345
|
+
* the reader asks for something that needs it.
|
|
346
|
+
*/
|
|
347
|
+
visible?: boolean;
|
|
348
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type MarkerSlot } from './clusters';
|
|
2
|
+
import type { MarkersProps } from './types';
|
|
3
|
+
export declare function useMarkerSlots({ data, selectedId, cluster, clusterRadius, clusterMaxZoom, clusterMinPoints, onPress, onClusterPress, }: MarkersProps): {
|
|
4
|
+
slots: readonly MarkerSlot[];
|
|
5
|
+
press: (slot: MarkerSlot) => void;
|
|
6
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `<Markers>` draws, and what a press on each does — the part both forks
|
|
3
|
+
* share. The fork only places each slot with its engine's marker.
|
|
4
|
+
*/
|
|
5
|
+
import { useCallback, useContext, useMemo, useState } from 'react';
|
|
6
|
+
import { DEFAULT_CLUSTER_OPTIONS, indexMarkers, markerSlotsAt, } from './clusters';
|
|
7
|
+
import { MapRefContext } from './mapRef';
|
|
8
|
+
import { drawableMarkers } from './shared';
|
|
9
|
+
import { useZoomLevel } from './zoom';
|
|
10
|
+
export function useMarkerSlots({ data, selectedId, cluster = false, clusterRadius = DEFAULT_CLUSTER_OPTIONS.radius, clusterMaxZoom = DEFAULT_CLUSTER_OPTIONS.maxZoom, clusterMinPoints = DEFAULT_CLUSTER_OPTIONS.minPoints, onPress, onClusterPress, }) {
|
|
11
|
+
const zoom = useZoomLevel();
|
|
12
|
+
const map = useContext(MapRefContext);
|
|
13
|
+
// Undrawable markers are dropped, and said so once (`shared.ts`).
|
|
14
|
+
const drawable = drawableMarkers(data);
|
|
15
|
+
// The selected marker is never folded into a cluster: what the reader is
|
|
16
|
+
// looking at stays on the map, at its own coordinate, whatever the zoom.
|
|
17
|
+
const pinned = cluster && selectedId != null ? drawable.find((m) => m.id === selectedId) : undefined;
|
|
18
|
+
const index = useMemo(() => {
|
|
19
|
+
const options = cluster
|
|
20
|
+
? { radius: clusterRadius, maxZoom: clusterMaxZoom, minPoints: clusterMinPoints }
|
|
21
|
+
: null;
|
|
22
|
+
const gathered = pinned ? drawable.filter((marker) => marker !== pinned) : drawable;
|
|
23
|
+
return indexMarkers(gathered, options);
|
|
24
|
+
}, [drawable, pinned, cluster, clusterRadius, clusterMaxZoom, clusterMinPoints]);
|
|
25
|
+
// Pressed before on this map: drawn muted, so the reader sees what they
|
|
26
|
+
// have already looked at.
|
|
27
|
+
const [visited, setVisited] = useState(() => new Set());
|
|
28
|
+
const slots = useMemo(() => {
|
|
29
|
+
const drawn = markerSlotsAt(index, zoom, selectedId, visited);
|
|
30
|
+
if (!pinned)
|
|
31
|
+
return drawn;
|
|
32
|
+
return [
|
|
33
|
+
...drawn,
|
|
34
|
+
{
|
|
35
|
+
key: `point:${pinned.id}`,
|
|
36
|
+
coordinate: pinned.coordinate,
|
|
37
|
+
item: { kind: 'point', marker: pinned, state: 'active' },
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
}, [index, zoom, selectedId, visited, pinned]);
|
|
41
|
+
const press = useCallback((slot) => {
|
|
42
|
+
const { item } = slot;
|
|
43
|
+
if (item.kind === 'point') {
|
|
44
|
+
const id = item.marker.id;
|
|
45
|
+
setVisited((previous) => (previous.has(id) ? previous : new Set(previous).add(id)));
|
|
46
|
+
onPress?.(item.marker);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// A cluster opens where it splits, as Mapbox's clusters do.
|
|
50
|
+
if (slot.expansionZoom !== undefined) {
|
|
51
|
+
map?.moveTo(slot.coordinate, { zoom: slot.expansionZoom });
|
|
52
|
+
}
|
|
53
|
+
onClusterPress?.(item.cluster);
|
|
54
|
+
}, [map, onPress, onClusterPress]);
|
|
55
|
+
return { slots, press };
|
|
56
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { GeoCoordinate } from './types';
|
|
2
|
+
export type LocationPermissionStatus =
|
|
3
|
+
/** Not asked yet in this session — and not asked ON OUR BEHALF at mount. */
|
|
4
|
+
'unknown' | 'granted' | 'denied';
|
|
5
|
+
export type LocationErrorReason =
|
|
6
|
+
/** The user was asked and said no (or the browser has blocked this origin). */
|
|
7
|
+
'denied'
|
|
8
|
+
/** Permission is fine; no position came back. Services off, no provider. */
|
|
9
|
+
| 'unavailable'
|
|
10
|
+
/** Permission is fine; nothing answered within {@link FIX_TIMEOUT_MS}. */
|
|
11
|
+
| 'timeout'
|
|
12
|
+
/**
|
|
13
|
+
* Web only, and NOT a permission decision: the page is not a secure context,
|
|
14
|
+
* so the browser refuses geolocation without prompting. Unrecoverable by
|
|
15
|
+
* retrying — only by loading the same app over https.
|
|
16
|
+
*/
|
|
17
|
+
| 'insecureContext';
|
|
18
|
+
export interface UserLocationState {
|
|
19
|
+
status: LocationPermissionStatus;
|
|
20
|
+
/** Last fix, for this screen only. Never persisted. */
|
|
21
|
+
coordinate: GeoCoordinate | null;
|
|
22
|
+
isLocating: boolean;
|
|
23
|
+
error: LocationErrorReason | null;
|
|
24
|
+
/**
|
|
25
|
+
* Whether invoking {@link UserLocationApi.locate} again could still produce a
|
|
26
|
+
* system prompt. `false` means the answer is durable and the only way back is
|
|
27
|
+
* the platform's own permission control — so an "allow location" button would
|
|
28
|
+
* be a button that does nothing.
|
|
29
|
+
*/
|
|
30
|
+
canAskAgain: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface UserLocationApi extends UserLocationState {
|
|
33
|
+
/**
|
|
34
|
+
* Ask (if needed) and locate, in that order, as one user-initiated action.
|
|
35
|
+
*
|
|
36
|
+
* Resolves `null` when the user declines or no fix is available. A caller
|
|
37
|
+
* that ignores the `null` MUST still react to {@link UserLocationState.error},
|
|
38
|
+
* which is set before this resolves: every failure here is silent otherwise.
|
|
39
|
+
*/
|
|
40
|
+
locate: () => Promise<GeoCoordinate | null>;
|
|
41
|
+
/** Drop the fix from memory (e.g. when leaving a directions flow). */
|
|
42
|
+
forget: () => void;
|
|
43
|
+
}
|
|
44
|
+
export declare function useUserLocation(): UserLocationApi;
|