@trackunit/react-map-adapter-google 0.0.3
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/LICENSE.txt +191 -0
- package/README.md +34 -0
- package/index.cjs.js +3015 -0
- package/index.d.ts +2 -0
- package/index.esm.js +3010 -0
- package/package.json +25 -0
- package/src/GoogleApiProvider.d.ts +29 -0
- package/src/GoogleMapsAdapterInstance.d.ts +84 -0
- package/src/GoogleMapsLayerPort.d.ts +255 -0
- package/src/GoogleMapsRenderer.d.ts +17 -0
- package/src/constants.d.ts +8 -0
- package/src/google-maps.d.ts +11 -0
- package/src/googleMapsAdapter.d.ts +39 -0
- package/src/googleTypeConverters.d.ts +22 -0
- package/src/index.d.ts +4 -0
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trackunit/react-map-adapter-google",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"repository": "https://github.com/Trackunit/manager",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=24.x"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@trackunit/react-map-adapter-shared": "0.0.16",
|
|
11
|
+
"@trackunit/geo-json-utils": "1.14.35",
|
|
12
|
+
"@googlemaps/markerclusterer": "^2.6.2",
|
|
13
|
+
"@vis.gl/react-google-maps": "^1.7.1",
|
|
14
|
+
"es-toolkit": "^1.39.10",
|
|
15
|
+
"react-device-detect": "^2.2.3",
|
|
16
|
+
"zod": "^3.25.76"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"react": "^19.0.0",
|
|
20
|
+
"@types/google.maps": "^3.54.10"
|
|
21
|
+
},
|
|
22
|
+
"module": "./index.esm.js",
|
|
23
|
+
"main": "./index.cjs.js",
|
|
24
|
+
"types": "./index.d.ts"
|
|
25
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from "react";
|
|
2
|
+
type GoogleApiProviderProps = Readonly<{
|
|
3
|
+
/** Google Maps API key */
|
|
4
|
+
apiKey: string;
|
|
5
|
+
/** Language for map labels */
|
|
6
|
+
language?: string;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Shared Google Maps API context for multi-map layouts.
|
|
11
|
+
*
|
|
12
|
+
* Wrap multiple `<Map>` instances in a single `GoogleApiProvider` to load the
|
|
13
|
+
* Google Maps JS API once and prevent zombie `google.maps.Map` instances that
|
|
14
|
+
* occur when multiple `APIProvider`s initialize concurrently with `mapId`
|
|
15
|
+
* (vector/cloud-styled maps).
|
|
16
|
+
*
|
|
17
|
+
* Each map should have a unique `mapInstanceId` in its adapter config so the
|
|
18
|
+
* vis.gl registry can distinguish them.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <GoogleApiProvider apiKey={API_KEY}>
|
|
23
|
+
* <Map1 /> // googleMapsAdapter({ apiKey, mapInstanceId: "map-1" })
|
|
24
|
+
* <Map2 /> // googleMapsAdapter({ apiKey, mapInstanceId: "map-2" })
|
|
25
|
+
* </GoogleApiProvider>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare const GoogleApiProvider: ({ apiKey, language, children }: GoogleApiProviderProps) => ReactElement;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type AdapterInstance, type BaseAdapterConfig, type CameraState, type FitBoundsOptions, type GeoJsonBbox, type GeoJsonPosition, type MapEvent, type MapEventHandler, type MapStatus, type MapTheme, type MapType } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
import { GoogleMapsLayerPort } from "./GoogleMapsLayerPort";
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for Google Maps adapter
|
|
5
|
+
*/
|
|
6
|
+
export type GoogleMapsConfig = Readonly<BaseAdapterConfig & {
|
|
7
|
+
/** Google Maps API key */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
/**
|
|
10
|
+
* Unique instance identifier for this map within a shared `APIProvider`.
|
|
11
|
+
* Required when multiple Google Maps coexist under a single `GoogleApiProvider`.
|
|
12
|
+
* If omitted, `GoogleMapsRenderer` auto-generates one via `useId()`.
|
|
13
|
+
*/
|
|
14
|
+
mapInstanceId?: string;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Google Maps adapter instance implementation
|
|
18
|
+
* Manages the connection between our abstract interface and Google Maps API
|
|
19
|
+
*/
|
|
20
|
+
export declare class GoogleMapsAdapterInstance implements AdapterInstance {
|
|
21
|
+
private readonly config;
|
|
22
|
+
/** Layer rendering capabilities, provided via MapLayerContext to <Layers>. */
|
|
23
|
+
readonly layers: GoogleMapsLayerPort;
|
|
24
|
+
private map;
|
|
25
|
+
private isDestroyedFlag;
|
|
26
|
+
private state;
|
|
27
|
+
private cachedCameraState;
|
|
28
|
+
private cachedStatus;
|
|
29
|
+
private readonly cameraListeners;
|
|
30
|
+
private readonly statusListeners;
|
|
31
|
+
private readonly eventListeners;
|
|
32
|
+
private googleEventListeners;
|
|
33
|
+
/** Throttled pointermove channel shared by map mousemove and shape overlay mousemove. */
|
|
34
|
+
private pointerMoveRaf;
|
|
35
|
+
private lastPointerPosition;
|
|
36
|
+
/** Unified appearance state — theme, mapType, and showRoads in one place */
|
|
37
|
+
private appearance;
|
|
38
|
+
constructor(config: GoogleMapsConfig);
|
|
39
|
+
/**
|
|
40
|
+
* Connect to a Google Maps instance - called by GoogleMapsRenderer.
|
|
41
|
+
*
|
|
42
|
+
* Handles both first connection and reconnection (e.g. after a theme change
|
|
43
|
+
* causes vis.gl to create a new google.maps.Map instance for the new mapId).
|
|
44
|
+
* On reconnection the previous center, zoom, and map type are restored so
|
|
45
|
+
* the user doesn't see the viewport jump.
|
|
46
|
+
*/
|
|
47
|
+
connect(map: google.maps.Map): void;
|
|
48
|
+
getConfig(): GoogleMapsConfig;
|
|
49
|
+
getCameraState(): CameraState;
|
|
50
|
+
getStatus(): MapStatus;
|
|
51
|
+
subscribeCamera(listener: () => void): () => void;
|
|
52
|
+
subscribeStatus(listener: () => void): () => void;
|
|
53
|
+
/**
|
|
54
|
+
* Returns the current theme.
|
|
55
|
+
* Used by GoogleMapsRenderer (via useSyncExternalStore) to reactively
|
|
56
|
+
* update the mapId prop on the <GoogleMap> component.
|
|
57
|
+
*/
|
|
58
|
+
getTheme(): MapTheme;
|
|
59
|
+
setCenter(center: GeoJsonPosition): Promise<void>;
|
|
60
|
+
setZoom(zoomLevel: number): Promise<void>;
|
|
61
|
+
zoomBy(delta: number): Promise<void>;
|
|
62
|
+
fitBounds(bounds: GeoJsonBbox, options?: FitBoundsOptions): Promise<void>;
|
|
63
|
+
panTo(center: GeoJsonPosition): Promise<void>;
|
|
64
|
+
panBy(deltaX: number, deltaY: number): Promise<void>;
|
|
65
|
+
setMapType(type: MapType): Promise<void>;
|
|
66
|
+
setTheme(theme: MapTheme): Promise<void>;
|
|
67
|
+
setShowRoads(showRoads: boolean): Promise<void>;
|
|
68
|
+
on<TEventType extends MapEvent["type"]>(event: TEventType, handler: MapEventHandler<TEventType>): () => void;
|
|
69
|
+
notifyInitializationFailed(): void;
|
|
70
|
+
destroy(): void;
|
|
71
|
+
/** Detach all Google Maps native event listeners without clearing our own subscribers */
|
|
72
|
+
private removeGoogleEventListeners;
|
|
73
|
+
/** Emit a throttled pointermove event (map canvas + shape overlays). */
|
|
74
|
+
private reportPointerMove;
|
|
75
|
+
private setupEventListeners;
|
|
76
|
+
private updateState;
|
|
77
|
+
private mapTypeToGoogleMapTypeId;
|
|
78
|
+
private updateCameraCache;
|
|
79
|
+
private updateStatusCache;
|
|
80
|
+
private notifyCameraListeners;
|
|
81
|
+
private notifyStatusListeners;
|
|
82
|
+
private emitEvent;
|
|
83
|
+
}
|
|
84
|
+
export declare const createGoogleMapsInstance: (config: GoogleMapsConfig) => GoogleMapsAdapterInstance;
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { type GeoJsonPosition } from "@trackunit/geo-json-utils";
|
|
2
|
+
import { type DomPortalStore, type EntityInteractionHandler, type LayerPort, type LayerSnapshot, type MapTheme, type ShapeStyleDefaults } from "@trackunit/react-map-adapter-shared";
|
|
3
|
+
/**
|
|
4
|
+
* Google Maps implementation of the LayerPort interface.
|
|
5
|
+
*
|
|
6
|
+
* Manages Google Maps objects to render map content.
|
|
7
|
+
* Each source type maps to appropriate Google Maps constructs:
|
|
8
|
+
* - Markers: Canvas OverlayView (symbol/adaptive mode), AdvancedMarkerElement (DOM mode)
|
|
9
|
+
* - Shapes: google.maps.Polygon / google.maps.Polyline
|
|
10
|
+
* - Routes: google.maps.Polyline with optional arrow symbols
|
|
11
|
+
* - Image overlays: google.maps.GroundOverlay
|
|
12
|
+
*
|
|
13
|
+
* Requires `connect(map)` to be called (by GoogleMapsAdapterInstance) before
|
|
14
|
+
* any sources can be rendered. Sources set before connect are queued.
|
|
15
|
+
*/
|
|
16
|
+
export declare class GoogleMapsLayerPort implements LayerPort {
|
|
17
|
+
private static readonly DBLCLICK_THRESHOLD_MS;
|
|
18
|
+
/**
|
|
19
|
+
* Marker portal subscription compatible with `useSyncExternalStore`.
|
|
20
|
+
* `<Layers>` routes descriptors from this store to `markerRender`. Includes
|
|
21
|
+
* static DOM markers and adaptive markers resolved to the DOM medium.
|
|
22
|
+
*/
|
|
23
|
+
readonly markerPortals: DomPortalStore;
|
|
24
|
+
/**
|
|
25
|
+
* Cluster portal subscription compatible with `useSyncExternalStore`.
|
|
26
|
+
* `<Layers>` routes descriptors from this store to `clusterRender`. Includes
|
|
27
|
+
* server-cluster DOM containers and any other cluster-class portals.
|
|
28
|
+
*/
|
|
29
|
+
readonly clusterPortals: DomPortalStore;
|
|
30
|
+
private map;
|
|
31
|
+
private readonly interactionHandlers;
|
|
32
|
+
private readonly backgroundClickHandlers;
|
|
33
|
+
private entityClickedInTick;
|
|
34
|
+
private mapClickListener;
|
|
35
|
+
private readonly markerSources;
|
|
36
|
+
/** featureId → element, keyed by sourceId. Cleared when a source is removed. */
|
|
37
|
+
private readonly adaptiveMarkerIndex;
|
|
38
|
+
/** cluster featureId → element, keyed by sourceId. Cleared when a source is removed. */
|
|
39
|
+
private readonly clusterMarkerIndex;
|
|
40
|
+
private readonly shapeSources;
|
|
41
|
+
private readonly routeSources;
|
|
42
|
+
private readonly overlaySources;
|
|
43
|
+
private pendingSnapshot;
|
|
44
|
+
private lastNonEmptySnapshot;
|
|
45
|
+
private markerPortalDescriptors;
|
|
46
|
+
private clusterPortalDescriptors;
|
|
47
|
+
private readonly markerPortalSubscribers;
|
|
48
|
+
private readonly clusterPortalSubscribers;
|
|
49
|
+
private readonly clusterEntitiesByMarker;
|
|
50
|
+
private readonly markerHoverState;
|
|
51
|
+
private selectedShapeHandleId;
|
|
52
|
+
private selectedShapeFeatureId;
|
|
53
|
+
private hoveredShapeHandleId;
|
|
54
|
+
private hoveredShapeFeatureId;
|
|
55
|
+
private theme;
|
|
56
|
+
private lastShapeClickTime;
|
|
57
|
+
private lastShapeClickEntity;
|
|
58
|
+
/** Forwards geographic pointer position to the adapter (fill tiling settle channel). */
|
|
59
|
+
private pointerMoveReporter;
|
|
60
|
+
private holdNativeZoomTimer;
|
|
61
|
+
private mapDblClickListenerCleanup;
|
|
62
|
+
private _shapeStyleDefaults;
|
|
63
|
+
private get shapeStyleDefaults();
|
|
64
|
+
setShapeStyleDefaults(defaults: ShapeStyleDefaults): void;
|
|
65
|
+
/**
|
|
66
|
+
* Connect to a Google Maps instance.
|
|
67
|
+
* Called by GoogleMapsAdapterInstance.connect() when the map is ready.
|
|
68
|
+
* Flushes any sources that were set before the map was available.
|
|
69
|
+
*/
|
|
70
|
+
connect(map: google.maps.Map): void;
|
|
71
|
+
setSnapshot(snapshot: LayerSnapshot): void;
|
|
72
|
+
/**
|
|
73
|
+
* Register a reporter for geographic pointer position. Google Maps does not
|
|
74
|
+
* emit map-level mousemove while the cursor is over clickable shape overlays;
|
|
75
|
+
* shape listeners call this so fill-tiling settle gets the correct position.
|
|
76
|
+
*/
|
|
77
|
+
setPointerMoveReporter(reporter: ((position: GeoJsonPosition) => void) | null): void;
|
|
78
|
+
/**
|
|
79
|
+
* Update the theme used for interaction style resolution (hover color shifts).
|
|
80
|
+
* Called by the adapter instance when the map theme changes.
|
|
81
|
+
*/
|
|
82
|
+
setTheme(theme: MapTheme): void;
|
|
83
|
+
onEntityInteraction(handler: EntityInteractionHandler): () => void;
|
|
84
|
+
onBackgroundClick(handler: () => void): () => void;
|
|
85
|
+
onSourceReady(_sourceId: string, callback: () => void): () => void;
|
|
86
|
+
/** @internal */
|
|
87
|
+
destroy(): void;
|
|
88
|
+
private applySnapshot;
|
|
89
|
+
private syncMarkerSource;
|
|
90
|
+
private removeMarkerSourceInternal;
|
|
91
|
+
private syncShapeSource;
|
|
92
|
+
/** Deep equality for clipped fill overrides — map emissions create new Map refs every time. */
|
|
93
|
+
private fillOverrideEqual;
|
|
94
|
+
/**
|
|
95
|
+
* Patch only features whose fill-override or z-index changed (ADR-0021 Path B).
|
|
96
|
+
* Returns the number of features that were updated or re-rendered.
|
|
97
|
+
*/
|
|
98
|
+
private syncShapeTilingOverrides;
|
|
99
|
+
/** Remove all map shapes (and their listeners) for a single feature id. */
|
|
100
|
+
private removeFeatureShapes;
|
|
101
|
+
/** Update z-index on fill-bearing polygons for one feature without a geometry rebuild. */
|
|
102
|
+
private updateFeatureShapeZIndex;
|
|
103
|
+
private removeShapeSourceInternal;
|
|
104
|
+
private applyShapeSelectionState;
|
|
105
|
+
private applyShapeHoverState;
|
|
106
|
+
private syncRouteSource;
|
|
107
|
+
private removeRouteSourceInternal;
|
|
108
|
+
private syncImageOverlay;
|
|
109
|
+
private removeImageOverlayInternal;
|
|
110
|
+
/** Add a portal descriptor to the marker store and notify subscribers */
|
|
111
|
+
private addMarkerPortalDescriptor;
|
|
112
|
+
/** Add a portal descriptor to the cluster store and notify subscribers */
|
|
113
|
+
private addClusterPortalDescriptor;
|
|
114
|
+
/** Remove all portal descriptors for `sourceId` from both stores and notify */
|
|
115
|
+
private removePortalDescriptorsForSource;
|
|
116
|
+
private notifyMarkerPortalSubscribers;
|
|
117
|
+
private notifyClusterPortalSubscribers;
|
|
118
|
+
/**
|
|
119
|
+
* Patch existing DOM markers in place: update positions and refresh portal
|
|
120
|
+
* descriptors so React re-renders with the latest render function.
|
|
121
|
+
* Avoids the remove-then-add cycle that causes a brief flash.
|
|
122
|
+
*/
|
|
123
|
+
private patchDomMarkers;
|
|
124
|
+
/**
|
|
125
|
+
* Incrementally sync adaptive markers and server-side clusters when a viewport
|
|
126
|
+
* refetch changes which feature ids are visible, without tearing down unchanged DOM markers.
|
|
127
|
+
*/
|
|
128
|
+
private patchAdaptiveViewportData;
|
|
129
|
+
private removeGoneAdaptiveAssetMarkers;
|
|
130
|
+
private removeGoneServerClusterMarkers;
|
|
131
|
+
private syncServerClusterMarkers;
|
|
132
|
+
/**
|
|
133
|
+
* Incrementally update adaptive DOM markers and the canvas symbol overlay
|
|
134
|
+
* when `adaptiveResolution` (or callbacks) changed but feature IDs are the same.
|
|
135
|
+
*
|
|
136
|
+
* Three phases:
|
|
137
|
+
* 1. DOM → symbol: detach DOM marker + remove portal for features leaving DOM mode.
|
|
138
|
+
* 2. Symbol → DOM: add DOM marker + portal for features entering DOM mode.
|
|
139
|
+
* 3. DOM → DOM: update portal renderFn (so the consumer's latest closure runs).
|
|
140
|
+
* 4. Update canvas symbol overlay to reflect the new DOM/symbol split.
|
|
141
|
+
*/
|
|
142
|
+
private patchAdaptiveMarkers;
|
|
143
|
+
/**
|
|
144
|
+
* Repaint the canvas symbol overlay for a `mode: "symbol"` layer after the
|
|
145
|
+
* render function changed (e.g. activeState flip). Rebuilds `canvasMarkers`
|
|
146
|
+
* in-place so the draw closure reflects the new style, then triggers draw().
|
|
147
|
+
*/
|
|
148
|
+
private updateSymbolCanvasMarkers;
|
|
149
|
+
/**
|
|
150
|
+
* Update the canvas symbol overlay for an adaptive marker layer by mutating
|
|
151
|
+
* the `canvasMarkers` array in-place (the canvas `draw()` closure captures
|
|
152
|
+
* the same array reference) and triggering an immediate redraw.
|
|
153
|
+
*/
|
|
154
|
+
private updateAdaptiveCanvasMarkers;
|
|
155
|
+
private createMarkerForFeature;
|
|
156
|
+
private createClusterMarkerForFeature;
|
|
157
|
+
/**
|
|
158
|
+
* Create an AdvancedMarkerElement with an empty div as content.
|
|
159
|
+
* Registers a portal descriptor in the target store (`marker` or `cluster`)
|
|
160
|
+
* so `<Layers>` renders React content into the container via
|
|
161
|
+
* `createPortal()` and routes to the matching render config.
|
|
162
|
+
*/
|
|
163
|
+
private createDomMarker;
|
|
164
|
+
/**
|
|
165
|
+
* Adaptive mode: DOM overlays for features whose resolved mode is not `"symbol"`.
|
|
166
|
+
*/
|
|
167
|
+
private createAdaptiveDomOverlays;
|
|
168
|
+
/**
|
|
169
|
+
* Set up a canvas-based OverlayView that draws all symbol markers in a
|
|
170
|
+
* single pass, replacing the per-element Data layer approach.
|
|
171
|
+
* For adaptive mode, DOM-rendered features are excluded (rendered as DOM overlays).
|
|
172
|
+
*/
|
|
173
|
+
private setupCanvasSymbolOverlay;
|
|
174
|
+
/**
|
|
175
|
+
* Set up client-side clustering using @googlemaps/markerclusterer.
|
|
176
|
+
* Creates AdvancedMarkerElement instances for all point features and
|
|
177
|
+
* hands them to a MarkerClusterer which manages their visibility
|
|
178
|
+
* based on zoom level.
|
|
179
|
+
*/
|
|
180
|
+
private setupClientClustering;
|
|
181
|
+
private pushToFeatureMap;
|
|
182
|
+
/**
|
|
183
|
+
* Re-apply all interaction-sensitive styles for every feature in the given
|
|
184
|
+
* shape source. Priority: selected > hovered > base.
|
|
185
|
+
*
|
|
186
|
+
* Called whenever hover, selection, or theme changes on the given handleId.
|
|
187
|
+
* When neither hover nor selection targets a feature, it reverts to its base style.
|
|
188
|
+
*/
|
|
189
|
+
private applyGoogleShapeInteraction;
|
|
190
|
+
private createPolygon;
|
|
191
|
+
private createPolyline;
|
|
192
|
+
/**
|
|
193
|
+
* Fill-only polygon (no stroke) for ADR-0021 fill tiling: the fill comes from
|
|
194
|
+
* clipped geometry while the full outline is drawn separately as polylines.
|
|
195
|
+
* An optional `zIndex` controls the Google Maps rendering order for hover promotion.
|
|
196
|
+
*/
|
|
197
|
+
private createFillOnlyPolygon;
|
|
198
|
+
/**
|
|
199
|
+
* Render a polygonal feature whose fill is tiled (ADR-0021): the visible
|
|
200
|
+
* outline is drawn from the feature's full geometry as polylines (so it stays
|
|
201
|
+
* complete through covering neighbours), and the fill is drawn from the
|
|
202
|
+
* clipped geometry as fill-only polygons. A fully covered loser yields no fill
|
|
203
|
+
* polygon — just its outline.
|
|
204
|
+
*/
|
|
205
|
+
private renderTiledPolygonFeature;
|
|
206
|
+
/**
|
|
207
|
+
* Create invisible polylines tracing each ring of a polygon's paths.
|
|
208
|
+
* Used as hit targets for stroke-only interaction mode — Google natively
|
|
209
|
+
* shows pointer cursor on clickable polylines, giving correct cursor
|
|
210
|
+
* behaviour without manual hit-testing.
|
|
211
|
+
*
|
|
212
|
+
* `geodesic` must match the parent polygon so hit areas align with the
|
|
213
|
+
* visible stroke.
|
|
214
|
+
*/
|
|
215
|
+
private createStrokeHitPolylines;
|
|
216
|
+
private createShapePointMarker;
|
|
217
|
+
private createRoutePolyline;
|
|
218
|
+
private attachMarkerInteractionListeners;
|
|
219
|
+
private attachClusterInteractionListeners;
|
|
220
|
+
private updateClusterMarkerEntity;
|
|
221
|
+
private attachShapeInteractionListeners;
|
|
222
|
+
private attachShapePointInteractionListeners;
|
|
223
|
+
private attachRouteInteractionListeners;
|
|
224
|
+
/**
|
|
225
|
+
* Attach a DOM-level dblclick listener on the map container.
|
|
226
|
+
* Google Maps overlay events suppress the second click and dblclick when
|
|
227
|
+
* disableDoubleClickZoom is true, so we rely on the browser's own dblclick
|
|
228
|
+
* on the container instead.
|
|
229
|
+
*/
|
|
230
|
+
private setupMapClickListener;
|
|
231
|
+
private setupMapContainerDblClickListener;
|
|
232
|
+
/**
|
|
233
|
+
* On shape mousedown, temporarily hold native zoom disabled so the browser
|
|
234
|
+
* can complete its dblclick detection without the map moving the shape.
|
|
235
|
+
* Re-enables after the dblclick threshold elapses.
|
|
236
|
+
*/
|
|
237
|
+
private holdNativeZoomForDblClick;
|
|
238
|
+
/**
|
|
239
|
+
* Temporarily disable the map's built-in double-click zoom so that our
|
|
240
|
+
* custom dblclick handler can call fitBounds without the native zoom
|
|
241
|
+
* overriding it. Re-enables on the next tick.
|
|
242
|
+
*/
|
|
243
|
+
private suppressNativeZoom;
|
|
244
|
+
/** Emit entity interaction event to all subscribed handlers */
|
|
245
|
+
private emitEntityInteraction;
|
|
246
|
+
/**
|
|
247
|
+
* When the Google Maps instance is recreated (e.g. theme change), all
|
|
248
|
+
* existing Google Maps objects lose their reference to the old map.
|
|
249
|
+
* We need to re-create them on the new map.
|
|
250
|
+
*
|
|
251
|
+
* We collect the current source configs from tracked state and re-apply
|
|
252
|
+
* them. Since set*Source calls removeSource first, this cleanly replaces.
|
|
253
|
+
*/
|
|
254
|
+
private reconnectAllSources;
|
|
255
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type AdapterRendererProps } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
import { type ReactElement } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Google Maps Renderer component
|
|
5
|
+
* Wraps the map with APIProvider. Loading/error UI is owned by `@trackunit/react-map` (`createMapComponent`);
|
|
6
|
+
* this module reports API load failure on the adapter and returns `null` until the script is loaded.
|
|
7
|
+
*
|
|
8
|
+
* Subscribes to the adapter's current theme via `useSyncExternalStore` so
|
|
9
|
+
* that when `setTheme()` is called, the component re-renders with the new
|
|
10
|
+
* `mapId` prop, causing `@vis.gl/react-google-maps` to create a fresh
|
|
11
|
+
* `google.maps.Map` instance styled with the correct cloud-based map style.
|
|
12
|
+
*
|
|
13
|
+
* When rendered inside an existing `APIProvider` (e.g. via `GoogleApiProvider`),
|
|
14
|
+
* the renderer skips creating its own `APIProvider`, sharing the Google Maps
|
|
15
|
+
* JS API context. Each map gets a unique `id` prop to avoid registry conflicts.
|
|
16
|
+
*/
|
|
17
|
+
export declare const GoogleMapsRenderer: (props: AdapterRendererProps) => ReactElement;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This library uses @types/google.maps (newer, maintained) instead of @types/googlemaps.
|
|
3
|
+
* This file is included only by react-map's tsconfig; the global Manager typings still use googlemaps.
|
|
4
|
+
*/
|
|
5
|
+
/// <reference types="google.maps" />
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
interface Window {
|
|
9
|
+
google: typeof google;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type GoogleMapsConfig } from "./GoogleMapsAdapterInstance";
|
|
2
|
+
/**
|
|
3
|
+
* Google Maps adapter factory
|
|
4
|
+
*
|
|
5
|
+
* Creates an adapter configuration for Google Maps that can be passed to useMap.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* const map = useMap(googleMapsAdapter({
|
|
10
|
+
* apiKey: process.env.GOOGLE_MAPS_API_KEY,
|
|
11
|
+
* theme: "light",
|
|
12
|
+
* language: "en",
|
|
13
|
+
* }));
|
|
14
|
+
*
|
|
15
|
+
* <map.Map className="h-full w-full">
|
|
16
|
+
* {children}
|
|
17
|
+
* </map.Map>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare const googleMapsAdapter: (config: Readonly<Readonly<{
|
|
21
|
+
language?: string;
|
|
22
|
+
region?: string;
|
|
23
|
+
theme?: import("@trackunit/react-map-adapter-shared").MapTheme;
|
|
24
|
+
initialViewport?: import("@trackunit/react-map-adapter-shared").InitialViewport;
|
|
25
|
+
restrictBounds?: import("@trackunit/geo-json-utils").GeoJsonBbox | null;
|
|
26
|
+
}> & {
|
|
27
|
+
apiKey: string;
|
|
28
|
+
mapInstanceId?: string;
|
|
29
|
+
}>) => import("@trackunit/react-map-adapter-shared").AdapterConfig<Readonly<Readonly<{
|
|
30
|
+
language?: string;
|
|
31
|
+
region?: string;
|
|
32
|
+
theme?: import("@trackunit/react-map-adapter-shared").MapTheme;
|
|
33
|
+
initialViewport?: import("@trackunit/react-map-adapter-shared").InitialViewport;
|
|
34
|
+
restrictBounds?: import("@trackunit/geo-json-utils").GeoJsonBbox | null;
|
|
35
|
+
}> & {
|
|
36
|
+
apiKey: string;
|
|
37
|
+
mapInstanceId?: string;
|
|
38
|
+
}>>;
|
|
39
|
+
export type { GoogleMapsConfig };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { GeoJsonBbox, GeoJsonPosition } from "@trackunit/react-map-adapter-shared";
|
|
2
|
+
/**
|
|
3
|
+
* Convert GeoJSON Position [lng, lat] to Google Maps LatLngLiteral
|
|
4
|
+
*/
|
|
5
|
+
export declare const positionToLatLng: (position: Readonly<GeoJsonPosition>) => google.maps.LatLngLiteral;
|
|
6
|
+
/**
|
|
7
|
+
* Convert Google Maps LatLng to GeoJSON Position [lng, lat]
|
|
8
|
+
* Uses Zod to distinguish between LatLngLiteral (plain object) and LatLng (class instance)
|
|
9
|
+
*/
|
|
10
|
+
export declare const latLngToPosition: (latLng: google.maps.LatLng | google.maps.LatLngLiteral) => GeoJsonPosition;
|
|
11
|
+
/**
|
|
12
|
+
* Convert GeoJSON Bbox [minLon, minLat, maxLon, maxLat] to Google Maps LatLngBoundsLiteral
|
|
13
|
+
*/
|
|
14
|
+
export declare const bboxToLatLngBounds: (bbox: Readonly<GeoJsonBbox>) => google.maps.LatLngBoundsLiteral;
|
|
15
|
+
/**
|
|
16
|
+
* Convert Google Maps LatLngBounds to GeoJSON Bbox [minLon, minLat, maxLon, maxLat]
|
|
17
|
+
*/
|
|
18
|
+
export declare const latLngBoundsToBbox: (bounds: google.maps.LatLngBounds) => GeoJsonBbox;
|
|
19
|
+
/**
|
|
20
|
+
* Convert Google Maps LatLngBoundsLiteral to GeoJSON Bbox
|
|
21
|
+
*/
|
|
22
|
+
export declare const latLngBoundsLiteralToBbox: (bounds: google.maps.LatLngBoundsLiteral) => GeoJsonBbox;
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { GoogleApiProvider } from "./GoogleApiProvider";
|
|
2
|
+
export { googleMapsAdapter, type GoogleMapsConfig } from "./googleMapsAdapter";
|
|
3
|
+
export { GoogleMapsAdapterInstance } from "./GoogleMapsAdapterInstance";
|
|
4
|
+
export { GoogleMapsRenderer } from "./GoogleMapsRenderer";
|