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