@tileflow/react 0.1.0-alpha.3 → 0.1.0-alpha.31
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 +201 -0
- package/README.md +305 -7
- package/dist/index.d.ts +82 -33
- package/dist/index.js +1343 -360
- package/dist/static.d.ts +8 -4
- package/dist/static.js +202 -49
- package/package.json +21 -16
package/dist/index.js
CHANGED
|
@@ -1,164 +1,651 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// src/boundary-feature-state.ts
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import {
|
|
6
|
+
createBoundaryFeatureState
|
|
7
|
+
} from "@tileflow/core/browser";
|
|
8
|
+
function useBoundaryFeatureState(map, values, options = {}) {
|
|
9
|
+
const { family, labels, state } = options;
|
|
10
|
+
const [controller, setController] = useState(null);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!map) return void 0;
|
|
13
|
+
const created = createBoundaryFeatureState(map, {
|
|
14
|
+
...family === void 0 ? {} : { family },
|
|
15
|
+
...labels === void 0 ? {} : { labels },
|
|
16
|
+
...state === void 0 ? {} : { state }
|
|
17
|
+
});
|
|
18
|
+
setController(created);
|
|
19
|
+
return () => {
|
|
20
|
+
setController(null);
|
|
21
|
+
created.dispose();
|
|
22
|
+
};
|
|
23
|
+
}, [map, family, labels, state]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!controller) return;
|
|
26
|
+
if (values) controller.set(values);
|
|
27
|
+
else controller.clear();
|
|
28
|
+
}, [controller, values]);
|
|
29
|
+
}
|
|
30
|
+
|
|
1
31
|
// src/map.tsx
|
|
2
32
|
import {
|
|
3
|
-
|
|
33
|
+
useCallback,
|
|
34
|
+
useEffect as useEffect2,
|
|
35
|
+
useLayoutEffect,
|
|
4
36
|
useMemo,
|
|
5
37
|
useRef,
|
|
6
|
-
useState
|
|
38
|
+
useState as useState2
|
|
7
39
|
} from "react";
|
|
8
|
-
import
|
|
40
|
+
import { createPortal } from "react-dom";
|
|
9
41
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
+
attachTileflowAtmosphere,
|
|
43
|
+
attachTileflowFairUseNotice,
|
|
44
|
+
attachTileflowMapLifecycle,
|
|
45
|
+
createTileflowSessionStarter,
|
|
46
|
+
createTileflowThemeController,
|
|
47
|
+
createTileflowVegetation,
|
|
48
|
+
createTileflowTransformRequest,
|
|
49
|
+
getTileflowSystemColorScheme,
|
|
50
|
+
loadTileflowStyleFonts,
|
|
51
|
+
registerTileflowContourProtocol,
|
|
52
|
+
registerTileflowPmtilesProtocol,
|
|
53
|
+
registerTileflowWorldRequestBridge,
|
|
54
|
+
subscribeTileflowSystemColorScheme
|
|
55
|
+
} from "@tileflow/core/browser";
|
|
56
|
+
import { normalizeTileflowCaptureId } from "@tileflow/core/capture";
|
|
57
|
+
import {
|
|
58
|
+
createTileflowSessionController,
|
|
59
|
+
defaultTileflowManifestUrl,
|
|
60
|
+
defaultTileflowRuntimeView,
|
|
61
|
+
loadTileflowManifest,
|
|
62
|
+
mergeTileflowAnalytics,
|
|
63
|
+
nearestTileflowBlendTheme,
|
|
64
|
+
normalizeTileflowRuntimeCenter,
|
|
65
|
+
normalizeTileflowStaticImageSize,
|
|
66
|
+
resolveTileflowManifestMap,
|
|
67
|
+
resolveTileflowMapMode,
|
|
68
|
+
resolveTileflowRuntimeStyle,
|
|
69
|
+
resolveTileflowRuntimeTheme,
|
|
70
|
+
resolveTileflowRuntimeThemeBlend,
|
|
71
|
+
resolveTileflowRuntimeView,
|
|
72
|
+
resolveTileflowStaticImageUrl
|
|
73
|
+
} from "@tileflow/core/runtime";
|
|
74
|
+
import {
|
|
75
|
+
createTileflowMapLibreDomRuntime,
|
|
76
|
+
createTileflowMapLibreInteractionCoordinator,
|
|
77
|
+
createTileflowMapLibreSemanticDomRuntime
|
|
78
|
+
} from "@tileflow/interactions/maplibre";
|
|
79
|
+
|
|
80
|
+
// src/annotations.ts
|
|
81
|
+
import {
|
|
82
|
+
validateTileflowAnnotations
|
|
83
|
+
} from "@tileflow/interactions";
|
|
84
|
+
function prepareTileflowReactAnnotations(annotations) {
|
|
85
|
+
const candidates = annotations ?? [];
|
|
86
|
+
const titles = new Map(candidates.map((annotation) => [annotation.id, annotation.ariaLabel]));
|
|
87
|
+
const validation = validateTileflowAnnotations(candidates);
|
|
88
|
+
if (!validation.ok) {
|
|
89
|
+
return {
|
|
90
|
+
annotations: [],
|
|
91
|
+
diagnostics: validation.diagnostics,
|
|
92
|
+
ok: false,
|
|
93
|
+
titles
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
// Keep caller-owned identities after validation so generic data and portal reconciliation stay
|
|
98
|
+
// stable instead of replacing application objects with a schema clone.
|
|
99
|
+
annotations: candidates,
|
|
100
|
+
diagnostics: [],
|
|
101
|
+
ok: true,
|
|
102
|
+
titles
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function imageModeAnnotationDiagnostic(annotationCount) {
|
|
106
|
+
return annotationCount > 0 ? {
|
|
107
|
+
code: "UNSUPPORTED_MODE",
|
|
108
|
+
level: "error",
|
|
109
|
+
message: 'Tileflow annotations require mode="interactive"; image mode cannot render interactive overlays.',
|
|
110
|
+
path: ""
|
|
111
|
+
} : void 0;
|
|
112
|
+
}
|
|
113
|
+
function imageModeMapInteractionDiagnostic(annotationCount, bindingCount, hasAdditionalInteractionConfiguration = false) {
|
|
114
|
+
if (bindingCount === 0 && !hasAdditionalInteractionConfiguration) {
|
|
115
|
+
return imageModeAnnotationDiagnostic(annotationCount);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
code: "UNSUPPORTED_MODE",
|
|
119
|
+
level: "error",
|
|
120
|
+
message: annotationCount > 0 && bindingCount > 0 ? 'Tileflow annotations and interactions require mode="interactive"; image mode cannot render interactive overlays.' : annotationCount > 0 ? 'Tileflow annotations require mode="interactive"; image mode cannot render interactive overlays.' : bindingCount > 0 ? 'Tileflow interactions require mode="interactive"; image mode cannot render interactive overlays.' : 'Tileflow interaction state, callbacks, and renderers require mode="interactive"; image mode cannot render interactive overlays.',
|
|
121
|
+
path: ""
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/interaction-bindings.ts
|
|
126
|
+
import {
|
|
127
|
+
validateTileflowInteractionBindings
|
|
128
|
+
} from "@tileflow/interactions";
|
|
129
|
+
function prepareTileflowReactInteractionBindings(interactions) {
|
|
130
|
+
const validation = validateTileflowInteractionBindings(interactions ?? []);
|
|
131
|
+
return validation.ok ? {
|
|
132
|
+
bindings: validation.bindings,
|
|
133
|
+
diagnostics: [],
|
|
134
|
+
ok: true
|
|
135
|
+
} : {
|
|
136
|
+
diagnostics: validation.diagnostics,
|
|
137
|
+
ok: false
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/interaction-state-input.ts
|
|
142
|
+
import {
|
|
143
|
+
initialTileflowInteractionState,
|
|
144
|
+
tileflowInteractionStateSchema
|
|
145
|
+
} from "@tileflow/interactions";
|
|
146
|
+
function prepareTileflowReactInteractionState(interactionState, defaultInteractionState, controlledOwnership = interactionState !== void 0) {
|
|
147
|
+
const controlled = interactionState !== void 0;
|
|
148
|
+
if (controlled && defaultInteractionState !== void 0) {
|
|
149
|
+
return {
|
|
150
|
+
controlled,
|
|
151
|
+
diagnostics: [
|
|
152
|
+
{
|
|
153
|
+
code: "INVALID_DOCUMENT",
|
|
154
|
+
level: "error",
|
|
155
|
+
message: "Tileflow Map accepts interactionState or defaultInteractionState, not both.",
|
|
156
|
+
path: ""
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
ok: false,
|
|
160
|
+
state: initialTileflowInteractionState
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
if (controlled !== controlledOwnership) {
|
|
164
|
+
return {
|
|
165
|
+
controlled,
|
|
166
|
+
diagnostics: [
|
|
167
|
+
{
|
|
168
|
+
code: "INVALID_DOCUMENT",
|
|
169
|
+
level: "error",
|
|
170
|
+
message: "Tileflow interaction state ownership cannot switch between controlled and uncontrolled.",
|
|
171
|
+
path: ""
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
ok: false,
|
|
175
|
+
state: initialTileflowInteractionState
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const statePath = controlled ? "/interactionState" : "/defaultInteractionState";
|
|
179
|
+
const parsed = tileflowInteractionStateSchema.safeParse(
|
|
180
|
+
interactionState ?? defaultInteractionState ?? initialTileflowInteractionState
|
|
181
|
+
);
|
|
182
|
+
if (parsed.success) {
|
|
183
|
+
return { controlled, diagnostics: [], ok: true, state: parsed.data };
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
controlled,
|
|
187
|
+
diagnostics: parsed.error.issues.map((issue) => ({
|
|
188
|
+
code: "INVALID_DOCUMENT",
|
|
189
|
+
level: "error",
|
|
190
|
+
message: issue.message,
|
|
191
|
+
path: `${statePath}${issue.path.map((segment) => String(segment).replaceAll("~", "~0").replaceAll("/", "~1")).map((segment) => `/${segment}`).join("")}`
|
|
192
|
+
})),
|
|
193
|
+
ok: false,
|
|
194
|
+
state: initialTileflowInteractionState
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/map-style-inputs.ts
|
|
199
|
+
import {
|
|
200
|
+
validateTileflowBrowserThemeBlendSelection
|
|
201
|
+
} from "@tileflow/core/browser";
|
|
202
|
+
import {
|
|
203
|
+
isTileflowTimeOfDay,
|
|
204
|
+
validateTileflowRuntimeSource,
|
|
205
|
+
validateTileflowThemeSelection
|
|
206
|
+
} from "@tileflow/core/runtime";
|
|
207
|
+
function validateTileflowMapStyleInputs(input) {
|
|
208
|
+
const sourceValidation = validateTileflowRuntimeSource(input.source);
|
|
209
|
+
if (!sourceValidation.ok) return sourceValidation;
|
|
210
|
+
if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
|
|
211
|
+
return { error: 'theme must be a concrete portable theme name or "system"', ok: false };
|
|
212
|
+
}
|
|
213
|
+
if (input.timeOfDay !== void 0 && !isTileflowTimeOfDay(input.timeOfDay)) {
|
|
214
|
+
return { error: "timeOfDay must be day, dawn, dusk, or night", ok: false };
|
|
215
|
+
}
|
|
216
|
+
if (input.themeBlend !== void 0 && !validateTileflowBrowserThemeBlendSelection(input.themeBlend)) {
|
|
217
|
+
return {
|
|
218
|
+
error: "themeBlend must name two to eight concrete themes, a position from 0 to themes.length - 1, and an optional boolean hold",
|
|
219
|
+
ok: false
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (input.themeTransition !== void 0 && !isThemeTransition(input.themeTransition)) {
|
|
223
|
+
return {
|
|
224
|
+
error: "themeTransition.duration must be a number of milliseconds from 0 to 5000",
|
|
225
|
+
ok: false
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return { ok: true };
|
|
229
|
+
}
|
|
230
|
+
function isThemeTransition(value) {
|
|
231
|
+
if (!value || typeof value !== "object") return false;
|
|
232
|
+
const { duration } = value;
|
|
233
|
+
return duration === void 0 || typeof duration === "number" && Number.isFinite(duration) && duration >= 0 && duration <= 5e3;
|
|
234
|
+
}
|
|
235
|
+
function assertTileflowMapStyleInputs(input) {
|
|
236
|
+
const validation = validateTileflowMapStyleInputs(input);
|
|
237
|
+
if (!validation.ok) {
|
|
238
|
+
throw new TypeError(`Invalid Tileflow <Map> source: ${validation.error}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/maplibre.ts
|
|
243
|
+
var mapLibrePromise;
|
|
244
|
+
var mapLibre;
|
|
245
|
+
var workerUrl;
|
|
246
|
+
function configureTileflowMapLibre(configuration) {
|
|
247
|
+
const nextWorkerUrl = configuration.workerUrl;
|
|
248
|
+
if (typeof nextWorkerUrl !== "string" || nextWorkerUrl.trim().length === 0) {
|
|
249
|
+
throw new TypeError("Tileflow MapLibre workerUrl must be a non-empty string.");
|
|
250
|
+
}
|
|
251
|
+
if (workerUrl !== void 0 && workerUrl !== nextWorkerUrl) {
|
|
252
|
+
throw new Error("Tileflow MapLibre workerUrl is already configured.");
|
|
253
|
+
}
|
|
254
|
+
workerUrl = nextWorkerUrl;
|
|
255
|
+
mapLibre?.setWorkerUrl(nextWorkerUrl);
|
|
256
|
+
}
|
|
257
|
+
function loadTileflowMapLibre() {
|
|
258
|
+
mapLibrePromise ??= import("maplibre-gl").then(resolveMapLibreModule).then(configureTileflowMapLibreModule).catch((error) => {
|
|
259
|
+
mapLibrePromise = void 0;
|
|
260
|
+
throw error;
|
|
261
|
+
});
|
|
262
|
+
return mapLibrePromise;
|
|
263
|
+
}
|
|
264
|
+
function resolveMapLibreModule(module) {
|
|
265
|
+
const defaultExport = module.default;
|
|
266
|
+
return defaultExport ?? module;
|
|
267
|
+
}
|
|
268
|
+
function configureTileflowMapLibreModule(maplibregl) {
|
|
269
|
+
if (workerUrl !== void 0) maplibregl.setWorkerUrl(workerUrl);
|
|
270
|
+
mapLibre = maplibregl;
|
|
271
|
+
return maplibregl;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/map.tsx
|
|
275
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
276
|
+
var defaultInteractions = [];
|
|
277
|
+
function Map2(props) {
|
|
278
|
+
assertTileflowMapStyleInputs(props);
|
|
279
|
+
const {
|
|
280
|
+
annotations,
|
|
281
|
+
captureId,
|
|
282
|
+
defaultInteractionState,
|
|
283
|
+
mode = "interactive",
|
|
284
|
+
source,
|
|
285
|
+
imageUrl,
|
|
286
|
+
alt = "",
|
|
287
|
+
center,
|
|
288
|
+
zoom,
|
|
289
|
+
className,
|
|
290
|
+
height = 420,
|
|
291
|
+
imageLoading = "eager",
|
|
292
|
+
interactive,
|
|
293
|
+
mapOptions,
|
|
294
|
+
analytics,
|
|
295
|
+
theme,
|
|
296
|
+
timeOfDay,
|
|
297
|
+
themeBlend,
|
|
298
|
+
themeTransition,
|
|
299
|
+
vegetation,
|
|
300
|
+
viewMode = "2d",
|
|
301
|
+
interactionState,
|
|
302
|
+
interactions = defaultInteractions,
|
|
303
|
+
onInteractionDiagnostic,
|
|
304
|
+
onInteractionEvent,
|
|
305
|
+
onInteractionStateChange,
|
|
306
|
+
onLoad,
|
|
307
|
+
renderMarker,
|
|
308
|
+
renderPopup,
|
|
309
|
+
renderTooltip
|
|
310
|
+
} = props;
|
|
311
|
+
const generalRenderPopup = renderPopup;
|
|
312
|
+
const generalRenderTooltip = renderTooltip;
|
|
313
|
+
const sourceMap = source.map;
|
|
314
|
+
const sourceManifestUrl = source.manifestUrl;
|
|
315
|
+
const runtimeSource = useMemo(
|
|
316
|
+
() => ({ manifestUrl: sourceManifestUrl, map: sourceMap }),
|
|
317
|
+
[sourceManifestUrl, sourceMap]
|
|
318
|
+
);
|
|
319
|
+
const mapName = runtimeSource.map;
|
|
320
|
+
const manifestUrl = runtimeSource.manifestUrl ?? defaultTileflowManifestUrl;
|
|
321
|
+
const preparedAnnotations = useMemo(
|
|
322
|
+
() => prepareTileflowReactAnnotations(annotations),
|
|
323
|
+
[annotations]
|
|
324
|
+
);
|
|
325
|
+
const preparedInteractions = useMemo(
|
|
326
|
+
() => prepareTileflowReactInteractionBindings(interactions),
|
|
327
|
+
[interactions]
|
|
328
|
+
);
|
|
329
|
+
const controlledInteractionOwnershipRef = useRef(interactionState !== void 0);
|
|
330
|
+
const preparedInteractionState = useMemo(
|
|
331
|
+
() => prepareTileflowReactInteractionState(
|
|
332
|
+
interactionState,
|
|
333
|
+
defaultInteractionState,
|
|
334
|
+
controlledInteractionOwnershipRef.current
|
|
335
|
+
),
|
|
336
|
+
[defaultInteractionState, interactionState]
|
|
337
|
+
);
|
|
42
338
|
const containerRef = useRef(null);
|
|
339
|
+
const imageRef = useRef(null);
|
|
43
340
|
const mapRef = useRef(null);
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
()
|
|
50
|
-
|
|
341
|
+
const vegetationRef = useRef(void 0);
|
|
342
|
+
const vegetationInputsRef = useRef({ vegetation, viewMode });
|
|
343
|
+
vegetationInputsRef.current = { vegetation, viewMode };
|
|
344
|
+
useEffect2(() => {
|
|
345
|
+
vegetationRef.current?.setVisible(vegetation);
|
|
346
|
+
vegetationRef.current?.setViewMode(viewMode);
|
|
347
|
+
}, [vegetation, viewMode]);
|
|
348
|
+
const themeControllerRef = useRef(void 0);
|
|
349
|
+
const runtimeStyleRef = useRef(null);
|
|
350
|
+
const interactionRuntimeRef = useRef(
|
|
351
|
+
void 0
|
|
352
|
+
);
|
|
353
|
+
const semanticRuntimeRef = useRef(void 0);
|
|
354
|
+
const interactionCoordinatorRef = useRef(
|
|
355
|
+
void 0
|
|
356
|
+
);
|
|
357
|
+
const annotationsRef = useRef(preparedAnnotations.annotations);
|
|
358
|
+
const annotationTitlesRef = useRef(preparedAnnotations.titles);
|
|
359
|
+
const preparedInteractionsRef = useRef(preparedInteractions);
|
|
360
|
+
const coordinatedInteractionStateRef = useRef(preparedInteractionState.state);
|
|
361
|
+
const onInteractionDiagnosticRef = useRef(onInteractionDiagnostic);
|
|
362
|
+
const onInteractionEventRef = useRef(onInteractionEvent);
|
|
363
|
+
const onInteractionStateChangeRef = useRef(onInteractionStateChange);
|
|
364
|
+
const onThemeChangeRef = useRef(props.onThemeChange);
|
|
365
|
+
const renderMarkerRef = useRef(renderMarker);
|
|
366
|
+
const renderPopupRef = useRef(renderPopup);
|
|
367
|
+
const renderTooltipRef = useRef(renderTooltip);
|
|
368
|
+
const reportedDeclarativeDiagnosticKeysRef = useRef(/* @__PURE__ */ new Set());
|
|
369
|
+
const [manifestResolution, setManifestResolution] = useState2({
|
|
370
|
+
key: "",
|
|
371
|
+
map: null,
|
|
372
|
+
state: "loading"
|
|
373
|
+
});
|
|
374
|
+
const [systemColorScheme, setSystemColorScheme] = useState2("light");
|
|
375
|
+
const [imageSize, setImageSize] = useState2(null);
|
|
376
|
+
const [captureState, setCaptureState] = useState2("loading");
|
|
377
|
+
const [themeTransitionState, setThemeTransitionState] = useState2(
|
|
378
|
+
"idle"
|
|
379
|
+
);
|
|
380
|
+
const [activeRuntimeResource, setActiveRuntimeResource] = useState2(void 0);
|
|
381
|
+
const [annotationRenderTargets, setAnnotationRenderTargets] = useState2([]);
|
|
382
|
+
const [semanticRenderTargets, setSemanticRenderTargets] = useState2([]);
|
|
383
|
+
const [annotationBridgeDiagnostic, setAnnotationBridgeDiagnostic] = useState2();
|
|
384
|
+
const [semanticBridgeDiagnostic, setSemanticBridgeDiagnostic] = useState2();
|
|
385
|
+
const [stateBridgeDiagnostic, setStateBridgeDiagnostic] = useState2();
|
|
386
|
+
const [rendererBridgeDiagnostic, setRendererBridgeDiagnostic] = useState2();
|
|
387
|
+
const [annotationDiagnostics, setAnnotationDiagnostics] = useState2([]);
|
|
388
|
+
const [semanticDiagnostics, setSemanticDiagnostics] = useState2([]);
|
|
389
|
+
const stableMapOptions = useStableMapOptions(mapOptions);
|
|
390
|
+
const resolvedCaptureId = normalizeTileflowCaptureId(captureId);
|
|
391
|
+
const resolvedMode = resolveTileflowMapMode({ mode });
|
|
392
|
+
const isImageMode = resolvedMode === "image";
|
|
393
|
+
const manifestRequestKey = JSON.stringify([manifestUrl, mapName]);
|
|
394
|
+
const currentManifestResolution = manifestResolution.key === manifestRequestKey ? manifestResolution : { key: manifestRequestKey, map: null, state: "loading" };
|
|
395
|
+
const manifestMap = currentManifestResolution.state === "ready" ? currentManifestResolution.map : null;
|
|
396
|
+
const themeBlendKey = themeBlend ? JSON.stringify(themeBlend.themes) : void 0;
|
|
397
|
+
const themeBlendPosition = themeBlend?.position;
|
|
398
|
+
const themeBlendHold = themeBlend?.hold === true;
|
|
399
|
+
const blendThemeName = themeBlend ? nearestTileflowBlendTheme(themeBlend) : void 0;
|
|
400
|
+
const themeResolution = useMemo(() => {
|
|
401
|
+
const selection = blendThemeName ?? theme;
|
|
402
|
+
if (!manifestMap) {
|
|
403
|
+
return {
|
|
404
|
+
error: false,
|
|
405
|
+
name: selection && selection !== "system" ? selection : void 0
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
if (themeBlendKey) {
|
|
410
|
+
for (const name of JSON.parse(themeBlendKey)) {
|
|
411
|
+
resolveTileflowRuntimeTheme(manifestMap, name);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return {
|
|
415
|
+
error: false,
|
|
416
|
+
name: resolveTileflowRuntimeTheme(
|
|
417
|
+
manifestMap,
|
|
418
|
+
selection,
|
|
419
|
+
systemColorScheme,
|
|
420
|
+
themeBlendKey ? void 0 : timeOfDay
|
|
421
|
+
).name
|
|
422
|
+
};
|
|
423
|
+
} catch {
|
|
424
|
+
return { error: true, name: void 0 };
|
|
425
|
+
}
|
|
426
|
+
}, [blendThemeName, manifestMap, systemColorScheme, theme, themeBlendKey, timeOfDay]);
|
|
427
|
+
const resolvedThemeName = themeResolution.name;
|
|
428
|
+
const manifestView = resolveTileflowRuntimeView({ manifestMap });
|
|
429
|
+
const manifestCenter = useStableMapOptionValue(
|
|
430
|
+
normalizeTileflowRuntimeCenter(manifestView.center)
|
|
431
|
+
);
|
|
432
|
+
const resolvedCenter = useStableMapOptionValue(
|
|
433
|
+
center ?? stableMapOptions?.center ?? manifestCenter
|
|
434
|
+
);
|
|
435
|
+
const resolvedZoom = zoom ?? stableMapOptions?.zoom ?? manifestView.zoom ?? defaultTileflowRuntimeView.zoom;
|
|
436
|
+
const resolvedBearing = stableMapOptions?.bearing ?? manifestView.bearing;
|
|
437
|
+
const resolvedPitch = stableMapOptions?.pitch ?? manifestView.pitch;
|
|
438
|
+
const resolvedInteractive = interactive ?? stableMapOptions?.interactive ?? true;
|
|
439
|
+
const staticImageCenter = useStableMapOptionValue(
|
|
440
|
+
normalizeTileflowRuntimeCenter(center ?? stableMapOptions?.center, manifestCenter)
|
|
51
441
|
);
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
442
|
+
const staticImageZoom = zoom ?? stableMapOptions?.zoom ?? manifestView.zoom ?? defaultTileflowRuntimeView.zoom;
|
|
443
|
+
const centerRef = useRef(resolvedCenter);
|
|
444
|
+
const zoomRef = useRef(resolvedZoom);
|
|
445
|
+
const bearingRef = useRef(resolvedBearing);
|
|
446
|
+
const pitchRef = useRef(resolvedPitch);
|
|
447
|
+
const onLoadRef = useRef(onLoad);
|
|
448
|
+
const mapNameRef = useRef(mapName);
|
|
449
|
+
const resolvedAnalyticsRef = useRef(void 0);
|
|
450
|
+
const readinessRunRef = useRef(0);
|
|
451
|
+
const hasAdditionalInteractionConfiguration = annotations !== void 0 || defaultInteractionState !== void 0 || interactionState !== void 0 || onInteractionDiagnostic !== void 0 || onInteractionEvent !== void 0 || onInteractionStateChange !== void 0 || renderMarker !== void 0 || renderPopup !== void 0 || renderTooltip !== void 0 || props.interactions !== void 0;
|
|
452
|
+
const modeDiagnostic = useMemo(
|
|
453
|
+
() => isImageMode ? imageModeMapInteractionDiagnostic(
|
|
454
|
+
preparedAnnotations.annotations.length,
|
|
455
|
+
interactions.length,
|
|
456
|
+
hasAdditionalInteractionConfiguration
|
|
457
|
+
) : void 0,
|
|
458
|
+
[
|
|
459
|
+
hasAdditionalInteractionConfiguration,
|
|
460
|
+
interactions.length,
|
|
461
|
+
isImageMode,
|
|
462
|
+
preparedAnnotations.annotations.length
|
|
463
|
+
]
|
|
464
|
+
);
|
|
465
|
+
const declarativeInteractionDiagnostics = useMemo(
|
|
466
|
+
() => [
|
|
467
|
+
modeDiagnostic,
|
|
468
|
+
...preparedAnnotations.diagnostics,
|
|
469
|
+
...preparedInteractions.diagnostics,
|
|
470
|
+
...preparedInteractionState.diagnostics
|
|
471
|
+
].filter(
|
|
472
|
+
(diagnostic) => diagnostic !== void 0
|
|
55
473
|
),
|
|
56
|
-
[
|
|
474
|
+
[
|
|
475
|
+
modeDiagnostic,
|
|
476
|
+
preparedAnnotations.diagnostics,
|
|
477
|
+
preparedInteractions.diagnostics,
|
|
478
|
+
preparedInteractionState.diagnostics
|
|
479
|
+
]
|
|
57
480
|
);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
481
|
+
const interactionDiagnostics = [
|
|
482
|
+
...declarativeInteractionDiagnostics,
|
|
483
|
+
...annotationBridgeDiagnostic ? [annotationBridgeDiagnostic] : [],
|
|
484
|
+
...semanticBridgeDiagnostic ? [semanticBridgeDiagnostic] : [],
|
|
485
|
+
...stateBridgeDiagnostic ? [stateBridgeDiagnostic] : [],
|
|
486
|
+
...rendererBridgeDiagnostic ? [rendererBridgeDiagnostic] : [],
|
|
487
|
+
...annotationDiagnostics,
|
|
488
|
+
...semanticDiagnostics
|
|
489
|
+
];
|
|
490
|
+
const interactionDiagnostic = interactionDiagnostics.find((diagnostic) => diagnostic.level === "error") ?? interactionDiagnostics[0];
|
|
491
|
+
const hasInteractionError = interactionDiagnostics.some(
|
|
492
|
+
(diagnostic) => diagnostic.level === "error"
|
|
493
|
+
);
|
|
494
|
+
const portalKeys = useMemo(
|
|
495
|
+
() => [
|
|
496
|
+
...annotationRenderTargets.map((target) => `annotation:${target.key}`),
|
|
497
|
+
...semanticRenderTargets.map((target) => `semantic:${target.key}`)
|
|
498
|
+
],
|
|
499
|
+
[annotationRenderTargets, semanticRenderTargets]
|
|
500
|
+
);
|
|
501
|
+
const portalReadiness = useTileflowPortalReadiness(portalKeys);
|
|
502
|
+
const interactionReadiness = hasInteractionError ? "error" : portalReadiness.idle ? "idle" : "loading";
|
|
503
|
+
if (preparedAnnotations.ok) {
|
|
504
|
+
annotationsRef.current = preparedAnnotations.annotations;
|
|
505
|
+
annotationTitlesRef.current = preparedAnnotations.titles;
|
|
506
|
+
}
|
|
507
|
+
preparedInteractionsRef.current = preparedInteractions;
|
|
508
|
+
onInteractionDiagnosticRef.current = onInteractionDiagnostic;
|
|
509
|
+
onInteractionEventRef.current = onInteractionEvent;
|
|
510
|
+
onInteractionStateChangeRef.current = onInteractionStateChange;
|
|
511
|
+
renderMarkerRef.current = renderMarker;
|
|
512
|
+
renderPopupRef.current = renderPopup;
|
|
513
|
+
renderTooltipRef.current = renderTooltip;
|
|
514
|
+
onThemeChangeRef.current = props.onThemeChange;
|
|
515
|
+
useEffect2(() => {
|
|
516
|
+
if (theme !== "system") return;
|
|
517
|
+
setSystemColorScheme(getTileflowSystemColorScheme());
|
|
518
|
+
return subscribeTileflowSystemColorScheme(setSystemColorScheme);
|
|
519
|
+
}, [theme]);
|
|
520
|
+
useEffect2(() => {
|
|
521
|
+
const previousKeys = reportedDeclarativeDiagnosticKeysRef.current;
|
|
522
|
+
const nextKeys = /* @__PURE__ */ new Set();
|
|
523
|
+
for (const diagnostic of declarativeInteractionDiagnostics) {
|
|
524
|
+
const key = tileflowInteractionDiagnosticKey(diagnostic);
|
|
525
|
+
nextKeys.add(key);
|
|
526
|
+
if (!previousKeys.has(key)) onInteractionDiagnosticRef.current?.(diagnostic);
|
|
65
527
|
}
|
|
528
|
+
reportedDeclarativeDiagnosticKeysRef.current = nextKeys;
|
|
529
|
+
}, [declarativeInteractionDiagnostics]);
|
|
530
|
+
useEffect2(() => {
|
|
66
531
|
let cancelled = false;
|
|
67
|
-
|
|
532
|
+
setManifestResolution({ key: manifestRequestKey, map: null, state: "loading" });
|
|
533
|
+
loadTileflowManifest(manifestUrl).then((manifest) => {
|
|
68
534
|
if (cancelled) {
|
|
69
535
|
return;
|
|
70
536
|
}
|
|
71
|
-
|
|
537
|
+
const resolvedMap = manifest && mapName ? resolveTileflowManifestMap(manifest, mapName) : null;
|
|
538
|
+
setManifestResolution({
|
|
539
|
+
key: manifestRequestKey,
|
|
540
|
+
map: resolvedMap,
|
|
541
|
+
state: resolvedMap ? "ready" : "error"
|
|
542
|
+
});
|
|
72
543
|
}).catch((error) => {
|
|
73
544
|
if (!cancelled) {
|
|
74
545
|
console.error("Failed to load Tileflow manifest", error);
|
|
75
|
-
|
|
546
|
+
setManifestResolution({ key: manifestRequestKey, map: null, state: "error" });
|
|
76
547
|
}
|
|
77
548
|
});
|
|
78
549
|
return () => {
|
|
79
550
|
cancelled = true;
|
|
80
551
|
};
|
|
81
|
-
}, [
|
|
82
|
-
config,
|
|
83
|
-
imageUrl,
|
|
84
|
-
isImageMode,
|
|
85
|
-
manifestUrl,
|
|
86
|
-
mapName,
|
|
87
|
-
style,
|
|
88
|
-
styleUrl,
|
|
89
|
-
useExplicitStyleBaseUrl
|
|
90
|
-
]);
|
|
552
|
+
}, [manifestRequestKey, manifestUrl, mapName]);
|
|
91
553
|
const runtimeStyle = useMemo(() => {
|
|
92
|
-
if (isImageMode) {
|
|
554
|
+
if (isImageMode || themeResolution.error) {
|
|
93
555
|
return null;
|
|
94
556
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
mapId: manifestMap.mapId,
|
|
113
|
-
styleId: manifestMap.styleId
|
|
114
|
-
},
|
|
115
|
-
style: manifestMap.styleUrl
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
if (useLocalDevFallback) {
|
|
119
|
-
return { style: resolveStyleUrl(mapName) };
|
|
120
|
-
}
|
|
557
|
+
return resolveTileflowRuntimeStyle({
|
|
558
|
+
colorScheme: systemColorScheme,
|
|
559
|
+
manifestMap,
|
|
560
|
+
source: runtimeSource,
|
|
561
|
+
theme: themeResolution.name
|
|
562
|
+
});
|
|
563
|
+
}, [isImageMode, manifestMap, runtimeSource, systemColorScheme, themeResolution]);
|
|
564
|
+
runtimeStyleRef.current = runtimeStyle;
|
|
565
|
+
const blendStyles = useMemo(() => {
|
|
566
|
+
if (isImageMode || !manifestMap || !themeBlendKey) return null;
|
|
567
|
+
try {
|
|
568
|
+
return resolveTileflowRuntimeThemeBlend({
|
|
569
|
+
blend: { position: 0, themes: JSON.parse(themeBlendKey) },
|
|
570
|
+
manifestMap,
|
|
571
|
+
source: runtimeSource
|
|
572
|
+
});
|
|
573
|
+
} catch {
|
|
121
574
|
return null;
|
|
122
575
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
styleBaseUrl,
|
|
131
|
-
styleUrl,
|
|
132
|
-
themes,
|
|
133
|
-
tileBaseUrl,
|
|
134
|
-
useExplicitStyleBaseUrl,
|
|
135
|
-
useLocalDevFallback
|
|
136
|
-
]);
|
|
576
|
+
}, [isImageMode, manifestMap, runtimeSource, themeBlendKey]);
|
|
577
|
+
const blendRequestRef = useRef(null);
|
|
578
|
+
blendRequestRef.current = blendStyles && themeBlendPosition !== void 0 ? { hold: themeBlendHold, position: themeBlendPosition, themes: blendStyles } : null;
|
|
579
|
+
const themeTransitionRef = useRef(themeTransition);
|
|
580
|
+
themeTransitionRef.current = themeTransition;
|
|
581
|
+
const runtimeMapIdentity = `${manifestUrl}\0${runtimeSource.map}`;
|
|
582
|
+
const runtimeStyleReady = runtimeStyle !== null || themeResolution.error && mapRef.current !== null;
|
|
137
583
|
const resolvedAnalytics = useMemo(
|
|
138
|
-
() =>
|
|
584
|
+
() => mergeTileflowAnalytics(analytics, runtimeStyle?.analytics),
|
|
139
585
|
[analytics, runtimeStyle]
|
|
140
586
|
);
|
|
587
|
+
useEffect2(() => {
|
|
588
|
+
centerRef.current = resolvedCenter;
|
|
589
|
+
zoomRef.current = resolvedZoom;
|
|
590
|
+
bearingRef.current = resolvedBearing;
|
|
591
|
+
pitchRef.current = resolvedPitch;
|
|
592
|
+
mapRef.current?.jumpTo({
|
|
593
|
+
bearing: resolvedBearing,
|
|
594
|
+
center: resolvedCenter,
|
|
595
|
+
pitch: resolvedPitch,
|
|
596
|
+
zoom: resolvedZoom
|
|
597
|
+
});
|
|
598
|
+
}, [resolvedBearing, resolvedCenter, resolvedPitch, resolvedZoom]);
|
|
599
|
+
useEffect2(() => {
|
|
600
|
+
onLoadRef.current = onLoad;
|
|
601
|
+
}, [onLoad]);
|
|
602
|
+
useEffect2(() => {
|
|
603
|
+
mapNameRef.current = mapName;
|
|
604
|
+
}, [mapName]);
|
|
605
|
+
useEffect2(() => {
|
|
606
|
+
resolvedAnalyticsRef.current = resolvedAnalytics;
|
|
607
|
+
}, [resolvedAnalytics]);
|
|
141
608
|
const runtimeImageUrl = useMemo(
|
|
142
|
-
() => imageUrl ?? (isImageMode ?
|
|
143
|
-
center,
|
|
609
|
+
() => imageUrl ?? (isImageMode && !themeResolution.error ? resolveTileflowStaticImageUrl({
|
|
610
|
+
center: staticImageCenter,
|
|
611
|
+
colorScheme: systemColorScheme,
|
|
144
612
|
imageSize,
|
|
145
613
|
manifestMap,
|
|
146
|
-
|
|
614
|
+
theme: themeResolution.name,
|
|
615
|
+
zoom: staticImageZoom
|
|
147
616
|
}) : void 0),
|
|
148
|
-
[
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
617
|
+
[
|
|
618
|
+
imageSize,
|
|
619
|
+
imageUrl,
|
|
620
|
+
isImageMode,
|
|
621
|
+
manifestMap,
|
|
622
|
+
staticImageCenter,
|
|
623
|
+
staticImageZoom,
|
|
624
|
+
systemColorScheme,
|
|
625
|
+
themeResolution
|
|
626
|
+
]
|
|
153
627
|
);
|
|
154
|
-
|
|
628
|
+
const runtimeResolutionState = (() => {
|
|
629
|
+
if (currentManifestResolution.state !== "ready") {
|
|
630
|
+
return currentManifestResolution.state === "error" ? "error" : "loading";
|
|
631
|
+
}
|
|
632
|
+
if (themeResolution.error) return "error";
|
|
633
|
+
if (isImageMode) {
|
|
634
|
+
if (runtimeImageUrl) return "idle";
|
|
635
|
+
return imageUrl === void 0 && imageSize === null ? "loading" : "error";
|
|
636
|
+
}
|
|
637
|
+
return runtimeStyle ? "idle" : "error";
|
|
638
|
+
})();
|
|
639
|
+
const currentCaptureState = activeRuntimeResource === (isImageMode ? runtimeImageUrl : runtimeStyle) ? captureState : "loading";
|
|
640
|
+
const effectiveCaptureState = currentCaptureState === "error" || interactionReadiness === "error" || runtimeResolutionState === "error" || themeTransitionState === "error" ? "error" : currentCaptureState === "idle" && interactionReadiness === "idle" && runtimeResolutionState === "idle" && themeTransitionState === "idle" ? "idle" : "loading";
|
|
641
|
+
useEffect2(() => {
|
|
155
642
|
if (!isImageMode || imageUrl || !containerRef.current) {
|
|
156
643
|
setImageSize(null);
|
|
157
644
|
return;
|
|
158
645
|
}
|
|
159
646
|
const element = containerRef.current;
|
|
160
647
|
const updateImageSize = () => {
|
|
161
|
-
const nextSize =
|
|
648
|
+
const nextSize = normalizeTileflowStaticImageSize({
|
|
162
649
|
height: element.clientHeight,
|
|
163
650
|
width: element.clientWidth
|
|
164
651
|
});
|
|
@@ -173,61 +660,464 @@ function Map({
|
|
|
173
660
|
resizeObserver.disconnect();
|
|
174
661
|
};
|
|
175
662
|
}, [imageUrl, isImageMode]);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
663
|
+
useEffect2(() => {
|
|
664
|
+
const runtime = interactionRuntimeRef.current;
|
|
665
|
+
if (!runtime || !preparedAnnotations.ok) return;
|
|
666
|
+
try {
|
|
667
|
+
setAnnotationBridgeDiagnostic(void 0);
|
|
668
|
+
runtime.reconcile(preparedAnnotations.annotations);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
console.error("Failed to reconcile Tileflow annotations", error);
|
|
671
|
+
const diagnostic = createOverlayFailureDiagnostic(
|
|
672
|
+
"Tileflow annotations could not be reconciled."
|
|
673
|
+
);
|
|
674
|
+
setAnnotationBridgeDiagnostic(diagnostic);
|
|
675
|
+
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
179
676
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
677
|
+
}, [preparedAnnotations]);
|
|
678
|
+
useEffect2(() => {
|
|
679
|
+
const runtime = semanticRuntimeRef.current;
|
|
680
|
+
if (!runtime || !preparedInteractions.ok) return;
|
|
681
|
+
try {
|
|
682
|
+
setSemanticBridgeDiagnostic(void 0);
|
|
683
|
+
runtime.reconcile(preparedInteractions.bindings);
|
|
684
|
+
} catch (error) {
|
|
685
|
+
console.error("Failed to reconcile Tileflow semantic interactions", error);
|
|
686
|
+
const diagnostic = createOverlayFailureDiagnostic(
|
|
687
|
+
"Tileflow semantic interactions could not be reconciled."
|
|
688
|
+
);
|
|
689
|
+
setSemanticBridgeDiagnostic(diagnostic);
|
|
690
|
+
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
193
691
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
692
|
+
}, [preparedInteractions]);
|
|
693
|
+
useEffect2(() => {
|
|
694
|
+
if (!preparedInteractionState.controlled || !preparedInteractionState.ok) return;
|
|
695
|
+
try {
|
|
696
|
+
setStateBridgeDiagnostic(void 0);
|
|
697
|
+
interactionCoordinatorRef.current?.setInteractionState(preparedInteractionState.state);
|
|
698
|
+
coordinatedInteractionStateRef.current = preparedInteractionState.state;
|
|
699
|
+
} catch (error) {
|
|
700
|
+
console.error("Failed to update Tileflow interaction state", error);
|
|
701
|
+
const diagnostic = createOverlayFailureDiagnostic(
|
|
702
|
+
"Tileflow interaction state could not be updated."
|
|
703
|
+
);
|
|
704
|
+
setStateBridgeDiagnostic(diagnostic);
|
|
705
|
+
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
706
|
+
}
|
|
707
|
+
}, [preparedInteractionState]);
|
|
708
|
+
useEffect2(() => {
|
|
709
|
+
const annotationRuntime = interactionRuntimeRef.current;
|
|
710
|
+
const semanticRuntime = semanticRuntimeRef.current;
|
|
711
|
+
if (!annotationRuntime && !semanticRuntime) return;
|
|
712
|
+
try {
|
|
713
|
+
setRendererBridgeDiagnostic(void 0);
|
|
714
|
+
annotationRuntime?.setCustomRenderers({
|
|
715
|
+
marker: renderMarker !== void 0,
|
|
716
|
+
popup: renderPopup !== void 0,
|
|
717
|
+
tooltip: renderTooltip !== void 0
|
|
718
|
+
});
|
|
719
|
+
semanticRuntime?.setCustomRenderers({
|
|
720
|
+
popup: renderPopup !== void 0,
|
|
721
|
+
tooltip: renderTooltip !== void 0
|
|
722
|
+
});
|
|
723
|
+
} catch (error) {
|
|
724
|
+
console.error("Failed to update Tileflow interaction renderers", error);
|
|
725
|
+
const diagnostic = createOverlayFailureDiagnostic(
|
|
726
|
+
"Tileflow interaction renderers could not be updated."
|
|
727
|
+
);
|
|
728
|
+
setRendererBridgeDiagnostic(diagnostic);
|
|
729
|
+
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
730
|
+
}
|
|
731
|
+
}, [renderMarker, renderPopup, renderTooltip]);
|
|
732
|
+
useEffect2(() => {
|
|
733
|
+
setThemeTransitionState("idle");
|
|
734
|
+
const initialRuntimeStyle = runtimeStyleRef.current;
|
|
735
|
+
if (!containerRef.current || !initialRuntimeStyle) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
readinessRunRef.current += 1;
|
|
739
|
+
setActiveRuntimeResource(initialRuntimeStyle);
|
|
740
|
+
setCaptureState("loading");
|
|
741
|
+
const container = containerRef.current;
|
|
742
|
+
let cancelled = false;
|
|
743
|
+
let disposeMap;
|
|
744
|
+
void Promise.all([
|
|
745
|
+
loadTileflowMapLibre(),
|
|
746
|
+
loadTileflowStyleFonts(initialRuntimeStyle.style, {
|
|
747
|
+
fontFaces: initialRuntimeStyle.fontFaces
|
|
748
|
+
})
|
|
749
|
+
]).then(([maplibregl]) => {
|
|
750
|
+
if (cancelled || containerRef.current !== container) {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const setupCleanups = [];
|
|
754
|
+
let setupDisposed = false;
|
|
755
|
+
const setupResources = {};
|
|
756
|
+
const registerCleanup = (cleanup) => setupCleanups.push(cleanup);
|
|
757
|
+
disposeMap = () => {
|
|
758
|
+
if (setupDisposed) return;
|
|
759
|
+
setupDisposed = true;
|
|
760
|
+
try {
|
|
761
|
+
runTileflowReactCleanups([...setupCleanups].reverse());
|
|
762
|
+
} finally {
|
|
763
|
+
if (mapRef.current === setupResources.map) mapRef.current = null;
|
|
764
|
+
if (interactionRuntimeRef.current === setupResources.annotationRuntime) {
|
|
765
|
+
interactionRuntimeRef.current = void 0;
|
|
766
|
+
}
|
|
767
|
+
if (semanticRuntimeRef.current === setupResources.semanticRuntime) {
|
|
768
|
+
semanticRuntimeRef.current = void 0;
|
|
769
|
+
}
|
|
770
|
+
if (interactionCoordinatorRef.current === setupResources.coordinator) {
|
|
771
|
+
interactionCoordinatorRef.current = void 0;
|
|
772
|
+
}
|
|
773
|
+
setAnnotationDiagnostics([]);
|
|
774
|
+
setAnnotationRenderTargets([]);
|
|
775
|
+
setAnnotationBridgeDiagnostic(void 0);
|
|
776
|
+
setSemanticDiagnostics([]);
|
|
777
|
+
setSemanticRenderTargets([]);
|
|
778
|
+
setSemanticBridgeDiagnostic(void 0);
|
|
779
|
+
setStateBridgeDiagnostic(void 0);
|
|
780
|
+
setRendererBridgeDiagnostic(void 0);
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
const session = createTileflowSessionController({ source: "react" });
|
|
784
|
+
const sessionStarter = createTileflowSessionStarter({
|
|
785
|
+
getSessionId: () => session.sessionId,
|
|
786
|
+
sessionId: session.sessionId,
|
|
787
|
+
source: "react"
|
|
788
|
+
});
|
|
789
|
+
const fairUseNotice = attachTileflowFairUseNotice(container);
|
|
790
|
+
registerCleanup(() => fairUseNotice.dispose());
|
|
791
|
+
registerTileflowContourProtocol({ addProtocol: maplibregl.addProtocol });
|
|
792
|
+
registerTileflowPmtilesProtocol({ addProtocol: maplibregl.addProtocol });
|
|
793
|
+
const worldRequestBridge = registerTileflowWorldRequestBridge({
|
|
794
|
+
addProtocol: maplibregl.addProtocol,
|
|
795
|
+
onNotice: fairUseNotice.update
|
|
796
|
+
});
|
|
797
|
+
registerCleanup(() => worldRequestBridge.dispose());
|
|
798
|
+
const transformRequest = createTileflowTransformRequest({
|
|
799
|
+
getAnalytics: () => resolvedAnalyticsRef.current,
|
|
800
|
+
sessionController: session,
|
|
801
|
+
sessionId: session.sessionId,
|
|
802
|
+
transformRequest: stableMapOptions?.transformRequest ?? void 0,
|
|
803
|
+
worldRequestBridge
|
|
804
|
+
});
|
|
805
|
+
const map = new maplibregl.Map({
|
|
806
|
+
...stableMapOptions,
|
|
807
|
+
container,
|
|
808
|
+
style: initialRuntimeStyle.style,
|
|
809
|
+
bearing: bearingRef.current,
|
|
810
|
+
center: centerRef.current,
|
|
811
|
+
pitch: pitchRef.current,
|
|
812
|
+
zoom: zoomRef.current,
|
|
813
|
+
interactive: resolvedInteractive,
|
|
814
|
+
attributionControl: stableMapOptions?.attributionControl ?? {
|
|
815
|
+
compact: true
|
|
816
|
+
},
|
|
817
|
+
transformRequest
|
|
818
|
+
});
|
|
819
|
+
mapRef.current = map;
|
|
820
|
+
setupResources.map = map;
|
|
821
|
+
registerCleanup(() => map.remove());
|
|
822
|
+
registerCleanup(attachTileflowAtmosphere(map));
|
|
823
|
+
const themeController = initialRuntimeStyle.theme === void 0 ? void 0 : createTileflowThemeController({
|
|
824
|
+
initial: initialRuntimeStyle,
|
|
825
|
+
map,
|
|
826
|
+
onTransition(transition) {
|
|
827
|
+
onThemeChangeRef.current?.(transition);
|
|
828
|
+
if (transition.phase === "preloading" || transition.phase === "applying") {
|
|
829
|
+
setThemeTransitionState("loading");
|
|
830
|
+
} else if (transition.phase === "error") {
|
|
831
|
+
setThemeTransitionState("error");
|
|
832
|
+
} else {
|
|
833
|
+
setThemeTransitionState("idle");
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
if (themeController) {
|
|
838
|
+
themeControllerRef.current = themeController;
|
|
839
|
+
registerCleanup(() => {
|
|
840
|
+
themeController.dispose();
|
|
841
|
+
if (themeControllerRef.current === themeController) {
|
|
842
|
+
themeControllerRef.current = void 0;
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
const vegetationController = createTileflowVegetation({
|
|
847
|
+
map,
|
|
848
|
+
...themeController ? { controller: themeController } : {},
|
|
849
|
+
visible: vegetationInputsRef.current.vegetation,
|
|
850
|
+
viewMode: vegetationInputsRef.current.viewMode,
|
|
851
|
+
// Direct tile readers use HTTP, not MapLibre's custom World protocol.
|
|
852
|
+
transformRequest: createTileflowTransformRequest({
|
|
853
|
+
getAnalytics: () => resolvedAnalyticsRef.current,
|
|
854
|
+
sessionController: session,
|
|
855
|
+
sessionId: session.sessionId,
|
|
856
|
+
transformRequest: (url) => stableMapOptions?.transformRequest?.(
|
|
857
|
+
url,
|
|
858
|
+
"Tile"
|
|
859
|
+
)
|
|
860
|
+
})
|
|
861
|
+
});
|
|
862
|
+
vegetationRef.current = vegetationController;
|
|
863
|
+
registerCleanup(() => {
|
|
864
|
+
vegetationController.dispose();
|
|
865
|
+
if (vegetationRef.current === vegetationController) vegetationRef.current = void 0;
|
|
866
|
+
});
|
|
867
|
+
const browserWindow = container.ownerDocument.defaultView;
|
|
868
|
+
if (!browserWindow) {
|
|
869
|
+
throw new Error("Tileflow semantic interactions require a browser document.");
|
|
870
|
+
}
|
|
871
|
+
const interactionCoordinator = createTileflowMapLibreInteractionCoordinator({
|
|
872
|
+
...controlledInteractionOwnershipRef.current ? { interactionState: coordinatedInteractionStateRef.current } : { defaultInteractionState: coordinatedInteractionStateRef.current },
|
|
873
|
+
onInteractionStateChange(state) {
|
|
874
|
+
if (!controlledInteractionOwnershipRef.current) {
|
|
875
|
+
coordinatedInteractionStateRef.current = state;
|
|
876
|
+
}
|
|
877
|
+
onInteractionStateChangeRef.current?.(state);
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
interactionCoordinatorRef.current = interactionCoordinator;
|
|
881
|
+
setupResources.coordinator = interactionCoordinator;
|
|
882
|
+
registerCleanup(() => interactionCoordinator.dispose());
|
|
883
|
+
const interactionRuntime = createTileflowMapLibreDomRuntime({
|
|
884
|
+
createMarker({ annotation, element }) {
|
|
885
|
+
const marker = new maplibregl.Marker({ element });
|
|
886
|
+
element.title = annotationTitlesRef.current.get(annotation.id) ?? annotation.ariaLabel;
|
|
887
|
+
return wrapMapLibrePositioned(marker);
|
|
888
|
+
},
|
|
889
|
+
createOverlay({ container: overlayContainer, kind }) {
|
|
890
|
+
const popup = new maplibregl.Popup({
|
|
891
|
+
closeButton: false,
|
|
892
|
+
closeOnClick: false,
|
|
893
|
+
closeOnMove: false,
|
|
894
|
+
focusAfterOpen: false,
|
|
895
|
+
offset: kind === "popup" ? 24 : 18
|
|
896
|
+
}).setDOMContent(overlayContainer);
|
|
897
|
+
return wrapMapLibrePositioned(popup);
|
|
898
|
+
},
|
|
899
|
+
customMarker: renderMarkerRef.current !== void 0,
|
|
900
|
+
customPopup: renderPopupRef.current !== void 0,
|
|
901
|
+
customTooltip: renderTooltipRef.current !== void 0,
|
|
902
|
+
document: container.ownerDocument,
|
|
903
|
+
interactionState: interactionCoordinator.getInteractionState(),
|
|
904
|
+
map,
|
|
905
|
+
onInteractionStateChange: interactionCoordinator.requestInteractionState,
|
|
906
|
+
onDiagnostic(diagnostic) {
|
|
907
|
+
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
908
|
+
},
|
|
909
|
+
updateMarker(_marker, { annotation, element }) {
|
|
910
|
+
element.title = annotationTitlesRef.current.get(annotation.id) ?? annotation.ariaLabel;
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
interactionRuntimeRef.current = interactionRuntime;
|
|
914
|
+
setupResources.annotationRuntime = interactionRuntime;
|
|
915
|
+
registerCleanup(() => interactionRuntime.dispose());
|
|
916
|
+
const semanticRuntime = createTileflowMapLibreSemanticDomRuntime({
|
|
917
|
+
cancelFrame: (frame) => browserWindow.cancelAnimationFrame(frame),
|
|
918
|
+
createOverlay({ container: overlayContainer, kind }) {
|
|
919
|
+
const popup = new maplibregl.Popup({
|
|
920
|
+
closeButton: false,
|
|
921
|
+
closeOnClick: false,
|
|
922
|
+
closeOnMove: false,
|
|
923
|
+
focusAfterOpen: false,
|
|
924
|
+
offset: kind === "popup" ? 24 : 18
|
|
925
|
+
}).setDOMContent(overlayContainer);
|
|
926
|
+
return wrapMapLibrePositioned(popup);
|
|
927
|
+
},
|
|
928
|
+
customPopup: renderPopupRef.current !== void 0,
|
|
929
|
+
customTooltip: renderTooltipRef.current !== void 0,
|
|
930
|
+
document: container.ownerDocument,
|
|
931
|
+
interactionState: interactionCoordinator.getInteractionState(),
|
|
932
|
+
map,
|
|
933
|
+
onDiagnostic(diagnostic) {
|
|
934
|
+
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
935
|
+
},
|
|
936
|
+
onInteractionStateChange: interactionCoordinator.requestInteractionState,
|
|
937
|
+
poiMap: createTileflowMapLibrePoiMap(map),
|
|
938
|
+
requestFrame: (callback) => browserWindow.requestAnimationFrame(() => callback())
|
|
939
|
+
});
|
|
940
|
+
semanticRuntimeRef.current = semanticRuntime;
|
|
941
|
+
setupResources.semanticRuntime = semanticRuntime;
|
|
942
|
+
registerCleanup(() => semanticRuntime.dispose());
|
|
943
|
+
const detachAnnotationRuntime = interactionCoordinator.attach(
|
|
944
|
+
"annotation",
|
|
945
|
+
interactionRuntime
|
|
946
|
+
);
|
|
947
|
+
registerCleanup(detachAnnotationRuntime);
|
|
948
|
+
const detachSemanticRuntime = interactionCoordinator.attach("semantic", semanticRuntime);
|
|
949
|
+
registerCleanup(detachSemanticRuntime);
|
|
950
|
+
const unsubscribeInteractionEvents = interactionRuntime.subscribeEvents((event) => {
|
|
951
|
+
onInteractionEventRef.current?.(event);
|
|
952
|
+
});
|
|
953
|
+
registerCleanup(unsubscribeInteractionEvents);
|
|
954
|
+
const unsubscribeDiagnostics = interactionRuntime.subscribeDiagnostics((diagnostics) => {
|
|
955
|
+
setAnnotationDiagnostics(diagnostics);
|
|
956
|
+
});
|
|
957
|
+
registerCleanup(unsubscribeDiagnostics);
|
|
958
|
+
const unsubscribeRenderTargets = interactionRuntime.subscribeRenderTargets((targets) => {
|
|
959
|
+
setAnnotationRenderTargets(targets);
|
|
960
|
+
});
|
|
961
|
+
registerCleanup(unsubscribeRenderTargets);
|
|
962
|
+
const unsubscribeSemanticEvents = semanticRuntime.subscribeEvents((event) => {
|
|
963
|
+
if (event.target.kind === "annotation") return;
|
|
964
|
+
onInteractionEventRef.current?.({ ...event, target: event.target });
|
|
965
|
+
});
|
|
966
|
+
registerCleanup(unsubscribeSemanticEvents);
|
|
967
|
+
const unsubscribeSemanticDiagnostics = semanticRuntime.subscribeDiagnostics(
|
|
968
|
+
(diagnostics) => {
|
|
969
|
+
setSemanticDiagnostics(diagnostics);
|
|
970
|
+
}
|
|
971
|
+
);
|
|
972
|
+
registerCleanup(unsubscribeSemanticDiagnostics);
|
|
973
|
+
const unsubscribeSemanticRenderTargets = semanticRuntime.subscribeRenderTargets(
|
|
974
|
+
(targets) => {
|
|
975
|
+
setSemanticRenderTargets(targets);
|
|
976
|
+
}
|
|
977
|
+
);
|
|
978
|
+
registerCleanup(unsubscribeSemanticRenderTargets);
|
|
979
|
+
setAnnotationDiagnostics(interactionRuntime.getDiagnostics());
|
|
980
|
+
setAnnotationRenderTargets(interactionRuntime.getRenderTargets());
|
|
981
|
+
setSemanticDiagnostics(semanticRuntime.getDiagnostics());
|
|
982
|
+
setSemanticRenderTargets(semanticRuntime.getRenderTargets());
|
|
983
|
+
try {
|
|
984
|
+
setAnnotationBridgeDiagnostic(void 0);
|
|
985
|
+
interactionRuntime.reconcile(annotationsRef.current);
|
|
986
|
+
} catch (error) {
|
|
987
|
+
console.error("Failed to reconcile Tileflow annotations", error);
|
|
988
|
+
const diagnostic = createOverlayFailureDiagnostic(
|
|
989
|
+
"Tileflow annotations could not be reconciled."
|
|
990
|
+
);
|
|
991
|
+
setAnnotationBridgeDiagnostic(diagnostic);
|
|
992
|
+
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
993
|
+
}
|
|
994
|
+
const initialInteractions = preparedInteractionsRef.current;
|
|
995
|
+
if (initialInteractions.ok) {
|
|
996
|
+
try {
|
|
997
|
+
setSemanticBridgeDiagnostic(void 0);
|
|
998
|
+
semanticRuntime.reconcile(initialInteractions.bindings);
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
console.error("Failed to reconcile Tileflow semantic interactions", error);
|
|
1001
|
+
const diagnostic = createOverlayFailureDiagnostic(
|
|
1002
|
+
"Tileflow semantic interactions could not be reconciled."
|
|
1003
|
+
);
|
|
1004
|
+
setSemanticBridgeDiagnostic(diagnostic);
|
|
1005
|
+
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
const controls = resolvedInteractive ? new maplibregl.NavigationControl() : null;
|
|
1009
|
+
if (controls) {
|
|
1010
|
+
map.addControl(controls, "top-right");
|
|
1011
|
+
}
|
|
1012
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
1013
|
+
map.resize();
|
|
1014
|
+
});
|
|
1015
|
+
registerCleanup(() => resizeObserver.disconnect());
|
|
1016
|
+
resizeObserver.observe(container);
|
|
1017
|
+
const lifecycle = attachTileflowMapLifecycle({
|
|
1018
|
+
getSession: () => {
|
|
1019
|
+
const analyticsForLoad = resolvedAnalyticsRef.current;
|
|
1020
|
+
return {
|
|
1021
|
+
analytics: analyticsForLoad,
|
|
1022
|
+
styleId: analyticsForLoad?.styleId ?? (typeof initialRuntimeStyle.style === "string" ? initialRuntimeStyle.style : mapNameRef.current)
|
|
1023
|
+
};
|
|
1024
|
+
},
|
|
1025
|
+
map,
|
|
1026
|
+
onLoad: (loadedMap) => {
|
|
1027
|
+
onLoadRef.current?.(loadedMap);
|
|
1028
|
+
},
|
|
1029
|
+
scheduler: {
|
|
1030
|
+
cancelFrame: (frame) => cancelAnimationFrame(frame),
|
|
1031
|
+
requestFrame: (callback) => requestAnimationFrame(callback)
|
|
1032
|
+
},
|
|
1033
|
+
sessionStarter,
|
|
1034
|
+
setState: setCaptureState,
|
|
1035
|
+
subscribe: (subscribedMap, event, listener) => {
|
|
1036
|
+
const subscription = subscribedMap.on(event, listener);
|
|
1037
|
+
return () => subscription.unsubscribe();
|
|
1038
|
+
}
|
|
211
1039
|
});
|
|
1040
|
+
registerCleanup(() => lifecycle.dispose());
|
|
1041
|
+
const latestRuntimeStyle = runtimeStyleRef.current;
|
|
1042
|
+
const latestBlend = blendRequestRef.current;
|
|
1043
|
+
if (themeController && latestBlend) {
|
|
1044
|
+
if (latestRuntimeStyle) setActiveRuntimeResource(latestRuntimeStyle);
|
|
1045
|
+
void themeController.setBlend(latestBlend, { transition: themeTransitionRef.current });
|
|
1046
|
+
} else if (themeController && latestRuntimeStyle && latestRuntimeStyle !== initialRuntimeStyle) {
|
|
1047
|
+
setActiveRuntimeResource(latestRuntimeStyle);
|
|
1048
|
+
void themeController.setTheme(latestRuntimeStyle, {
|
|
1049
|
+
transition: themeTransitionRef.current
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
}).catch((error) => {
|
|
1053
|
+
try {
|
|
1054
|
+
disposeMap?.();
|
|
1055
|
+
} catch (cleanupError) {
|
|
1056
|
+
console.error("Failed to clean up the Tileflow map runtime", cleanupError);
|
|
1057
|
+
}
|
|
1058
|
+
if (!cancelled) {
|
|
1059
|
+
console.error("Failed to load the Tileflow map runtime", error);
|
|
1060
|
+
setCaptureState("error");
|
|
1061
|
+
}
|
|
212
1062
|
});
|
|
213
1063
|
return () => {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
mapRef.current = null;
|
|
1064
|
+
cancelled = true;
|
|
1065
|
+
readinessRunRef.current += 1;
|
|
1066
|
+
disposeMap?.();
|
|
218
1067
|
};
|
|
219
|
-
}, [
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
1068
|
+
}, [resolvedInteractive, runtimeMapIdentity, runtimeStyleReady, stableMapOptions]);
|
|
1069
|
+
useEffect2(() => {
|
|
1070
|
+
const controller = themeControllerRef.current;
|
|
1071
|
+
if (!controller || themeBlendKey) return;
|
|
1072
|
+
if (themeResolution.error) {
|
|
1073
|
+
void controller.setTheme(controller.getCurrent());
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
if (!runtimeStyle || controller.getCurrent() === runtimeStyle && !controller.getBlend())
|
|
1077
|
+
return;
|
|
1078
|
+
setActiveRuntimeResource(runtimeStyle);
|
|
1079
|
+
void controller.setTheme(runtimeStyle, { transition: themeTransitionRef.current }).then((result) => {
|
|
1080
|
+
if (result.status === "failed") {
|
|
1081
|
+
console.error("Failed to change the Tileflow map theme", result.error);
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
}, [runtimeStyle, themeBlendKey, themeResolution.error]);
|
|
1085
|
+
useEffect2(() => {
|
|
1086
|
+
const controller = themeControllerRef.current;
|
|
1087
|
+
if (!controller || !blendStyles || themeBlendPosition === void 0) return;
|
|
1088
|
+
if (runtimeStyle) setActiveRuntimeResource(runtimeStyle);
|
|
1089
|
+
void controller.setBlend(
|
|
1090
|
+
{ hold: themeBlendHold, position: themeBlendPosition, themes: blendStyles },
|
|
1091
|
+
{ transition: themeTransitionRef.current }
|
|
1092
|
+
).then((result) => {
|
|
1093
|
+
if (result.status === "failed") {
|
|
1094
|
+
console.error("Failed to blend the Tileflow map themes", result.error);
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
}, [blendStyles, runtimeStyle, themeBlendHold, themeBlendPosition]);
|
|
1098
|
+
useLayoutEffect(() => {
|
|
1099
|
+
if (!isImageMode) return;
|
|
1100
|
+
readinessRunRef.current += 1;
|
|
1101
|
+
setActiveRuntimeResource(runtimeImageUrl);
|
|
1102
|
+
setCaptureState("loading");
|
|
1103
|
+
const image = imageRef.current;
|
|
1104
|
+
if (image?.complete) {
|
|
1105
|
+
if (image.naturalWidth === 0) setCaptureState("error");
|
|
1106
|
+
else void markImageReady(image);
|
|
1107
|
+
}
|
|
1108
|
+
}, [isImageMode, runtimeImageUrl]);
|
|
1109
|
+
async function markImageReady(image) {
|
|
1110
|
+
const run = ++readinessRunRef.current;
|
|
1111
|
+
try {
|
|
1112
|
+
if (typeof image.decode === "function") await image.decode();
|
|
1113
|
+
} catch {
|
|
1114
|
+
if (!image.complete || image.naturalWidth === 0) {
|
|
1115
|
+
if (readinessRunRef.current === run) setCaptureState("error");
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
if (readinessRunRef.current === run) setCaptureState("idle");
|
|
1120
|
+
}
|
|
231
1121
|
const frameStyle = {
|
|
232
1122
|
height,
|
|
233
1123
|
minHeight: 240,
|
|
@@ -235,228 +1125,321 @@ function Map({
|
|
|
235
1125
|
position: "relative",
|
|
236
1126
|
width: "100%"
|
|
237
1127
|
};
|
|
1128
|
+
const diagnosticView = interactionDiagnostic ? /* @__PURE__ */ jsx(
|
|
1129
|
+
"div",
|
|
1130
|
+
{
|
|
1131
|
+
"data-tileflow-interaction-diagnostic": interactionDiagnostic.code,
|
|
1132
|
+
role: "alert",
|
|
1133
|
+
style: interactionDiagnosticStyle,
|
|
1134
|
+
children: interactionDiagnostic.message
|
|
1135
|
+
}
|
|
1136
|
+
) : null;
|
|
238
1137
|
if (isImageMode) {
|
|
239
|
-
return /* @__PURE__ */
|
|
240
|
-
"
|
|
1138
|
+
return /* @__PURE__ */ jsxs(
|
|
1139
|
+
"div",
|
|
241
1140
|
{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
1141
|
+
className,
|
|
1142
|
+
"data-tileflow-capture-id": resolvedCaptureId,
|
|
1143
|
+
"data-tileflow-map": mapName,
|
|
1144
|
+
"data-tileflow-theme": resolvedThemeName,
|
|
1145
|
+
"data-tileflow-state": effectiveCaptureState,
|
|
1146
|
+
ref: containerRef,
|
|
1147
|
+
style: frameStyle,
|
|
1148
|
+
children: [
|
|
1149
|
+
runtimeImageUrl ? /* @__PURE__ */ jsx(
|
|
1150
|
+
"img",
|
|
1151
|
+
{
|
|
1152
|
+
alt,
|
|
1153
|
+
decoding: "async",
|
|
1154
|
+
loading: imageLoading,
|
|
1155
|
+
onError: () => {
|
|
1156
|
+
readinessRunRef.current += 1;
|
|
1157
|
+
setCaptureState("error");
|
|
1158
|
+
},
|
|
1159
|
+
onLoad: (event) => void markImageReady(event.currentTarget),
|
|
1160
|
+
ref: imageRef,
|
|
1161
|
+
src: runtimeImageUrl,
|
|
1162
|
+
style: {
|
|
1163
|
+
display: "block",
|
|
1164
|
+
height: "100%",
|
|
1165
|
+
objectFit: "cover",
|
|
1166
|
+
width: "100%"
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
) : null,
|
|
1170
|
+
diagnosticView
|
|
1171
|
+
]
|
|
252
1172
|
}
|
|
253
|
-
)
|
|
1173
|
+
);
|
|
254
1174
|
}
|
|
255
|
-
return /* @__PURE__ */
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
1175
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1176
|
+
/* @__PURE__ */ jsxs(
|
|
1177
|
+
"div",
|
|
1178
|
+
{
|
|
1179
|
+
className,
|
|
1180
|
+
"data-tileflow-capture-id": resolvedCaptureId,
|
|
1181
|
+
"data-tileflow-map": mapName,
|
|
1182
|
+
"data-tileflow-theme": resolvedThemeName,
|
|
1183
|
+
"data-tileflow-state": effectiveCaptureState,
|
|
1184
|
+
style: frameStyle,
|
|
1185
|
+
children: [
|
|
1186
|
+
/* @__PURE__ */ jsx("div", { ref: containerRef, style: mapContainerStyle }),
|
|
1187
|
+
diagnosticView
|
|
1188
|
+
]
|
|
1189
|
+
}
|
|
1190
|
+
),
|
|
1191
|
+
annotationRenderTargets.map((target) => {
|
|
1192
|
+
const portalKey = `annotation:${target.key}`;
|
|
1193
|
+
return createPortal(
|
|
1194
|
+
/* @__PURE__ */ jsx(
|
|
1195
|
+
TileflowAnnotationPortal,
|
|
1196
|
+
{
|
|
1197
|
+
onCommit: portalReadiness.markCommitted,
|
|
1198
|
+
portalKey,
|
|
1199
|
+
readinessToken: portalReadiness.token,
|
|
1200
|
+
renderMarker,
|
|
1201
|
+
renderPopup: generalRenderPopup,
|
|
1202
|
+
renderTarget: target,
|
|
1203
|
+
renderTooltip: generalRenderTooltip
|
|
1204
|
+
}
|
|
1205
|
+
),
|
|
1206
|
+
target.container,
|
|
1207
|
+
portalKey
|
|
1208
|
+
);
|
|
1209
|
+
}),
|
|
1210
|
+
semanticRenderTargets.map((target) => {
|
|
1211
|
+
const portalKey = `semantic:${target.key}`;
|
|
1212
|
+
return createPortal(
|
|
1213
|
+
/* @__PURE__ */ jsx(
|
|
1214
|
+
TileflowSemanticPortal,
|
|
1215
|
+
{
|
|
1216
|
+
onCommit: portalReadiness.markCommitted,
|
|
1217
|
+
portalKey,
|
|
1218
|
+
readinessToken: portalReadiness.token,
|
|
1219
|
+
renderPopup: generalRenderPopup,
|
|
1220
|
+
renderTarget: target,
|
|
1221
|
+
renderTooltip: generalRenderTooltip
|
|
1222
|
+
}
|
|
1223
|
+
),
|
|
1224
|
+
target.container,
|
|
1225
|
+
portalKey
|
|
1226
|
+
);
|
|
1227
|
+
})
|
|
1228
|
+
] });
|
|
260
1229
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
1230
|
+
var mapContainerStyle = {
|
|
1231
|
+
inset: 0,
|
|
1232
|
+
position: "absolute"
|
|
1233
|
+
};
|
|
1234
|
+
var interactionDiagnosticStyle = {
|
|
1235
|
+
background: "#7f1d1d",
|
|
1236
|
+
border: "1px solid #fecaca",
|
|
1237
|
+
borderRadius: 4,
|
|
1238
|
+
color: "#ffffff",
|
|
1239
|
+
fontFamily: "system-ui, sans-serif",
|
|
1240
|
+
fontSize: 13,
|
|
1241
|
+
left: 12,
|
|
1242
|
+
maxWidth: "calc(100% - 24px)",
|
|
1243
|
+
padding: "8px 10px",
|
|
1244
|
+
position: "absolute",
|
|
1245
|
+
top: 12,
|
|
1246
|
+
zIndex: 10
|
|
1247
|
+
};
|
|
1248
|
+
function TileflowAnnotationPortal({
|
|
1249
|
+
onCommit,
|
|
1250
|
+
portalKey,
|
|
1251
|
+
readinessToken,
|
|
1252
|
+
renderMarker,
|
|
1253
|
+
renderPopup,
|
|
1254
|
+
renderTarget,
|
|
1255
|
+
renderTooltip
|
|
1256
|
+
}) {
|
|
1257
|
+
useLayoutEffect(() => {
|
|
1258
|
+
onCommit(readinessToken, portalKey);
|
|
1259
|
+
}, [onCommit, portalKey, readinessToken]);
|
|
1260
|
+
const { annotation } = renderTarget;
|
|
1261
|
+
const content = renderTarget.kind === "marker" ? annotation.marker?.content : renderTarget.kind === "tooltip" ? annotation.tooltip?.content : annotation.popup?.content;
|
|
1262
|
+
const context = {
|
|
1263
|
+
annotation,
|
|
1264
|
+
close: renderTarget.close,
|
|
1265
|
+
...content ? { content } : {},
|
|
1266
|
+
target: {
|
|
1267
|
+
annotation,
|
|
1268
|
+
coordinate: annotation.coordinate,
|
|
1269
|
+
kind: "annotation"
|
|
1270
|
+
},
|
|
1271
|
+
...content?.kind === "view" ? { viewName: content.name } : {}
|
|
1272
|
+
};
|
|
1273
|
+
if (renderTarget.kind === "marker") return renderMarker?.(context) ?? null;
|
|
1274
|
+
const renderer = renderTarget.kind === "tooltip" ? renderTooltip : renderPopup;
|
|
1275
|
+
return renderer?.(context) ?? null;
|
|
264
1276
|
}
|
|
265
|
-
function
|
|
266
|
-
|
|
1277
|
+
function TileflowSemanticPortal({
|
|
1278
|
+
onCommit,
|
|
1279
|
+
portalKey,
|
|
1280
|
+
readinessToken,
|
|
1281
|
+
renderPopup,
|
|
1282
|
+
renderTarget,
|
|
1283
|
+
renderTooltip
|
|
1284
|
+
}) {
|
|
1285
|
+
useLayoutEffect(() => {
|
|
1286
|
+
onCommit(readinessToken, portalKey);
|
|
1287
|
+
}, [onCommit, portalKey, readinessToken]);
|
|
1288
|
+
const context = {
|
|
1289
|
+
close: renderTarget.close,
|
|
1290
|
+
content: renderTarget.content,
|
|
1291
|
+
target: renderTarget.target,
|
|
1292
|
+
...renderTarget.viewName ? { viewName: renderTarget.viewName } : {}
|
|
1293
|
+
};
|
|
1294
|
+
const renderer = renderTarget.kind === "tooltip" ? renderTooltip : renderPopup;
|
|
1295
|
+
return renderer?.(context) ?? null;
|
|
267
1296
|
}
|
|
268
|
-
function
|
|
269
|
-
|
|
270
|
-
|
|
1297
|
+
function useTileflowPortalReadiness(portalKeys) {
|
|
1298
|
+
const signature = JSON.stringify([...portalKeys].sort());
|
|
1299
|
+
const versionRef = useRef({ signature, version: 0 });
|
|
1300
|
+
if (versionRef.current.signature !== signature) {
|
|
1301
|
+
versionRef.current = { signature, version: versionRef.current.version + 1 };
|
|
271
1302
|
}
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return void 0;
|
|
1303
|
+
const token = `${versionRef.current.version}:${signature}`;
|
|
1304
|
+
const expectedKeysRef = useRef({ keys: [...portalKeys].sort(), token });
|
|
1305
|
+
if (expectedKeysRef.current.token !== token) {
|
|
1306
|
+
expectedKeysRef.current = { keys: [...portalKeys].sort(), token };
|
|
277
1307
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
1308
|
+
const expectedKeys = expectedKeysRef.current.keys;
|
|
1309
|
+
const [committed, setCommitted] = useState2(() => ({
|
|
1310
|
+
keys: /* @__PURE__ */ new Set(),
|
|
1311
|
+
token
|
|
1312
|
+
}));
|
|
1313
|
+
const [readyToken, setReadyToken] = useState2(token);
|
|
1314
|
+
const readinessRunRef = useRef(0);
|
|
1315
|
+
const markCommitted = useCallback((committedToken, portalKey) => {
|
|
1316
|
+
setCommitted((current) => {
|
|
1317
|
+
if (current.token !== committedToken) {
|
|
1318
|
+
return { keys: /* @__PURE__ */ new Set([portalKey]), token: committedToken };
|
|
1319
|
+
}
|
|
1320
|
+
if (current.keys.has(portalKey)) return current;
|
|
1321
|
+
return { keys: /* @__PURE__ */ new Set([...current.keys, portalKey]), token: committedToken };
|
|
1322
|
+
});
|
|
1323
|
+
}, []);
|
|
1324
|
+
useLayoutEffect(() => {
|
|
1325
|
+
setCommitted((current) => current.token === token ? current : { keys: /* @__PURE__ */ new Set(), token });
|
|
1326
|
+
}, [token]);
|
|
1327
|
+
useLayoutEffect(() => {
|
|
1328
|
+
if (readyToken === token || committed.token !== token || !expectedKeys.every((key) => committed.keys.has(key))) {
|
|
1329
|
+
return;
|
|
287
1330
|
}
|
|
288
|
-
|
|
289
|
-
|
|
1331
|
+
const browserWindow = globalThis.window;
|
|
1332
|
+
const run = ++readinessRunRef.current;
|
|
1333
|
+
let secondFrame;
|
|
1334
|
+
const firstFrame = browserWindow.requestAnimationFrame(() => {
|
|
1335
|
+
secondFrame = browserWindow.requestAnimationFrame(() => {
|
|
1336
|
+
if (readinessRunRef.current === run) setReadyToken(token);
|
|
1337
|
+
});
|
|
1338
|
+
});
|
|
1339
|
+
return () => {
|
|
1340
|
+
readinessRunRef.current += 1;
|
|
1341
|
+
browserWindow.cancelAnimationFrame(firstFrame);
|
|
1342
|
+
if (secondFrame !== void 0) browserWindow.cancelAnimationFrame(secondFrame);
|
|
1343
|
+
};
|
|
1344
|
+
}, [committed, expectedKeys, readyToken, token]);
|
|
1345
|
+
return { idle: readyToken === token, markCommitted, token };
|
|
1346
|
+
}
|
|
1347
|
+
function wrapMapLibrePositioned(positioned) {
|
|
1348
|
+
return {
|
|
1349
|
+
addTo(map) {
|
|
1350
|
+
positioned.addTo(map);
|
|
1351
|
+
},
|
|
1352
|
+
remove() {
|
|
1353
|
+
positioned.remove();
|
|
1354
|
+
},
|
|
1355
|
+
setLngLat(coordinate) {
|
|
1356
|
+
positioned.setLngLat([coordinate[0], coordinate[1]]);
|
|
290
1357
|
}
|
|
291
|
-
return { url: nextUrl.toString() };
|
|
292
1358
|
};
|
|
293
1359
|
}
|
|
294
|
-
function
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
mapId: input.mapId,
|
|
301
|
-
metadata: analytics.metadata,
|
|
302
|
-
referrer: typeof document === "undefined" ? void 0 : document.referrer || "",
|
|
303
|
-
sdkVersion: analytics.sdkVersion,
|
|
304
|
-
sessionId: input.sessionId,
|
|
305
|
-
source: analytics.source ?? "react",
|
|
306
|
-
styleId: input.styleId,
|
|
307
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
308
|
-
});
|
|
309
|
-
const blob = new Blob([payload], { type: "application/json" });
|
|
310
|
-
if (navigator.sendBeacon?.(endpoint, blob)) {
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
void fetch(endpoint, {
|
|
314
|
-
body: payload,
|
|
315
|
-
credentials: "omit",
|
|
316
|
-
headers: {
|
|
317
|
-
"Content-Type": "application/json"
|
|
1360
|
+
function createTileflowMapLibrePoiMap(map) {
|
|
1361
|
+
const poiMap = map;
|
|
1362
|
+
return {
|
|
1363
|
+
getStyle: () => map.getStyle(),
|
|
1364
|
+
on(event, listener) {
|
|
1365
|
+
return poiMap.on(event, listener);
|
|
318
1366
|
},
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
return normalizeUrl(analytics.apiUrl);
|
|
326
|
-
}
|
|
327
|
-
if (analytics.endpoint) {
|
|
328
|
-
const url = new URL(analytics.endpoint);
|
|
329
|
-
return url.origin;
|
|
330
|
-
}
|
|
331
|
-
return "https://api.tileflow.dev";
|
|
1367
|
+
queryRenderedFeatures(point, options) {
|
|
1368
|
+
return map.queryRenderedFeatures(point, {
|
|
1369
|
+
layers: [...options.layers]
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
332
1373
|
}
|
|
333
|
-
function
|
|
334
|
-
if (userAnalytics?.enabled === false) {
|
|
335
|
-
return userAnalytics;
|
|
336
|
-
}
|
|
337
|
-
if (!userAnalytics && !runtimeAnalytics?.mapId) {
|
|
338
|
-
return void 0;
|
|
339
|
-
}
|
|
1374
|
+
function createOverlayFailureDiagnostic(message) {
|
|
340
1375
|
return {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
styleId: userAnalytics?.styleId ?? runtimeAnalytics?.styleId
|
|
1376
|
+
code: "OVERLAY_FAILURE",
|
|
1377
|
+
level: "error",
|
|
1378
|
+
message,
|
|
1379
|
+
path: ""
|
|
346
1380
|
};
|
|
347
1381
|
}
|
|
348
|
-
function
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
1382
|
+
function tileflowInteractionDiagnosticKey(diagnostic) {
|
|
1383
|
+
return [
|
|
1384
|
+
diagnostic.code,
|
|
1385
|
+
diagnostic.level,
|
|
1386
|
+
diagnostic.path ?? "",
|
|
1387
|
+
diagnostic.target ? JSON.stringify(diagnostic.target) : "",
|
|
1388
|
+
diagnostic.message
|
|
1389
|
+
].join("\0");
|
|
356
1390
|
}
|
|
357
|
-
function
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
1391
|
+
function runTileflowReactCleanups(cleanups) {
|
|
1392
|
+
const errors = [];
|
|
1393
|
+
for (const cleanup of cleanups) {
|
|
1394
|
+
try {
|
|
1395
|
+
cleanup();
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
errors.push(error);
|
|
1398
|
+
}
|
|
363
1399
|
}
|
|
364
|
-
|
|
1400
|
+
if (errors.length === 1) throw errors[0];
|
|
1401
|
+
if (errors.length > 1) throw new AggregateError(errors, "Tileflow React cleanup failed.");
|
|
365
1402
|
}
|
|
366
|
-
function
|
|
367
|
-
|
|
368
|
-
manifestCache.set(
|
|
369
|
-
url,
|
|
370
|
-
fetch(url, { cache: "no-store" }).then(async (response) => {
|
|
371
|
-
if (response.status === 404) {
|
|
372
|
-
return null;
|
|
373
|
-
}
|
|
374
|
-
if (!response.ok) {
|
|
375
|
-
throw new Error(`Tileflow manifest failed: ${response.status}`);
|
|
376
|
-
}
|
|
377
|
-
return await response.json();
|
|
378
|
-
})
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
return manifestCache.get(url);
|
|
1403
|
+
function useStableMapOptions(options) {
|
|
1404
|
+
return useStableMapOptionValue(options);
|
|
382
1405
|
}
|
|
383
|
-
function
|
|
384
|
-
const
|
|
385
|
-
if (!
|
|
386
|
-
|
|
1406
|
+
function useStableMapOptionValue(value) {
|
|
1407
|
+
const valueRef = useRef(value);
|
|
1408
|
+
if (!areEquivalentMapOptionValues(valueRef.current, value)) {
|
|
1409
|
+
valueRef.current = value;
|
|
387
1410
|
}
|
|
388
|
-
|
|
389
|
-
const analytics2 = inferAnalyticsFromStyleUrl(entry);
|
|
390
|
-
return {
|
|
391
|
-
apiUrl: analytics2?.apiUrl ?? manifest.apiUrl,
|
|
392
|
-
mapId: analytics2?.mapId,
|
|
393
|
-
styleId: analytics2?.styleId,
|
|
394
|
-
styleUrl: entry
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
const styleUrl = entry.styleUrl ?? entry.url;
|
|
398
|
-
if (!styleUrl && !entry.mapId) {
|
|
399
|
-
return null;
|
|
400
|
-
}
|
|
401
|
-
const analytics = styleUrl ? inferAnalyticsFromStyleUrl(styleUrl) : void 0;
|
|
402
|
-
return {
|
|
403
|
-
apiUrl: entry.apiUrl ?? manifest.apiUrl ?? analytics?.apiUrl,
|
|
404
|
-
mapId: entry.mapId ?? analytics?.mapId,
|
|
405
|
-
styleId: entry.styleId ?? analytics?.styleId,
|
|
406
|
-
styleUrl
|
|
407
|
-
};
|
|
1411
|
+
return valueRef.current;
|
|
408
1412
|
}
|
|
409
|
-
function
|
|
410
|
-
if (
|
|
411
|
-
return
|
|
1413
|
+
function areEquivalentMapOptionValues(left, right, seen = /* @__PURE__ */ new WeakMap()) {
|
|
1414
|
+
if (Object.is(left, right)) {
|
|
1415
|
+
return true;
|
|
412
1416
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
url.searchParams.set("center", input.center.join(","));
|
|
416
|
-
url.searchParams.set("zoom", String(input.zoom));
|
|
417
|
-
url.searchParams.set("width", String(input.imageSize.width));
|
|
418
|
-
url.searchParams.set("height", String(input.imageSize.height));
|
|
419
|
-
return url.toString();
|
|
420
|
-
}
|
|
421
|
-
function normalizeStaticImageSize(input) {
|
|
422
|
-
let width = clampDimension(input.width);
|
|
423
|
-
let height = clampDimension(input.height);
|
|
424
|
-
const pixelCount = width * height;
|
|
425
|
-
if (pixelCount > maxStaticImagePixels) {
|
|
426
|
-
const scale = Math.sqrt(maxStaticImagePixels / pixelCount);
|
|
427
|
-
width = clampDimension(width * scale);
|
|
428
|
-
height = clampDimension(height * scale);
|
|
1417
|
+
if (!left || !right || typeof left !== "object" || typeof right !== "object") {
|
|
1418
|
+
return false;
|
|
429
1419
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
434
|
-
return 640;
|
|
1420
|
+
const seenRight = seen.get(left);
|
|
1421
|
+
if (seenRight) {
|
|
1422
|
+
return seenRight === right;
|
|
435
1423
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
try {
|
|
440
|
-
const url = new URL(styleUrl, getLocationOrigin());
|
|
441
|
-
const match = url.pathname.match(/\/(?:v1\/)?maps\/([^/]+)\/style\.json$/);
|
|
442
|
-
if (!match?.[1]) {
|
|
443
|
-
return void 0;
|
|
444
|
-
}
|
|
445
|
-
return {
|
|
446
|
-
apiUrl: url.origin,
|
|
447
|
-
mapId: match[1],
|
|
448
|
-
styleId: url.toString()
|
|
449
|
-
};
|
|
450
|
-
} catch {
|
|
451
|
-
return void 0;
|
|
1424
|
+
seen.set(left, right);
|
|
1425
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
1426
|
+
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => areEquivalentMapOptionValues(value, right[index], seen));
|
|
452
1427
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
if (typeof window === "undefined") {
|
|
456
|
-
return "http://localhost";
|
|
1428
|
+
if (!isPlainObject(left) || !isPlainObject(right)) {
|
|
1429
|
+
return false;
|
|
457
1430
|
}
|
|
458
|
-
|
|
1431
|
+
const leftKeys = Object.keys(left);
|
|
1432
|
+
const rightKeys = Object.keys(right);
|
|
1433
|
+
return leftKeys.length === rightKeys.length && leftKeys.every(
|
|
1434
|
+
(key) => Object.hasOwn(right, key) && areEquivalentMapOptionValues(left[key], right[key], seen)
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
function isPlainObject(value) {
|
|
1438
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1439
|
+
return prototype === Object.prototype || prototype === null;
|
|
459
1440
|
}
|
|
460
1441
|
export {
|
|
461
|
-
Map
|
|
1442
|
+
Map2 as Map,
|
|
1443
|
+
configureTileflowMapLibre,
|
|
1444
|
+
useBoundaryFeatureState
|
|
462
1445
|
};
|