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