@tileflow/react 0.1.0-alpha.19 → 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 +189 -15
- package/TRADEMARKS.md +10 -0
- package/dist/index.d.ts +61 -15
- package/dist/index.js +1116 -212
- package/dist/static.d.ts +3 -2
- package/dist/static.js +74 -123
- package/package.json +17 -8
package/dist/index.js
CHANGED
|
@@ -2,154 +2,501 @@
|
|
|
2
2
|
|
|
3
3
|
// src/map.tsx
|
|
4
4
|
import {
|
|
5
|
+
useCallback,
|
|
5
6
|
useEffect,
|
|
6
7
|
useLayoutEffect,
|
|
7
8
|
useMemo,
|
|
8
9
|
useRef,
|
|
9
10
|
useState
|
|
10
11
|
} from "react";
|
|
11
|
-
import
|
|
12
|
+
import { createPortal } from "react-dom";
|
|
13
|
+
import {
|
|
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";
|
|
12
26
|
import {
|
|
13
27
|
createTileflowSessionController,
|
|
14
28
|
defaultTileflowManifestUrl,
|
|
29
|
+
defaultTileflowRuntimeView,
|
|
15
30
|
loadTileflowManifest,
|
|
16
31
|
mergeTileflowAnalytics,
|
|
17
|
-
|
|
32
|
+
normalizeTileflowRuntimeCenter,
|
|
18
33
|
normalizeTileflowStaticImageSize,
|
|
19
34
|
resolveTileflowManifestMap,
|
|
20
35
|
resolveTileflowMapMode,
|
|
21
36
|
resolveTileflowRuntimeStyle,
|
|
37
|
+
resolveTileflowRuntimeTheme,
|
|
38
|
+
resolveTileflowRuntimeView,
|
|
22
39
|
resolveTileflowStaticImageUrl,
|
|
23
40
|
shouldLoadTileflowManifest
|
|
24
|
-
} from "@tileflow/core";
|
|
41
|
+
} from "@tileflow/core/runtime";
|
|
25
42
|
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
+
);
|
|
58
282
|
const containerRef = useRef(null);
|
|
59
283
|
const imageRef = useRef(null);
|
|
60
284
|
const mapRef = useRef(null);
|
|
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");
|
|
312
|
+
const [imageSize, setImageSize] = useState(null);
|
|
313
|
+
const [captureState, setCaptureState] = useState("loading");
|
|
314
|
+
const [themeTransitionState, setThemeTransitionState] = useState(
|
|
315
|
+
"idle"
|
|
316
|
+
);
|
|
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([]);
|
|
61
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
|
+
);
|
|
62
361
|
const resolvedCenter = useStableMapOptionValue(
|
|
63
|
-
center ?? stableMapOptions?.center ??
|
|
362
|
+
center ?? stableMapOptions?.center ?? manifestCenter
|
|
64
363
|
);
|
|
65
|
-
const resolvedZoom = zoom ?? stableMapOptions?.zoom ??
|
|
364
|
+
const resolvedZoom = zoom ?? stableMapOptions?.zoom ?? manifestView.zoom ?? defaultTileflowRuntimeView.zoom;
|
|
365
|
+
const resolvedBearing = stableMapOptions?.bearing ?? manifestView.bearing;
|
|
366
|
+
const resolvedPitch = stableMapOptions?.pitch ?? manifestView.pitch;
|
|
66
367
|
const resolvedInteractive = interactive ?? stableMapOptions?.interactive ?? true;
|
|
67
|
-
const staticImageCenter =
|
|
368
|
+
const staticImageCenter = useStableMapOptionValue(
|
|
369
|
+
normalizeTileflowRuntimeCenter(center ?? stableMapOptions?.center, manifestCenter)
|
|
370
|
+
);
|
|
371
|
+
const staticImageZoom = zoom ?? stableMapOptions?.zoom ?? manifestView.zoom ?? defaultTileflowRuntimeView.zoom;
|
|
68
372
|
const centerRef = useRef(resolvedCenter);
|
|
69
373
|
const zoomRef = useRef(resolvedZoom);
|
|
374
|
+
const bearingRef = useRef(resolvedBearing);
|
|
375
|
+
const pitchRef = useRef(resolvedPitch);
|
|
70
376
|
const onLoadRef = useRef(onLoad);
|
|
71
377
|
const mapNameRef = useRef(mapName);
|
|
72
378
|
const resolvedAnalyticsRef = useRef(void 0);
|
|
73
379
|
const readinessRunRef = useRef(0);
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
}),
|
|
89
|
-
[]
|
|
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
|
+
]
|
|
90
393
|
);
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
394
|
+
const declarativeInteractionDiagnostics = useMemo(
|
|
395
|
+
() => [
|
|
396
|
+
modeDiagnostic,
|
|
397
|
+
...preparedAnnotations.diagnostics,
|
|
398
|
+
...preparedInteractions.diagnostics,
|
|
399
|
+
...preparedInteractionState.diagnostics
|
|
400
|
+
].filter(
|
|
401
|
+
(diagnostic) => diagnostic !== void 0
|
|
402
|
+
),
|
|
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"
|
|
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]);
|
|
97
459
|
useEffect(() => {
|
|
98
|
-
const shouldLoadManifest = shouldLoadTileflowManifest({
|
|
99
|
-
config,
|
|
100
|
-
imageMode: isImageMode,
|
|
101
|
-
imageUrl,
|
|
102
|
-
map: mapName,
|
|
103
|
-
style,
|
|
104
|
-
styleBaseUrl,
|
|
105
|
-
styleUrl
|
|
106
|
-
});
|
|
107
460
|
if (!shouldLoadManifest) {
|
|
108
|
-
|
|
461
|
+
setManifestResolution({ key: manifestRequestKey, map: null, state: "not-needed" });
|
|
109
462
|
return;
|
|
110
463
|
}
|
|
111
464
|
let cancelled = false;
|
|
112
|
-
|
|
465
|
+
setManifestResolution({ key: manifestRequestKey, map: null, state: "loading" });
|
|
113
466
|
loadTileflowManifest(manifestUrl).then((manifest) => {
|
|
114
467
|
if (cancelled) {
|
|
115
468
|
return;
|
|
116
469
|
}
|
|
117
|
-
|
|
470
|
+
const resolvedMap = manifest && mapName ? resolveTileflowManifestMap(manifest, mapName) : null;
|
|
471
|
+
setManifestResolution({
|
|
472
|
+
key: manifestRequestKey,
|
|
473
|
+
map: resolvedMap,
|
|
474
|
+
state: resolvedMap ? "ready" : "error"
|
|
475
|
+
});
|
|
118
476
|
}).catch((error) => {
|
|
119
477
|
if (!cancelled) {
|
|
120
478
|
console.error("Failed to load Tileflow manifest", error);
|
|
121
|
-
|
|
479
|
+
setManifestResolution({ key: manifestRequestKey, map: null, state: "error" });
|
|
122
480
|
}
|
|
123
481
|
});
|
|
124
482
|
return () => {
|
|
125
483
|
cancelled = true;
|
|
126
484
|
};
|
|
127
|
-
}, [
|
|
485
|
+
}, [manifestRequestKey, manifestUrl, mapName, shouldLoadManifest]);
|
|
128
486
|
const runtimeStyle = useMemo(() => {
|
|
129
|
-
if (isImageMode) {
|
|
487
|
+
if (isImageMode || themeResolution.error) {
|
|
130
488
|
return null;
|
|
131
489
|
}
|
|
132
490
|
return resolveTileflowRuntimeStyle({
|
|
133
|
-
|
|
491
|
+
colorScheme: systemColorScheme,
|
|
134
492
|
manifestMap,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
style,
|
|
138
|
-
styleBaseUrl,
|
|
139
|
-
styleUrl,
|
|
140
|
-
themes
|
|
493
|
+
source: runtimeSource,
|
|
494
|
+
theme: themeResolution.name
|
|
141
495
|
});
|
|
142
|
-
}, [
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
mapName,
|
|
147
|
-
preferLocalDev,
|
|
148
|
-
style,
|
|
149
|
-
styleBaseUrl,
|
|
150
|
-
styleUrl,
|
|
151
|
-
themes
|
|
152
|
-
]);
|
|
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;
|
|
153
500
|
const resolvedAnalytics = useMemo(
|
|
154
501
|
() => mergeTileflowAnalytics(analytics, runtimeStyle?.analytics),
|
|
155
502
|
[analytics, runtimeStyle]
|
|
@@ -157,11 +504,15 @@ function Map({
|
|
|
157
504
|
useEffect(() => {
|
|
158
505
|
centerRef.current = resolvedCenter;
|
|
159
506
|
zoomRef.current = resolvedZoom;
|
|
507
|
+
bearingRef.current = resolvedBearing;
|
|
508
|
+
pitchRef.current = resolvedPitch;
|
|
160
509
|
mapRef.current?.jumpTo({
|
|
510
|
+
bearing: resolvedBearing,
|
|
161
511
|
center: resolvedCenter,
|
|
512
|
+
pitch: resolvedPitch,
|
|
162
513
|
zoom: resolvedZoom
|
|
163
514
|
});
|
|
164
|
-
}, [resolvedCenter, resolvedZoom]);
|
|
515
|
+
}, [resolvedBearing, resolvedCenter, resolvedPitch, resolvedZoom]);
|
|
165
516
|
useEffect(() => {
|
|
166
517
|
onLoadRef.current = onLoad;
|
|
167
518
|
}, [onLoad]);
|
|
@@ -172,14 +523,38 @@ function Map({
|
|
|
172
523
|
resolvedAnalyticsRef.current = resolvedAnalytics;
|
|
173
524
|
}, [resolvedAnalytics]);
|
|
174
525
|
const runtimeImageUrl = useMemo(
|
|
175
|
-
() => imageUrl ?? (isImageMode ? resolveTileflowStaticImageUrl({
|
|
526
|
+
() => imageUrl ?? (isImageMode && !themeResolution.error ? resolveTileflowStaticImageUrl({
|
|
176
527
|
center: staticImageCenter,
|
|
528
|
+
colorScheme: systemColorScheme,
|
|
177
529
|
imageSize,
|
|
178
530
|
manifestMap,
|
|
179
|
-
|
|
531
|
+
theme: themeResolution.name,
|
|
532
|
+
zoom: staticImageZoom
|
|
180
533
|
}) : void 0),
|
|
181
|
-
[
|
|
534
|
+
[
|
|
535
|
+
imageSize,
|
|
536
|
+
imageUrl,
|
|
537
|
+
isImageMode,
|
|
538
|
+
manifestMap,
|
|
539
|
+
staticImageCenter,
|
|
540
|
+
staticImageZoom,
|
|
541
|
+
systemColorScheme,
|
|
542
|
+
themeResolution
|
|
543
|
+
]
|
|
182
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";
|
|
183
558
|
useEffect(() => {
|
|
184
559
|
if (!isImageMode || imageUrl || !containerRef.current) {
|
|
185
560
|
setImageSize(null);
|
|
@@ -203,91 +578,403 @@ function Map({
|
|
|
203
578
|
};
|
|
204
579
|
}, [imageUrl, isImageMode]);
|
|
205
580
|
useEffect(() => {
|
|
206
|
-
|
|
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);
|
|
593
|
+
}
|
|
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);
|
|
608
|
+
}
|
|
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
|
|
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) {
|
|
207
653
|
return;
|
|
208
654
|
}
|
|
209
655
|
readinessRunRef.current += 1;
|
|
656
|
+
setActiveRuntimeResource(initialRuntimeStyle);
|
|
210
657
|
setCaptureState("loading");
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
sessionId: session.sessionId,
|
|
223
|
-
transformRequest: stableMapOptions?.transformRequest ?? void 0
|
|
224
|
-
});
|
|
225
|
-
const map = new maplibregl.Map({
|
|
226
|
-
...stableMapOptions,
|
|
227
|
-
container: containerRef.current,
|
|
228
|
-
style: runtimeStyle.style,
|
|
229
|
-
center: centerRef.current,
|
|
230
|
-
zoom: zoomRef.current,
|
|
231
|
-
interactive: resolvedInteractive,
|
|
232
|
-
attributionControl: stableMapOptions?.attributionControl ?? { compact: true },
|
|
233
|
-
transformRequest
|
|
234
|
-
});
|
|
235
|
-
mapRef.current = map;
|
|
236
|
-
const controls = resolvedInteractive ? new maplibregl.NavigationControl() : null;
|
|
237
|
-
if (controls) {
|
|
238
|
-
map.addControl(controls, "top-right");
|
|
239
|
-
}
|
|
240
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
241
|
-
map.resize();
|
|
242
|
-
});
|
|
243
|
-
resizeObserver.observe(containerRef.current);
|
|
244
|
-
const lifecycle = attachTileflowMapLifecycle({
|
|
245
|
-
getSession: () => {
|
|
246
|
-
const analyticsForLoad = resolvedAnalyticsRef.current;
|
|
247
|
-
return {
|
|
248
|
-
analytics: analyticsForLoad,
|
|
249
|
-
styleId: analyticsForLoad?.styleId ?? (typeof runtimeStyle.style === "string" ? runtimeStyle.style : mapNameRef.current)
|
|
250
|
-
};
|
|
251
|
-
},
|
|
252
|
-
map,
|
|
253
|
-
onLoad: (loadedMap) => {
|
|
254
|
-
onLoadRef.current?.(loadedMap);
|
|
255
|
-
},
|
|
256
|
-
scheduler: {
|
|
257
|
-
cancelFrame: (frame) => cancelAnimationFrame(frame),
|
|
258
|
-
requestFrame: (callback) => requestAnimationFrame(callback)
|
|
259
|
-
},
|
|
260
|
-
sessionStarter,
|
|
261
|
-
setState: setCaptureState,
|
|
262
|
-
subscribe: (subscribedMap, event, listener) => {
|
|
263
|
-
const subscription = subscribedMap.on(event, listener);
|
|
264
|
-
return () => subscription.unsubscribe();
|
|
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;
|
|
265
669
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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;
|
|
272
677
|
try {
|
|
273
|
-
|
|
678
|
+
runTileflowReactCleanups([...setupCleanups].reverse());
|
|
274
679
|
} finally {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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;
|
|
283
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);
|
|
284
902
|
}
|
|
285
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
|
+
}
|
|
952
|
+
});
|
|
953
|
+
return () => {
|
|
954
|
+
cancelled = true;
|
|
955
|
+
readinessRunRef.current += 1;
|
|
956
|
+
disposeMap?.();
|
|
286
957
|
};
|
|
287
|
-
}, [
|
|
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]);
|
|
288
974
|
useLayoutEffect(() => {
|
|
289
975
|
if (!isImageMode) return;
|
|
290
976
|
readinessRunRef.current += 1;
|
|
977
|
+
setActiveRuntimeResource(runtimeImageUrl);
|
|
291
978
|
setCaptureState("loading");
|
|
292
979
|
const image = imageRef.current;
|
|
293
980
|
if (image?.complete) {
|
|
@@ -307,17 +994,6 @@ function Map({
|
|
|
307
994
|
}
|
|
308
995
|
if (readinessRunRef.current === run) setCaptureState("idle");
|
|
309
996
|
}
|
|
310
|
-
useEffect(() => {
|
|
311
|
-
const map = mapRef.current;
|
|
312
|
-
markerController.clear();
|
|
313
|
-
if (!map) {
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
markerController.replace(map, markers);
|
|
317
|
-
return () => {
|
|
318
|
-
markerController.clear();
|
|
319
|
-
};
|
|
320
|
-
}, [markerController, markers, runtimeStyle]);
|
|
321
997
|
const frameStyle = {
|
|
322
998
|
height,
|
|
323
999
|
minHeight: 240,
|
|
@@ -325,51 +1001,279 @@ function Map({
|
|
|
325
1001
|
position: "relative",
|
|
326
1002
|
width: "100%"
|
|
327
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;
|
|
328
1013
|
if (isImageMode) {
|
|
329
|
-
return /* @__PURE__ */
|
|
1014
|
+
return /* @__PURE__ */ jsxs(
|
|
330
1015
|
"div",
|
|
331
1016
|
{
|
|
332
1017
|
className,
|
|
333
1018
|
"data-tileflow-capture-id": resolvedCaptureId,
|
|
334
1019
|
"data-tileflow-map": mapName,
|
|
335
|
-
"data-tileflow-
|
|
1020
|
+
"data-tileflow-theme": resolvedThemeName,
|
|
1021
|
+
"data-tileflow-state": effectiveCaptureState,
|
|
336
1022
|
ref: containerRef,
|
|
337
1023
|
style: frameStyle,
|
|
338
|
-
children:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
+
}
|
|
356
1044
|
}
|
|
357
|
-
|
|
358
|
-
|
|
1045
|
+
) : null,
|
|
1046
|
+
diagnosticView
|
|
1047
|
+
]
|
|
359
1048
|
}
|
|
360
1049
|
);
|
|
361
1050
|
}
|
|
362
|
-
return /* @__PURE__ */
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
+
] });
|
|
1105
|
+
}
|
|
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;
|
|
1152
|
+
}
|
|
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;
|
|
1172
|
+
}
|
|
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 };
|
|
1178
|
+
}
|
|
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 };
|
|
1183
|
+
}
|
|
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;
|
|
371
1206
|
}
|
|
372
|
-
|
|
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]]);
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
function createTileflowMapLibrePoiMap(map) {
|
|
1237
|
+
return {
|
|
1238
|
+
getStyle: () => map.getStyle(),
|
|
1239
|
+
on(event, listener) {
|
|
1240
|
+
return map.on(event, listener);
|
|
1241
|
+
},
|
|
1242
|
+
queryRenderedFeatures(point, options) {
|
|
1243
|
+
return map.queryRenderedFeatures(point, {
|
|
1244
|
+
layers: [...options.layers]
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
function createOverlayFailureDiagnostic(message) {
|
|
1250
|
+
return {
|
|
1251
|
+
code: "OVERLAY_FAILURE",
|
|
1252
|
+
level: "error",
|
|
1253
|
+
message,
|
|
1254
|
+
path: ""
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
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");
|
|
1265
|
+
}
|
|
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
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
if (errors.length === 1) throw errors[0];
|
|
1276
|
+
if (errors.length > 1) throw new AggregateError(errors, "Tileflow React cleanup failed.");
|
|
373
1277
|
}
|
|
374
1278
|
function useStableMapOptions(options) {
|
|
375
1279
|
return useStableMapOptionValue(options);
|
|
@@ -410,5 +1314,5 @@ function isPlainObject(value) {
|
|
|
410
1314
|
return prototype === Object.prototype || prototype === null;
|
|
411
1315
|
}
|
|
412
1316
|
export {
|
|
413
|
-
Map
|
|
1317
|
+
Map2 as Map
|
|
414
1318
|
};
|