@tileflow/vue 0.1.0-alpha.19 → 0.1.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GENERATED_OUTPUT_LICENSE.md +21 -0
- package/LICENSE +201 -0
- package/NOTICE +9 -0
- package/README.md +157 -6
- package/TRADEMARKS.md +10 -0
- package/dist/index.d.ts +65 -124
- package/dist/index.js +859 -154
- package/package.json +14 -8
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import maplibregl from "maplibre-gl";
|
|
3
2
|
import {
|
|
4
3
|
computed,
|
|
5
4
|
defineComponent,
|
|
@@ -7,44 +6,177 @@ import {
|
|
|
7
6
|
nextTick,
|
|
8
7
|
onBeforeUnmount,
|
|
9
8
|
onMounted,
|
|
9
|
+
onUpdated,
|
|
10
10
|
ref,
|
|
11
11
|
shallowRef,
|
|
12
|
-
|
|
12
|
+
Teleport,
|
|
13
|
+
watch,
|
|
14
|
+
watchEffect
|
|
13
15
|
} from "vue";
|
|
16
|
+
import {
|
|
17
|
+
attachTileflowFairUseNotice,
|
|
18
|
+
attachTileflowMapLifecycle,
|
|
19
|
+
createTileflowSessionStarter,
|
|
20
|
+
createTileflowThemeController,
|
|
21
|
+
createTileflowTransformRequest,
|
|
22
|
+
getTileflowSystemColorScheme,
|
|
23
|
+
loadTileflowStyleFonts,
|
|
24
|
+
registerTileflowContourProtocol,
|
|
25
|
+
registerTileflowPmtilesProtocol,
|
|
26
|
+
registerTileflowWorldRequestBridge,
|
|
27
|
+
subscribeTileflowSystemColorScheme
|
|
28
|
+
} from "@tileflow/core/browser";
|
|
29
|
+
import { normalizeTileflowCaptureId } from "@tileflow/core/capture";
|
|
14
30
|
import {
|
|
15
31
|
createTileflowSessionController,
|
|
16
32
|
defaultTileflowManifestUrl,
|
|
33
|
+
defaultTileflowRuntimeView,
|
|
17
34
|
loadTileflowManifest,
|
|
18
35
|
mergeTileflowAnalytics,
|
|
19
|
-
|
|
36
|
+
normalizeTileflowRuntimeCenter,
|
|
20
37
|
normalizeTileflowStaticImageSize,
|
|
21
38
|
resolveTileflowManifestMap,
|
|
22
39
|
resolveTileflowMapMode,
|
|
23
40
|
resolveTileflowRuntimeStyle,
|
|
41
|
+
resolveTileflowRuntimeTheme,
|
|
42
|
+
resolveTileflowRuntimeView,
|
|
24
43
|
resolveTileflowStaticImageUrl,
|
|
25
44
|
shouldLoadTileflowManifest
|
|
26
|
-
} from "@tileflow/core";
|
|
45
|
+
} from "@tileflow/core/runtime";
|
|
27
46
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
47
|
+
initialTileflowInteractionState
|
|
48
|
+
} from "@tileflow/interactions";
|
|
49
|
+
import {
|
|
50
|
+
createTileflowMapLibreDomRuntime,
|
|
51
|
+
createTileflowMapLibreInteractionCoordinator,
|
|
52
|
+
createTileflowMapLibreSemanticDomRuntime
|
|
53
|
+
} from "@tileflow/interactions/maplibre";
|
|
54
|
+
|
|
55
|
+
// src/interactions.ts
|
|
56
|
+
import {
|
|
57
|
+
tileflowInteractionStateSchema,
|
|
58
|
+
validateTileflowAnnotations,
|
|
59
|
+
validateTileflowInteractionBindings
|
|
60
|
+
} from "@tileflow/interactions";
|
|
61
|
+
function resolveTileflowVueAnnotations({ annotations }) {
|
|
62
|
+
const candidates = annotations ?? [];
|
|
63
|
+
const validation = validateTileflowAnnotations(candidates);
|
|
64
|
+
if (!validation.ok) {
|
|
65
|
+
return {
|
|
66
|
+
annotations: [],
|
|
67
|
+
diagnostics: validation.diagnostics,
|
|
68
|
+
ok: false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
// Validation deliberately does not replace application data with a schema clone. Slot
|
|
73
|
+
// contexts keep the caller's typed annotation and data identities.
|
|
74
|
+
annotations: candidates,
|
|
75
|
+
diagnostics: [],
|
|
76
|
+
ok: true
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function resolveTileflowVueInteractionBindings(interactions) {
|
|
80
|
+
const candidates = interactions ?? [];
|
|
81
|
+
const validation = validateTileflowInteractionBindings(candidates);
|
|
82
|
+
if (!validation.ok) {
|
|
83
|
+
return { bindings: [], diagnostics: validation.diagnostics, ok: false };
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
// As with annotations, validation must not replace caller-owned objects: bindings can be
|
|
87
|
+
// compared by identity by framework consumers even though their runtime shape is JSON-safe.
|
|
88
|
+
bindings: candidates,
|
|
89
|
+
diagnostics: [],
|
|
90
|
+
ok: true
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function validateTileflowVueInteractionState(interactionState, defaultInteractionState) {
|
|
94
|
+
if (interactionState !== void 0 && defaultInteractionState !== void 0) {
|
|
95
|
+
return [
|
|
96
|
+
createTileflowVueInteractionDiagnostic(
|
|
97
|
+
"INVALID_DOCUMENT",
|
|
98
|
+
"interactionState and defaultInteractionState are mutually exclusive."
|
|
99
|
+
)
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
const candidate = interactionState ?? defaultInteractionState;
|
|
103
|
+
if (candidate === void 0) return [];
|
|
104
|
+
const statePath = interactionState === void 0 ? "/defaultInteractionState" : "/interactionState";
|
|
105
|
+
const parsed = tileflowInteractionStateSchema.safeParse(candidate);
|
|
106
|
+
if (parsed.success) return [];
|
|
107
|
+
return parsed.error.issues.map(
|
|
108
|
+
(issue) => createTileflowVueInteractionDiagnostic(
|
|
109
|
+
"INVALID_DOCUMENT",
|
|
110
|
+
issue.message,
|
|
111
|
+
`${statePath}${jsonPointer(issue.path)}`
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function createTileflowVueInteractionDiagnostic(code, message, path) {
|
|
116
|
+
return {
|
|
117
|
+
code,
|
|
118
|
+
level: "error",
|
|
119
|
+
message,
|
|
120
|
+
...path === void 0 ? {} : { path }
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function jsonPointer(path) {
|
|
124
|
+
return path.map((segment) => String(segment).replaceAll("~", "~0").replaceAll("/", "~1")).map((segment) => `/${segment}`).join("");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/maplibre.ts
|
|
128
|
+
var mapLibrePromise;
|
|
129
|
+
function loadTileflowMapLibre() {
|
|
130
|
+
mapLibrePromise ??= import("maplibre-gl").then(resolveMapLibreModule).catch((error) => {
|
|
131
|
+
mapLibrePromise = void 0;
|
|
132
|
+
throw error;
|
|
133
|
+
});
|
|
134
|
+
return mapLibrePromise;
|
|
135
|
+
}
|
|
136
|
+
function resolveMapLibreModule(module) {
|
|
137
|
+
const defaultExport = module.default;
|
|
138
|
+
return defaultExport ?? module;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/style-source.ts
|
|
142
|
+
import {
|
|
143
|
+
validateTileflowRuntimeSource,
|
|
144
|
+
validateTileflowThemeSelection
|
|
145
|
+
} from "@tileflow/core/runtime";
|
|
146
|
+
function validateTileflowMapStyleInputs(input) {
|
|
147
|
+
const sourceValidation = validateTileflowRuntimeSource(input.source);
|
|
148
|
+
if (!sourceValidation.ok) return sourceValidation;
|
|
149
|
+
if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
|
|
150
|
+
return { error: 'theme must be a concrete portable theme name or "system"', ok: false };
|
|
151
|
+
}
|
|
152
|
+
if (input.theme !== void 0 && input.source.kind !== "tileflow") {
|
|
153
|
+
return { error: "theme is only valid with a tileflow source", ok: false };
|
|
154
|
+
}
|
|
155
|
+
return { ok: true };
|
|
156
|
+
}
|
|
157
|
+
function assertTileflowMapStyleInputs(input) {
|
|
158
|
+
const validation = validateTileflowMapStyleInputs(input);
|
|
159
|
+
if (!validation.ok) {
|
|
160
|
+
throw new TypeError(`Invalid TileflowMap source: ${validation.error}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/index.ts
|
|
35
165
|
var TileflowMap = defineComponent({
|
|
36
166
|
name: "TileflowMap",
|
|
37
167
|
inheritAttrs: false,
|
|
168
|
+
slots: Object,
|
|
38
169
|
props: {
|
|
39
170
|
alt: {
|
|
40
171
|
default: "",
|
|
41
172
|
type: String
|
|
42
173
|
},
|
|
43
174
|
analytics: Object,
|
|
175
|
+
annotations: Array,
|
|
44
176
|
center: Array,
|
|
45
177
|
captureId: String,
|
|
46
178
|
className: String,
|
|
47
|
-
|
|
179
|
+
defaultInteractionState: Object,
|
|
48
180
|
height: {
|
|
49
181
|
default: 420,
|
|
50
182
|
type: [Number, String]
|
|
@@ -58,127 +190,361 @@ var TileflowMap = defineComponent({
|
|
|
58
190
|
default: void 0,
|
|
59
191
|
type: Boolean
|
|
60
192
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
type: String
|
|
64
|
-
},
|
|
65
|
-
map: String,
|
|
193
|
+
interactions: Array,
|
|
194
|
+
interactionState: Object,
|
|
66
195
|
mapOptions: Object,
|
|
67
|
-
mapStyle: Object,
|
|
68
|
-
markers: {
|
|
69
|
-
default: () => [],
|
|
70
|
-
type: Array
|
|
71
|
-
},
|
|
72
196
|
mode: {
|
|
73
197
|
default: "interactive",
|
|
74
198
|
type: String
|
|
75
199
|
},
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
type:
|
|
200
|
+
source: {
|
|
201
|
+
required: true,
|
|
202
|
+
type: Object
|
|
79
203
|
},
|
|
80
|
-
|
|
81
|
-
styleUrl: String,
|
|
82
|
-
themes: Object,
|
|
204
|
+
theme: String,
|
|
83
205
|
zoom: Number
|
|
84
206
|
},
|
|
85
207
|
emits: {
|
|
86
|
-
|
|
208
|
+
interactionDiagnostic: (_diagnostic) => true,
|
|
209
|
+
interactionEvent: (_event) => true,
|
|
210
|
+
load: (_map) => true,
|
|
211
|
+
themeChange: (_transition) => true,
|
|
212
|
+
"update:interactionState": (_state) => true
|
|
87
213
|
},
|
|
88
|
-
setup(props, { attrs, emit }) {
|
|
214
|
+
setup(props, { attrs, emit, slots }) {
|
|
89
215
|
const containerRef = ref(null);
|
|
90
|
-
const
|
|
216
|
+
const loadedManifestMap = shallowRef(null);
|
|
217
|
+
const manifestResolutionKey = ref("");
|
|
218
|
+
const manifestResolutionState = ref("loading");
|
|
91
219
|
const imageSize = shallowRef(null);
|
|
92
220
|
const mapRef = shallowRef(null);
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
221
|
+
const systemColorScheme = ref("light");
|
|
222
|
+
const mapCaptureState = ref("loading");
|
|
223
|
+
const themeTransitionState = ref("idle");
|
|
224
|
+
const activeRuntimeResource = shallowRef(void 0);
|
|
225
|
+
const interactionCaptureState = ref("idle");
|
|
226
|
+
const annotationRenderTargets = shallowRef([]);
|
|
227
|
+
const semanticRenderTargets = shallowRef(
|
|
228
|
+
[]
|
|
229
|
+
);
|
|
230
|
+
const controlledInteractionOwnership = props.interactionState !== void 0;
|
|
231
|
+
let annotationRuntime = null;
|
|
232
|
+
let semanticRuntime = null;
|
|
233
|
+
let interactionRuntimesDisposing = false;
|
|
234
|
+
let initializeSemanticRuntime = null;
|
|
235
|
+
let annotationRuntimeDiagnosticsUnsubscribe = null;
|
|
236
|
+
let annotationRuntimeCoordinatorDetach = null;
|
|
237
|
+
let annotationRuntimeEventsUnsubscribe = null;
|
|
238
|
+
let annotationRuntimeTargetsUnsubscribe = null;
|
|
239
|
+
let semanticRuntimeDiagnosticsUnsubscribe = null;
|
|
240
|
+
let semanticRuntimeCoordinatorDetach = null;
|
|
241
|
+
let semanticRuntimeEventsUnsubscribe = null;
|
|
242
|
+
let semanticRuntimeTargetsUnsubscribe = null;
|
|
243
|
+
let activeAnnotationDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
244
|
+
let activeSemanticDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
245
|
+
let annotationRuntimeDiagnostics = [];
|
|
246
|
+
let semanticRuntimeDiagnostics = [];
|
|
104
247
|
let imageResizeObserver = null;
|
|
248
|
+
let mapFairUseNotice = null;
|
|
105
249
|
let mapLifecycle = null;
|
|
106
250
|
let mapResizeObserver = null;
|
|
251
|
+
let mapWorldRequestBridge = null;
|
|
252
|
+
let themeController = null;
|
|
253
|
+
let unsubscribeSystemColorScheme = null;
|
|
254
|
+
let mapLoadId = 0;
|
|
107
255
|
let manifestLoadId = 0;
|
|
108
256
|
let readinessRunId = 0;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
257
|
+
let interactionReadinessRunId = 0;
|
|
258
|
+
let interactionReadinessFrames = [];
|
|
259
|
+
let interactionOverlayFailed = false;
|
|
260
|
+
let interactionInputDiagnosticSignature = "";
|
|
261
|
+
const resolvedMode = computed(() => resolveTileflowMapMode({ mode: props.mode }));
|
|
262
|
+
const isImageMode = computed(() => resolvedMode.value === "image");
|
|
263
|
+
const annotationResolution = computed(
|
|
264
|
+
() => resolveTileflowVueAnnotations({
|
|
265
|
+
annotations: props.annotations
|
|
114
266
|
})
|
|
115
267
|
);
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
268
|
+
const interactionBindingResolution = computed(
|
|
269
|
+
() => resolveTileflowVueInteractionBindings(props.interactions)
|
|
270
|
+
);
|
|
271
|
+
const interactionStateDiagnostics = computed(() => {
|
|
272
|
+
const diagnostics = [
|
|
273
|
+
...validateTileflowVueInteractionState(
|
|
274
|
+
props.interactionState,
|
|
275
|
+
props.defaultInteractionState
|
|
276
|
+
)
|
|
277
|
+
];
|
|
278
|
+
if (props.interactionState !== void 0 !== controlledInteractionOwnership) {
|
|
279
|
+
diagnostics.push(
|
|
280
|
+
createTileflowVueInteractionDiagnostic(
|
|
281
|
+
"INVALID_DOCUMENT",
|
|
282
|
+
"Tileflow interaction state ownership cannot switch between controlled and uncontrolled."
|
|
283
|
+
)
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
return diagnostics;
|
|
287
|
+
});
|
|
288
|
+
const hasInteractionConfiguration = computed(
|
|
289
|
+
() => props.annotations !== void 0 || props.interactions !== void 0 || props.interactionState !== void 0 || props.defaultInteractionState !== void 0 || slots.marker !== void 0 || slots.popup !== void 0 || slots.tooltip !== void 0
|
|
290
|
+
);
|
|
291
|
+
const interactionInputDiagnostics = computed(() => {
|
|
292
|
+
const diagnostics = [
|
|
293
|
+
...annotationResolution.value.diagnostics,
|
|
294
|
+
...interactionBindingResolution.value.diagnostics,
|
|
295
|
+
...interactionStateDiagnostics.value
|
|
296
|
+
];
|
|
297
|
+
if (isImageMode.value && hasInteractionConfiguration.value) {
|
|
298
|
+
diagnostics.push(
|
|
299
|
+
createTileflowVueInteractionDiagnostic(
|
|
300
|
+
"UNSUPPORTED_MODE",
|
|
301
|
+
"Annotations, semantic interactions, and interaction state are unavailable in image mode."
|
|
302
|
+
)
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
return diagnostics;
|
|
306
|
+
});
|
|
307
|
+
const initialInteractionState = validateTileflowVueInteractionState(props.interactionState, props.defaultInteractionState).length === 0 ? props.interactionState ?? props.defaultInteractionState ?? initialTileflowInteractionState : initialTileflowInteractionState;
|
|
308
|
+
const interactionCoordinator = createTileflowMapLibreInteractionCoordinator({
|
|
309
|
+
onInteractionStateChange(nextState) {
|
|
310
|
+
if (interactionRuntimesDisposing) return;
|
|
311
|
+
emit("update:interactionState", nextState);
|
|
312
|
+
},
|
|
313
|
+
...controlledInteractionOwnership ? { interactionState: initialInteractionState } : { defaultInteractionState: initialInteractionState }
|
|
314
|
+
});
|
|
315
|
+
const mapName = computed(
|
|
316
|
+
() => props.source.kind === "tileflow" ? props.source.map : void 0
|
|
317
|
+
);
|
|
318
|
+
const manifestUrl = computed(
|
|
319
|
+
() => props.source.kind === "tileflow" ? props.source.manifestUrl ?? defaultTileflowManifestUrl : defaultTileflowManifestUrl
|
|
320
|
+
);
|
|
321
|
+
const shouldLoadManifest = computed(
|
|
322
|
+
() => shouldLoadTileflowManifest({
|
|
323
|
+
source: props.source
|
|
324
|
+
})
|
|
325
|
+
);
|
|
326
|
+
const manifestRequestKey = computed(
|
|
327
|
+
() => shouldLoadManifest.value ? JSON.stringify([manifestUrl.value, mapName.value]) : "not-needed"
|
|
328
|
+
);
|
|
329
|
+
const currentManifestResolutionState = computed(
|
|
330
|
+
() => manifestResolutionKey.value === manifestRequestKey.value ? manifestResolutionState.value : shouldLoadManifest.value ? "loading" : "not-needed"
|
|
331
|
+
);
|
|
332
|
+
const manifestMap = computed(
|
|
333
|
+
() => manifestResolutionKey.value === manifestRequestKey.value && manifestResolutionState.value === "ready" ? loadedManifestMap.value : null
|
|
334
|
+
);
|
|
335
|
+
const themeResolution = computed(() => {
|
|
336
|
+
if (!manifestMap.value || props.source.kind !== "tileflow") {
|
|
337
|
+
return {
|
|
338
|
+
error: false,
|
|
339
|
+
name: props.source.kind === "tileflow" && props.theme && props.theme !== "system" ? props.theme : void 0
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
try {
|
|
343
|
+
return {
|
|
344
|
+
error: false,
|
|
345
|
+
name: resolveTileflowRuntimeTheme(manifestMap.value, props.theme, systemColorScheme.value).name
|
|
346
|
+
};
|
|
347
|
+
} catch {
|
|
348
|
+
return { error: true, name: void 0 };
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
const resolvedThemeName = computed(() => themeResolution.value.name);
|
|
352
|
+
const manifestView = computed(
|
|
353
|
+
() => resolveTileflowRuntimeView({ manifestMap: manifestMap.value })
|
|
354
|
+
);
|
|
355
|
+
const manifestCenter = computed(
|
|
356
|
+
() => normalizeTileflowRuntimeCenter(manifestView.value.center)
|
|
357
|
+
);
|
|
358
|
+
const imageCenter = computed(
|
|
359
|
+
() => normalizeTileflowRuntimeCenter(
|
|
360
|
+
props.center ?? props.mapOptions?.center,
|
|
361
|
+
manifestCenter.value
|
|
362
|
+
)
|
|
363
|
+
);
|
|
364
|
+
const imageZoom = computed(
|
|
365
|
+
() => props.zoom ?? props.mapOptions?.zoom ?? manifestView.value.zoom ?? defaultTileflowRuntimeView.zoom
|
|
366
|
+
);
|
|
119
367
|
const resolvedCenter = computed(
|
|
120
|
-
() => props.center ?? props.mapOptions?.center ??
|
|
368
|
+
() => props.center ?? props.mapOptions?.center ?? manifestCenter.value
|
|
369
|
+
);
|
|
370
|
+
const resolvedZoom = computed(
|
|
371
|
+
() => props.zoom ?? props.mapOptions?.zoom ?? manifestView.value.zoom ?? defaultTileflowRuntimeView.zoom
|
|
121
372
|
);
|
|
122
|
-
const
|
|
373
|
+
const resolvedBearing = computed(() => props.mapOptions?.bearing ?? manifestView.value.bearing);
|
|
374
|
+
const resolvedPitch = computed(() => props.mapOptions?.pitch ?? manifestView.value.pitch);
|
|
123
375
|
const resolvedInteractive = computed(
|
|
124
376
|
() => props.interactive ?? props.mapOptions?.interactive ?? true
|
|
125
377
|
);
|
|
126
378
|
const runtimeStyle = computed(
|
|
127
|
-
() => isImageMode.value ? null : resolveTileflowRuntimeStyle({
|
|
128
|
-
|
|
379
|
+
() => isImageMode.value || themeResolution.value.error ? null : resolveTileflowRuntimeStyle({
|
|
380
|
+
colorScheme: systemColorScheme.value,
|
|
129
381
|
manifestMap: manifestMap.value,
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
style: props.mapStyle,
|
|
133
|
-
styleBaseUrl: props.styleBaseUrl,
|
|
134
|
-
styleUrl: props.styleUrl,
|
|
135
|
-
themes: props.themes
|
|
382
|
+
source: props.source,
|
|
383
|
+
theme: themeResolution.value.name
|
|
136
384
|
})
|
|
137
385
|
);
|
|
138
386
|
const resolvedAnalytics = computed(
|
|
139
387
|
() => mergeTileflowAnalytics(props.analytics, runtimeStyle.value?.analytics)
|
|
140
388
|
);
|
|
141
389
|
const runtimeImageUrl = computed(
|
|
142
|
-
() => props.imageUrl ?? (isImageMode.value ? resolveTileflowStaticImageUrl({
|
|
390
|
+
() => props.imageUrl ?? (isImageMode.value && !themeResolution.value.error ? resolveTileflowStaticImageUrl({
|
|
143
391
|
center: imageCenter.value,
|
|
392
|
+
colorScheme: systemColorScheme.value,
|
|
144
393
|
imageSize: imageSize.value,
|
|
145
394
|
manifestMap: manifestMap.value,
|
|
395
|
+
theme: themeResolution.value.name,
|
|
146
396
|
zoom: imageZoom.value
|
|
147
397
|
}) : void 0)
|
|
148
398
|
);
|
|
399
|
+
const runtimeResolutionState = computed(() => {
|
|
400
|
+
if (shouldLoadManifest.value && currentManifestResolutionState.value !== "ready") {
|
|
401
|
+
return currentManifestResolutionState.value === "error" ? "error" : "loading";
|
|
402
|
+
}
|
|
403
|
+
if (themeResolution.value.error) return "error";
|
|
404
|
+
if (isImageMode.value) {
|
|
405
|
+
if (runtimeImageUrl.value) return "idle";
|
|
406
|
+
return props.imageUrl === void 0 && imageSize.value === null ? "loading" : "error";
|
|
407
|
+
}
|
|
408
|
+
return runtimeStyle.value ? "idle" : "error";
|
|
409
|
+
});
|
|
410
|
+
const currentMapCaptureState = computed(
|
|
411
|
+
() => activeRuntimeResource.value === (isImageMode.value ? runtimeImageUrl.value : runtimeStyle.value) ? mapCaptureState.value : "loading"
|
|
412
|
+
);
|
|
413
|
+
const captureState = computed(() => {
|
|
414
|
+
if (currentMapCaptureState.value === "error" || interactionCaptureState.value === "error" || runtimeResolutionState.value === "error" || themeTransitionState.value === "error") {
|
|
415
|
+
return "error";
|
|
416
|
+
}
|
|
417
|
+
if (currentMapCaptureState.value === "loading" || interactionCaptureState.value === "loading" || runtimeResolutionState.value === "loading" || themeTransitionState.value === "loading") {
|
|
418
|
+
return "loading";
|
|
419
|
+
}
|
|
420
|
+
return "idle";
|
|
421
|
+
});
|
|
149
422
|
const frameStyle = computed(() => ({
|
|
150
|
-
height: formatHeight(props.height),
|
|
423
|
+
height: formatHeight(props.height ?? 420),
|
|
151
424
|
minHeight: "240px",
|
|
152
425
|
overflow: "hidden",
|
|
153
426
|
position: "relative",
|
|
154
427
|
width: "100%"
|
|
155
428
|
}));
|
|
156
429
|
const resolvedCaptureId = computed(() => normalizeTileflowCaptureId(props.captureId));
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
430
|
+
const emitInteractionDiagnostics = (diagnostics) => {
|
|
431
|
+
for (const diagnostic of diagnostics) emit("interactionDiagnostic", diagnostic);
|
|
432
|
+
};
|
|
433
|
+
const hasInteractionErrors = () => interactionOverlayFailed || interactionInputDiagnostics.value.some(({ level }) => level === "error") || annotationRuntimeDiagnostics.some(({ level }) => level === "error") || semanticRuntimeDiagnostics.some(({ level }) => level === "error");
|
|
434
|
+
const invalidateInteractionReadiness = () => {
|
|
435
|
+
interactionReadinessRunId += 1;
|
|
436
|
+
if (typeof cancelAnimationFrame === "function") {
|
|
437
|
+
for (const frame of interactionReadinessFrames) cancelAnimationFrame(frame);
|
|
438
|
+
}
|
|
439
|
+
interactionReadinessFrames = [];
|
|
440
|
+
};
|
|
441
|
+
const settleInteractionReadiness = () => {
|
|
442
|
+
invalidateInteractionReadiness();
|
|
443
|
+
if (hasInteractionErrors()) {
|
|
444
|
+
interactionCaptureState.value = "error";
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const runId = interactionReadinessRunId;
|
|
448
|
+
interactionCaptureState.value = "loading";
|
|
449
|
+
void nextTick().then(() => {
|
|
450
|
+
if (runId !== interactionReadinessRunId || hasInteractionErrors()) return;
|
|
451
|
+
const view = containerRef.value?.ownerDocument.defaultView;
|
|
452
|
+
if (!view) {
|
|
453
|
+
interactionCaptureState.value = "idle";
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const firstFrame = view.requestAnimationFrame(() => {
|
|
457
|
+
interactionReadinessFrames = interactionReadinessFrames.filter(
|
|
458
|
+
(frame) => frame !== firstFrame
|
|
459
|
+
);
|
|
460
|
+
if (runId !== interactionReadinessRunId || hasInteractionErrors()) return;
|
|
461
|
+
const secondFrame = view.requestAnimationFrame(() => {
|
|
462
|
+
interactionReadinessFrames = interactionReadinessFrames.filter(
|
|
463
|
+
(frame) => frame !== secondFrame
|
|
464
|
+
);
|
|
465
|
+
if (runId !== interactionReadinessRunId || hasInteractionErrors()) return;
|
|
466
|
+
interactionCaptureState.value = "idle";
|
|
467
|
+
});
|
|
468
|
+
interactionReadinessFrames.push(secondFrame);
|
|
469
|
+
});
|
|
470
|
+
interactionReadinessFrames.push(firstFrame);
|
|
166
471
|
});
|
|
472
|
+
};
|
|
473
|
+
let renderTargetKeySignature = "";
|
|
474
|
+
const syncInteractionRenderTargetReadiness = () => {
|
|
475
|
+
const nextSignature = JSON.stringify([
|
|
476
|
+
...annotationRenderTargets.value.map(({ key }) => `annotation:${key}`),
|
|
477
|
+
...semanticRenderTargets.value.map(({ key }) => `semantic:${key}`)
|
|
478
|
+
]);
|
|
479
|
+
if (nextSignature === renderTargetKeySignature) return;
|
|
480
|
+
renderTargetKeySignature = nextSignature;
|
|
481
|
+
settleInteractionReadiness();
|
|
482
|
+
};
|
|
483
|
+
const syncRuntimeDiagnostics = (source, diagnostics) => {
|
|
484
|
+
const previouslyActiveKeys = /* @__PURE__ */ new Set([
|
|
485
|
+
...activeAnnotationDiagnosticKeys,
|
|
486
|
+
...activeSemanticDiagnosticKeys
|
|
487
|
+
]);
|
|
488
|
+
const nextKeys = new Set(diagnostics.map(interactionDiagnosticKey));
|
|
489
|
+
if (source === "annotation") {
|
|
490
|
+
activeAnnotationDiagnosticKeys = nextKeys;
|
|
491
|
+
annotationRuntimeDiagnostics = diagnostics;
|
|
492
|
+
} else {
|
|
493
|
+
activeSemanticDiagnosticKeys = nextKeys;
|
|
494
|
+
semanticRuntimeDiagnostics = diagnostics;
|
|
495
|
+
}
|
|
496
|
+
emitInteractionDiagnostics(
|
|
497
|
+
diagnostics.filter(
|
|
498
|
+
(diagnostic) => !previouslyActiveKeys.has(interactionDiagnosticKey(diagnostic))
|
|
499
|
+
)
|
|
500
|
+
);
|
|
501
|
+
settleInteractionReadiness();
|
|
502
|
+
};
|
|
503
|
+
const reportOverlayFailure = (message) => {
|
|
504
|
+
interactionOverlayFailed = true;
|
|
505
|
+
interactionCaptureState.value = "error";
|
|
506
|
+
emitInteractionDiagnostics([
|
|
507
|
+
createTileflowVueInteractionDiagnostic("OVERLAY_FAILURE", message)
|
|
508
|
+
]);
|
|
509
|
+
};
|
|
510
|
+
watchEffect(() => {
|
|
511
|
+
assertTileflowMapStyleInputs({ source: props.source, theme: props.theme });
|
|
512
|
+
});
|
|
513
|
+
watchEffect(() => {
|
|
514
|
+
const diagnostics = interactionInputDiagnostics.value;
|
|
515
|
+
const signature = JSON.stringify(diagnostics);
|
|
516
|
+
if (signature !== interactionInputDiagnosticSignature) {
|
|
517
|
+
interactionInputDiagnosticSignature = signature;
|
|
518
|
+
emitInteractionDiagnostics(diagnostics);
|
|
519
|
+
}
|
|
520
|
+
settleInteractionReadiness();
|
|
521
|
+
});
|
|
522
|
+
const refreshManifest = async () => {
|
|
523
|
+
const shouldLoad = shouldLoadManifest.value;
|
|
524
|
+
const requestKey = manifestRequestKey.value;
|
|
167
525
|
if (!shouldLoad) {
|
|
168
|
-
|
|
526
|
+
manifestLoadId += 1;
|
|
527
|
+
loadedManifestMap.value = null;
|
|
528
|
+
manifestResolutionKey.value = requestKey;
|
|
529
|
+
manifestResolutionState.value = "not-needed";
|
|
169
530
|
return;
|
|
170
531
|
}
|
|
171
532
|
const loadId = ++manifestLoadId;
|
|
172
|
-
|
|
533
|
+
loadedManifestMap.value = null;
|
|
534
|
+
manifestResolutionKey.value = requestKey;
|
|
535
|
+
manifestResolutionState.value = "loading";
|
|
173
536
|
try {
|
|
174
|
-
const manifest = await loadTileflowManifest(
|
|
537
|
+
const manifest = await loadTileflowManifest(manifestUrl.value);
|
|
175
538
|
if (loadId === manifestLoadId) {
|
|
176
|
-
|
|
539
|
+
const resolvedMap = manifest && mapName.value ? resolveTileflowManifestMap(manifest, mapName.value) : null;
|
|
540
|
+
loadedManifestMap.value = resolvedMap;
|
|
541
|
+
manifestResolutionState.value = resolvedMap ? "ready" : "error";
|
|
177
542
|
}
|
|
178
543
|
} catch (error) {
|
|
179
544
|
if (loadId === manifestLoadId) {
|
|
180
545
|
console.error("Failed to load Tileflow manifest", error);
|
|
181
|
-
|
|
546
|
+
loadedManifestMap.value = null;
|
|
547
|
+
manifestResolutionState.value = "error";
|
|
182
548
|
}
|
|
183
549
|
}
|
|
184
550
|
};
|
|
@@ -202,70 +568,303 @@ var TileflowMap = defineComponent({
|
|
|
202
568
|
updateImageSize();
|
|
203
569
|
};
|
|
204
570
|
const destroyMap = () => {
|
|
571
|
+
mapLoadId += 1;
|
|
205
572
|
readinessRunId += 1;
|
|
573
|
+
invalidateInteractionReadiness();
|
|
206
574
|
const lifecycle = mapLifecycle;
|
|
575
|
+
const fairUseNotice = mapFairUseNotice;
|
|
207
576
|
const resizeObserver = mapResizeObserver;
|
|
208
577
|
const map = mapRef.value;
|
|
578
|
+
const controller = themeController;
|
|
579
|
+
const annotationInteractionsForMap = annotationRuntime;
|
|
580
|
+
const semanticInteractionsForMap = semanticRuntime;
|
|
581
|
+
const worldRequestBridge = mapWorldRequestBridge;
|
|
582
|
+
const teardownSteps = [
|
|
583
|
+
annotationRuntimeCoordinatorDetach,
|
|
584
|
+
semanticRuntimeCoordinatorDetach,
|
|
585
|
+
annotationRuntimeTargetsUnsubscribe,
|
|
586
|
+
annotationRuntimeDiagnosticsUnsubscribe,
|
|
587
|
+
annotationRuntimeEventsUnsubscribe,
|
|
588
|
+
semanticRuntimeTargetsUnsubscribe,
|
|
589
|
+
semanticRuntimeDiagnosticsUnsubscribe,
|
|
590
|
+
semanticRuntimeEventsUnsubscribe,
|
|
591
|
+
() => annotationInteractionsForMap?.dispose(),
|
|
592
|
+
() => semanticInteractionsForMap?.dispose(),
|
|
593
|
+
() => worldRequestBridge?.dispose(),
|
|
594
|
+
() => fairUseNotice?.dispose(),
|
|
595
|
+
() => lifecycle?.dispose(),
|
|
596
|
+
() => controller?.dispose(),
|
|
597
|
+
() => resizeObserver?.disconnect(),
|
|
598
|
+
() => map?.remove()
|
|
599
|
+
];
|
|
600
|
+
annotationRuntime = null;
|
|
601
|
+
semanticRuntime = null;
|
|
602
|
+
initializeSemanticRuntime = null;
|
|
603
|
+
annotationRuntimeCoordinatorDetach = null;
|
|
604
|
+
annotationRuntimeDiagnosticsUnsubscribe = null;
|
|
605
|
+
annotationRuntimeEventsUnsubscribe = null;
|
|
606
|
+
annotationRuntimeTargetsUnsubscribe = null;
|
|
607
|
+
semanticRuntimeDiagnosticsUnsubscribe = null;
|
|
608
|
+
semanticRuntimeCoordinatorDetach = null;
|
|
609
|
+
semanticRuntimeEventsUnsubscribe = null;
|
|
610
|
+
semanticRuntimeTargetsUnsubscribe = null;
|
|
611
|
+
annotationRenderTargets.value = [];
|
|
612
|
+
semanticRenderTargets.value = [];
|
|
613
|
+
activeAnnotationDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
614
|
+
activeSemanticDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
615
|
+
annotationRuntimeDiagnostics = [];
|
|
616
|
+
semanticRuntimeDiagnostics = [];
|
|
617
|
+
renderTargetKeySignature = "";
|
|
618
|
+
mapFairUseNotice = null;
|
|
209
619
|
mapLifecycle = null;
|
|
210
620
|
mapResizeObserver = null;
|
|
211
621
|
mapRef.value = null;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
622
|
+
mapWorldRequestBridge = null;
|
|
623
|
+
themeController = null;
|
|
624
|
+
interactionRuntimesDisposing = true;
|
|
625
|
+
let teardownError;
|
|
626
|
+
for (const teardown of teardownSteps) {
|
|
215
627
|
try {
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
markerController.clear();
|
|
220
|
-
} finally {
|
|
221
|
-
map?.remove();
|
|
222
|
-
}
|
|
628
|
+
teardown?.();
|
|
629
|
+
} catch (error) {
|
|
630
|
+
teardownError ??= error;
|
|
223
631
|
}
|
|
224
632
|
}
|
|
633
|
+
interactionRuntimesDisposing = false;
|
|
634
|
+
interactionCaptureState.value = hasInteractionErrors() ? "error" : "idle";
|
|
635
|
+
if (teardownError) {
|
|
636
|
+
console.error("Failed to fully dispose the Tileflow map runtime", teardownError);
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
const syncAnnotationRuntime = () => {
|
|
640
|
+
const resolution = annotationResolution.value;
|
|
641
|
+
if (!annotationRuntime) return;
|
|
642
|
+
try {
|
|
643
|
+
annotationRuntime.reconcile(
|
|
644
|
+
resolution.ok && !isImageMode.value ? resolution.annotations : []
|
|
645
|
+
);
|
|
646
|
+
} catch {
|
|
647
|
+
reportOverlayFailure("Unable to reconcile the Tileflow annotation runtime.");
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
const syncSemanticRuntime = () => {
|
|
651
|
+
const resolution = interactionBindingResolution.value;
|
|
652
|
+
const bindings = resolution.ok && !isImageMode.value ? resolution.bindings : [];
|
|
653
|
+
if (bindings.length > 0 && !semanticRuntime) initializeSemanticRuntime?.();
|
|
654
|
+
if (!semanticRuntime) return;
|
|
655
|
+
try {
|
|
656
|
+
semanticRuntime.reconcile(bindings);
|
|
657
|
+
} catch {
|
|
658
|
+
reportOverlayFailure("Unable to reconcile the Tileflow semantic interaction runtime.");
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
const syncCustomRenderers = () => {
|
|
662
|
+
try {
|
|
663
|
+
annotationRuntime?.setCustomRenderers({
|
|
664
|
+
marker: slots.marker !== void 0,
|
|
665
|
+
popup: slots.popup !== void 0,
|
|
666
|
+
tooltip: slots.tooltip !== void 0
|
|
667
|
+
});
|
|
668
|
+
semanticRuntime?.setCustomRenderers({
|
|
669
|
+
popup: slots.popup !== void 0,
|
|
670
|
+
tooltip: slots.tooltip !== void 0
|
|
671
|
+
});
|
|
672
|
+
} catch {
|
|
673
|
+
reportOverlayFailure("Unable to update the Tileflow interaction renderers.");
|
|
674
|
+
}
|
|
225
675
|
};
|
|
226
|
-
const recreateMap = () => {
|
|
676
|
+
const recreateMap = async () => {
|
|
227
677
|
destroyMap();
|
|
228
|
-
|
|
229
|
-
|
|
678
|
+
interactionOverlayFailed = false;
|
|
679
|
+
settleInteractionReadiness();
|
|
680
|
+
mapCaptureState.value = "loading";
|
|
681
|
+
themeTransitionState.value = "idle";
|
|
682
|
+
if (isImageMode.value) {
|
|
683
|
+
await resetImageReadiness();
|
|
230
684
|
return;
|
|
231
685
|
}
|
|
686
|
+
activeRuntimeResource.value = runtimeStyle.value;
|
|
687
|
+
if (!containerRef.value || !runtimeStyle.value) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
const loadId = mapLoadId;
|
|
691
|
+
const container = containerRef.value;
|
|
232
692
|
const runtime = runtimeStyle.value;
|
|
233
|
-
|
|
693
|
+
let maplibregl;
|
|
694
|
+
try {
|
|
695
|
+
[maplibregl] = await Promise.all([
|
|
696
|
+
loadTileflowMapLibre(),
|
|
697
|
+
loadTileflowStyleFonts(runtime.style, {
|
|
698
|
+
fontFaces: runtime.fontFaces
|
|
699
|
+
})
|
|
700
|
+
]);
|
|
701
|
+
} catch (error) {
|
|
702
|
+
if (loadId === mapLoadId) {
|
|
703
|
+
console.error("Failed to load the Tileflow map runtime", error);
|
|
704
|
+
mapCaptureState.value = "error";
|
|
705
|
+
}
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
if (loadId !== mapLoadId || containerRef.value !== container || isImageMode.value) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
234
711
|
const session = createTileflowSessionController({ source: "vue" });
|
|
235
712
|
const sessionStarter = createTileflowSessionStarter({
|
|
236
713
|
getSessionId: () => session.sessionId,
|
|
237
714
|
sessionId: session.sessionId,
|
|
238
715
|
source: "vue"
|
|
239
716
|
});
|
|
717
|
+
mapFairUseNotice = attachTileflowFairUseNotice(container);
|
|
718
|
+
registerTileflowContourProtocol({ addProtocol: maplibregl.addProtocol });
|
|
719
|
+
registerTileflowPmtilesProtocol({ addProtocol: maplibregl.addProtocol });
|
|
720
|
+
mapWorldRequestBridge = registerTileflowWorldRequestBridge({
|
|
721
|
+
addProtocol: maplibregl.addProtocol,
|
|
722
|
+
onNotice: mapFairUseNotice.update
|
|
723
|
+
});
|
|
240
724
|
const map = new maplibregl.Map({
|
|
241
725
|
...props.mapOptions,
|
|
242
|
-
attributionControl: props.mapOptions?.attributionControl ?? {
|
|
726
|
+
attributionControl: props.mapOptions?.attributionControl ?? {
|
|
727
|
+
compact: true
|
|
728
|
+
},
|
|
729
|
+
bearing: resolvedBearing.value,
|
|
243
730
|
center: resolvedCenter.value,
|
|
244
|
-
container
|
|
731
|
+
container,
|
|
245
732
|
interactive: resolvedInteractive.value,
|
|
733
|
+
pitch: resolvedPitch.value,
|
|
246
734
|
style: runtime.style,
|
|
247
735
|
transformRequest: createTileflowTransformRequest({
|
|
248
|
-
getAnalytics: () =>
|
|
736
|
+
getAnalytics: () => resolvedAnalytics.value,
|
|
249
737
|
sessionController: session,
|
|
250
738
|
sessionId: session.sessionId,
|
|
251
|
-
transformRequest: props.mapOptions?.transformRequest ?? void 0
|
|
739
|
+
transformRequest: props.mapOptions?.transformRequest ?? void 0,
|
|
740
|
+
worldRequestBridge: mapWorldRequestBridge
|
|
252
741
|
}),
|
|
253
742
|
zoom: resolvedZoom.value
|
|
254
743
|
});
|
|
255
744
|
mapRef.value = map;
|
|
745
|
+
themeController = runtime.theme === void 0 ? null : createTileflowThemeController({
|
|
746
|
+
initial: runtime,
|
|
747
|
+
map,
|
|
748
|
+
onTransition(transition) {
|
|
749
|
+
emit("themeChange", transition);
|
|
750
|
+
if (transition.phase === "preloading" || transition.phase === "applying") {
|
|
751
|
+
themeTransitionState.value = "loading";
|
|
752
|
+
} else if (transition.phase === "error") {
|
|
753
|
+
themeTransitionState.value = "error";
|
|
754
|
+
} else {
|
|
755
|
+
themeTransitionState.value = "idle";
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
try {
|
|
760
|
+
const mapInteractions = createTileflowMapLibreDomRuntime({
|
|
761
|
+
createMarker({ annotation, element }) {
|
|
762
|
+
element.title = annotation.ariaLabel;
|
|
763
|
+
return new maplibregl.Marker({ element });
|
|
764
|
+
},
|
|
765
|
+
customMarker: slots.marker !== void 0,
|
|
766
|
+
customPopup: slots.popup !== void 0,
|
|
767
|
+
customTooltip: slots.tooltip !== void 0,
|
|
768
|
+
createOverlay({ container: overlayContainer, kind }) {
|
|
769
|
+
return new maplibregl.Popup({
|
|
770
|
+
closeButton: false,
|
|
771
|
+
closeOnClick: false,
|
|
772
|
+
closeOnMove: false,
|
|
773
|
+
focusAfterOpen: false,
|
|
774
|
+
offset: kind === "popup" ? 24 : 18
|
|
775
|
+
}).setDOMContent(overlayContainer);
|
|
776
|
+
},
|
|
777
|
+
document: container.ownerDocument,
|
|
778
|
+
interactionState: interactionCoordinator.getInteractionState(),
|
|
779
|
+
map,
|
|
780
|
+
onInteractionStateChange: interactionCoordinator.requestInteractionState,
|
|
781
|
+
updateMarker(_marker, { annotation, element }) {
|
|
782
|
+
element.title = annotation.ariaLabel;
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
annotationRuntime = mapInteractions;
|
|
786
|
+
annotationRuntimeCoordinatorDetach = interactionCoordinator.attach(
|
|
787
|
+
"annotation",
|
|
788
|
+
mapInteractions
|
|
789
|
+
);
|
|
790
|
+
annotationRuntimeDiagnosticsUnsubscribe = mapInteractions.subscribeDiagnostics(
|
|
791
|
+
(diagnostics) => syncRuntimeDiagnostics("annotation", diagnostics)
|
|
792
|
+
);
|
|
793
|
+
annotationRuntimeEventsUnsubscribe = mapInteractions.subscribeEvents((event) => {
|
|
794
|
+
if (!interactionRuntimesDisposing) emit("interactionEvent", event);
|
|
795
|
+
});
|
|
796
|
+
syncRuntimeDiagnostics("annotation", mapInteractions.getDiagnostics());
|
|
797
|
+
annotationRuntimeTargetsUnsubscribe = mapInteractions.subscribeRenderTargets((targets) => {
|
|
798
|
+
annotationRenderTargets.value = targets;
|
|
799
|
+
syncInteractionRenderTargetReadiness();
|
|
800
|
+
});
|
|
801
|
+
annotationRenderTargets.value = mapInteractions.getRenderTargets();
|
|
802
|
+
syncInteractionRenderTargetReadiness();
|
|
803
|
+
syncAnnotationRuntime();
|
|
804
|
+
} catch {
|
|
805
|
+
reportOverlayFailure("Unable to initialize the Tileflow annotation runtime.");
|
|
806
|
+
}
|
|
807
|
+
initializeSemanticRuntime = () => {
|
|
808
|
+
if (semanticRuntime || interactionRuntimesDisposing || mapRef.value !== map) return;
|
|
809
|
+
try {
|
|
810
|
+
const semanticInteractions = createTileflowMapLibreSemanticDomRuntime({
|
|
811
|
+
cancelFrame: (frame) => cancelAnimationFrame(frame),
|
|
812
|
+
createOverlay({ container: overlayContainer, kind }) {
|
|
813
|
+
return new maplibregl.Popup({
|
|
814
|
+
closeButton: false,
|
|
815
|
+
closeOnClick: false,
|
|
816
|
+
closeOnMove: false,
|
|
817
|
+
focusAfterOpen: false,
|
|
818
|
+
offset: kind === "popup" ? 24 : 18
|
|
819
|
+
}).setDOMContent(overlayContainer);
|
|
820
|
+
},
|
|
821
|
+
customPopup: slots.popup !== void 0,
|
|
822
|
+
customTooltip: slots.tooltip !== void 0,
|
|
823
|
+
document: container.ownerDocument,
|
|
824
|
+
interactionState: interactionCoordinator.getInteractionState(),
|
|
825
|
+
map,
|
|
826
|
+
onInteractionStateChange: interactionCoordinator.requestInteractionState,
|
|
827
|
+
poiMap: createTileflowVuePoiMap(map),
|
|
828
|
+
requestFrame: (callback) => requestAnimationFrame(callback)
|
|
829
|
+
});
|
|
830
|
+
semanticRuntime = semanticInteractions;
|
|
831
|
+
semanticRuntimeCoordinatorDetach = interactionCoordinator.attach(
|
|
832
|
+
"semantic",
|
|
833
|
+
semanticInteractions
|
|
834
|
+
);
|
|
835
|
+
semanticRuntimeDiagnosticsUnsubscribe = semanticInteractions.subscribeDiagnostics(
|
|
836
|
+
(diagnostics) => syncRuntimeDiagnostics("semantic", diagnostics)
|
|
837
|
+
);
|
|
838
|
+
semanticRuntimeEventsUnsubscribe = semanticInteractions.subscribeEvents((event) => {
|
|
839
|
+
if (!interactionRuntimesDisposing) emit("interactionEvent", event);
|
|
840
|
+
});
|
|
841
|
+
syncRuntimeDiagnostics("semantic", semanticInteractions.getDiagnostics());
|
|
842
|
+
semanticRuntimeTargetsUnsubscribe = semanticInteractions.subscribeRenderTargets(
|
|
843
|
+
(targets) => {
|
|
844
|
+
semanticRenderTargets.value = targets;
|
|
845
|
+
syncInteractionRenderTargetReadiness();
|
|
846
|
+
}
|
|
847
|
+
);
|
|
848
|
+
semanticRenderTargets.value = semanticInteractions.getRenderTargets();
|
|
849
|
+
syncInteractionRenderTargetReadiness();
|
|
850
|
+
} catch {
|
|
851
|
+
reportOverlayFailure("Unable to initialize the Tileflow semantic interaction runtime.");
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
syncSemanticRuntime();
|
|
256
855
|
if (resolvedInteractive.value) {
|
|
257
856
|
map.addControl(new maplibregl.NavigationControl(), "top-right");
|
|
258
857
|
}
|
|
259
858
|
mapResizeObserver = new ResizeObserver(() => {
|
|
260
859
|
map.resize();
|
|
261
860
|
});
|
|
262
|
-
mapResizeObserver.observe(
|
|
861
|
+
mapResizeObserver.observe(container);
|
|
263
862
|
mapLifecycle = attachTileflowMapLifecycle({
|
|
264
863
|
getSession: () => {
|
|
265
864
|
const analyticsForLoad = resolvedAnalytics.value;
|
|
266
865
|
return {
|
|
267
866
|
analytics: analyticsForLoad,
|
|
268
|
-
styleId: analyticsForLoad?.styleId ?? (typeof runtime.style === "string" ? runtime.style :
|
|
867
|
+
styleId: analyticsForLoad?.styleId ?? (typeof runtime.style === "string" ? runtime.style : mapName.value)
|
|
269
868
|
};
|
|
270
869
|
},
|
|
271
870
|
map,
|
|
@@ -276,120 +875,174 @@ var TileflowMap = defineComponent({
|
|
|
276
875
|
},
|
|
277
876
|
sessionStarter,
|
|
278
877
|
setState: (state) => {
|
|
279
|
-
|
|
878
|
+
mapCaptureState.value = state;
|
|
280
879
|
},
|
|
281
880
|
subscribe: (subscribedMap, event, listener) => {
|
|
282
881
|
const subscription = subscribedMap.on(event, listener);
|
|
283
882
|
return () => subscription.unsubscribe();
|
|
284
883
|
}
|
|
285
884
|
});
|
|
286
|
-
|
|
885
|
+
if (themeController && runtimeStyle.value && runtimeStyle.value !== runtime) {
|
|
886
|
+
activeRuntimeResource.value = runtimeStyle.value;
|
|
887
|
+
void themeController.setTheme(runtimeStyle.value);
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
const switchTheme = () => {
|
|
891
|
+
if (!themeController) return;
|
|
892
|
+
if (themeResolution.value.error) {
|
|
893
|
+
void themeController.setTheme(themeController.getCurrent());
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
const next = runtimeStyle.value;
|
|
897
|
+
if (!next) return;
|
|
898
|
+
activeRuntimeResource.value = next;
|
|
899
|
+
void themeController.setTheme(next).then((result) => {
|
|
900
|
+
if (result.status === "failed") {
|
|
901
|
+
console.error("Failed to change the Tileflow map theme", result.error);
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
};
|
|
905
|
+
const syncSystemColorSchemeSubscription = () => {
|
|
906
|
+
unsubscribeSystemColorScheme?.();
|
|
907
|
+
unsubscribeSystemColorScheme = null;
|
|
908
|
+
if (props.theme !== "system") return;
|
|
909
|
+
systemColorScheme.value = getTileflowSystemColorScheme();
|
|
910
|
+
unsubscribeSystemColorScheme = subscribeTileflowSystemColorScheme((scheme) => {
|
|
911
|
+
systemColorScheme.value = scheme;
|
|
912
|
+
});
|
|
287
913
|
};
|
|
288
914
|
const syncView = () => {
|
|
289
915
|
mapRef.value?.jumpTo({
|
|
916
|
+
bearing: resolvedBearing.value,
|
|
290
917
|
center: resolvedCenter.value,
|
|
918
|
+
pitch: resolvedPitch.value,
|
|
291
919
|
zoom: resolvedZoom.value
|
|
292
920
|
});
|
|
293
921
|
};
|
|
294
|
-
const syncMarkers = () => {
|
|
295
|
-
const map = mapRef.value;
|
|
296
|
-
if (!map) {
|
|
297
|
-
markerController.clear();
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
markerController.replace(map, props.markers);
|
|
301
|
-
};
|
|
302
922
|
const markImageReady = async (image) => {
|
|
303
923
|
const runId = ++readinessRunId;
|
|
304
924
|
try {
|
|
305
925
|
if (typeof image.decode === "function") await image.decode();
|
|
306
926
|
} catch {
|
|
307
927
|
if (!image.complete || image.naturalWidth === 0) {
|
|
308
|
-
if (runId === readinessRunId)
|
|
928
|
+
if (runId === readinessRunId) mapCaptureState.value = "error";
|
|
309
929
|
return;
|
|
310
930
|
}
|
|
311
931
|
}
|
|
312
|
-
if (runId === readinessRunId)
|
|
932
|
+
if (runId === readinessRunId) {
|
|
933
|
+
mapCaptureState.value = "idle";
|
|
934
|
+
}
|
|
313
935
|
};
|
|
314
936
|
const resetImageReadiness = async () => {
|
|
315
937
|
if (!isImageMode.value) return;
|
|
316
938
|
const runId = ++readinessRunId;
|
|
317
|
-
|
|
939
|
+
activeRuntimeResource.value = runtimeImageUrl.value;
|
|
940
|
+
if (hasInteractionConfiguration.value) {
|
|
941
|
+
mapCaptureState.value = "idle";
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
mapCaptureState.value = "loading";
|
|
318
945
|
await nextTick();
|
|
319
946
|
if (runId !== readinessRunId) return;
|
|
320
947
|
const image = containerRef.value?.querySelector("img");
|
|
321
948
|
if (!image?.complete) return;
|
|
322
949
|
if (image.naturalWidth === 0) {
|
|
323
|
-
|
|
950
|
+
mapCaptureState.value = "error";
|
|
324
951
|
return;
|
|
325
952
|
}
|
|
326
953
|
await markImageReady(image);
|
|
327
954
|
};
|
|
328
955
|
onMounted(() => {
|
|
956
|
+
syncSystemColorSchemeSubscription();
|
|
329
957
|
void refreshManifest();
|
|
330
958
|
updateImageResizeObserver();
|
|
331
|
-
recreateMap();
|
|
959
|
+
void recreateMap();
|
|
332
960
|
void resetImageReadiness();
|
|
333
961
|
});
|
|
334
962
|
onBeforeUnmount(() => {
|
|
963
|
+
unsubscribeSystemColorScheme?.();
|
|
335
964
|
imageResizeObserver?.disconnect();
|
|
336
965
|
destroyMap();
|
|
966
|
+
interactionCoordinator.dispose();
|
|
967
|
+
});
|
|
968
|
+
onUpdated(() => {
|
|
969
|
+
syncCustomRenderers();
|
|
337
970
|
});
|
|
338
971
|
watch(
|
|
339
|
-
() => [
|
|
340
|
-
props.config,
|
|
341
|
-
props.imageUrl,
|
|
342
|
-
props.manifestUrl,
|
|
343
|
-
props.map,
|
|
344
|
-
props.mapStyle,
|
|
345
|
-
props.mode,
|
|
346
|
-
props.preferLocalDev,
|
|
347
|
-
props.styleBaseUrl,
|
|
348
|
-
props.styleUrl
|
|
349
|
-
],
|
|
972
|
+
() => [props.imageUrl, props.mode, props.source],
|
|
350
973
|
() => {
|
|
351
974
|
void refreshManifest();
|
|
352
975
|
}
|
|
353
976
|
);
|
|
354
977
|
watch(
|
|
355
|
-
() =>
|
|
978
|
+
() => props.theme,
|
|
979
|
+
() => syncSystemColorSchemeSubscription()
|
|
980
|
+
);
|
|
981
|
+
watch(
|
|
982
|
+
() => runtimeStyle.value,
|
|
983
|
+
() => switchTheme(),
|
|
984
|
+
{ flush: "post" }
|
|
985
|
+
);
|
|
986
|
+
watch(
|
|
987
|
+
() => [hasInteractionConfiguration.value, isImageMode.value, runtimeImageUrl.value],
|
|
356
988
|
() => {
|
|
357
989
|
void resetImageReadiness();
|
|
358
990
|
},
|
|
359
991
|
{ flush: "post" }
|
|
360
992
|
);
|
|
361
993
|
watch(
|
|
362
|
-
() => [props.imageUrl, props.mode
|
|
994
|
+
() => [props.imageUrl, props.mode],
|
|
363
995
|
() => updateImageResizeObserver(),
|
|
364
996
|
{ flush: "post" }
|
|
365
997
|
);
|
|
366
998
|
watch(
|
|
367
999
|
() => [
|
|
368
|
-
props.config,
|
|
369
1000
|
props.analytics,
|
|
370
1001
|
props.interactive,
|
|
371
|
-
props.map,
|
|
372
1002
|
props.mapOptions,
|
|
373
|
-
props.mapStyle,
|
|
374
1003
|
props.mode,
|
|
375
|
-
props.
|
|
376
|
-
props.styleBaseUrl,
|
|
377
|
-
props.styleUrl,
|
|
378
|
-
props.themes,
|
|
1004
|
+
props.source,
|
|
379
1005
|
manifestMap.value
|
|
380
1006
|
],
|
|
381
|
-
() => recreateMap(),
|
|
1007
|
+
() => void recreateMap(),
|
|
382
1008
|
{ flush: "post" }
|
|
383
1009
|
);
|
|
384
1010
|
watch(
|
|
385
|
-
() => [
|
|
1011
|
+
() => [
|
|
1012
|
+
props.center,
|
|
1013
|
+
props.zoom,
|
|
1014
|
+
props.mapOptions?.bearing,
|
|
1015
|
+
props.mapOptions?.center,
|
|
1016
|
+
props.mapOptions?.pitch,
|
|
1017
|
+
props.mapOptions?.zoom,
|
|
1018
|
+
manifestMap.value?.view
|
|
1019
|
+
],
|
|
386
1020
|
() => syncView(),
|
|
387
1021
|
{ flush: "post" }
|
|
388
1022
|
);
|
|
389
1023
|
watch(
|
|
390
|
-
() => props.
|
|
391
|
-
() =>
|
|
392
|
-
{ flush: "post" }
|
|
1024
|
+
() => props.annotations,
|
|
1025
|
+
() => syncAnnotationRuntime(),
|
|
1026
|
+
{ deep: true, flush: "post" }
|
|
1027
|
+
);
|
|
1028
|
+
watch(
|
|
1029
|
+
() => props.interactions,
|
|
1030
|
+
() => syncSemanticRuntime(),
|
|
1031
|
+
{ deep: true, flush: "post" }
|
|
1032
|
+
);
|
|
1033
|
+
watch(
|
|
1034
|
+
() => props.interactionState,
|
|
1035
|
+
(nextState) => {
|
|
1036
|
+
if (!controlledInteractionOwnership || nextState === void 0 || interactionStateDiagnostics.value.length > 0) {
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
try {
|
|
1040
|
+
interactionCoordinator.setInteractionState(nextState);
|
|
1041
|
+
} catch {
|
|
1042
|
+
reportOverlayFailure("Unable to apply Tileflow interaction state.");
|
|
1043
|
+
}
|
|
1044
|
+
},
|
|
1045
|
+
{ deep: true, flush: "post" }
|
|
393
1046
|
);
|
|
394
1047
|
return () => {
|
|
395
1048
|
const rootAttrs = { ...attrs };
|
|
@@ -397,34 +1050,66 @@ var TileflowMap = defineComponent({
|
|
|
397
1050
|
const styleValue = [attrs.style, frameStyle.value];
|
|
398
1051
|
delete rootAttrs.class;
|
|
399
1052
|
delete rootAttrs.style;
|
|
1053
|
+
const image = isImageMode.value && runtimeImageUrl.value ? h("img", {
|
|
1054
|
+
alt: props.alt,
|
|
1055
|
+
decoding: "async",
|
|
1056
|
+
loading: props.imageLoading,
|
|
1057
|
+
onError: () => {
|
|
1058
|
+
readinessRunId += 1;
|
|
1059
|
+
mapCaptureState.value = "error";
|
|
1060
|
+
},
|
|
1061
|
+
onLoad: (event) => void markImageReady(event.currentTarget),
|
|
1062
|
+
src: runtimeImageUrl.value,
|
|
1063
|
+
style: {
|
|
1064
|
+
display: "block",
|
|
1065
|
+
height: "100%",
|
|
1066
|
+
objectFit: "cover",
|
|
1067
|
+
width: "100%"
|
|
1068
|
+
}
|
|
1069
|
+
}) : void 0;
|
|
1070
|
+
const annotationTeleports = annotationRenderTargets.value.flatMap((target) => {
|
|
1071
|
+
const slot = target.kind === "marker" ? slots.marker : target.kind === "tooltip" ? slots.tooltip : slots.popup;
|
|
1072
|
+
if (!slot) return [];
|
|
1073
|
+
const content = target.annotation[target.kind]?.content;
|
|
1074
|
+
const context = {
|
|
1075
|
+
annotation: target.annotation,
|
|
1076
|
+
close: target.close,
|
|
1077
|
+
content,
|
|
1078
|
+
target: {
|
|
1079
|
+
annotation: target.annotation,
|
|
1080
|
+
coordinate: target.annotation.coordinate,
|
|
1081
|
+
kind: "annotation"
|
|
1082
|
+
},
|
|
1083
|
+
viewName: content?.kind === "view" ? content.name : void 0
|
|
1084
|
+
};
|
|
1085
|
+
return [
|
|
1086
|
+
h(Teleport, { key: `annotation:${target.key}`, to: target.container }, slot(context))
|
|
1087
|
+
];
|
|
1088
|
+
});
|
|
1089
|
+
const semanticTeleports = semanticRenderTargets.value.flatMap((target) => {
|
|
1090
|
+
const slot = target.kind === "tooltip" ? slots.tooltip : slots.popup;
|
|
1091
|
+
if (!slot) return [];
|
|
1092
|
+
const context = {
|
|
1093
|
+
close: target.close,
|
|
1094
|
+
content: target.content,
|
|
1095
|
+
target: target.target,
|
|
1096
|
+
viewName: target.viewName
|
|
1097
|
+
};
|
|
1098
|
+
return [h(Teleport, { key: `semantic:${target.key}`, to: target.container }, slot(context))];
|
|
1099
|
+
});
|
|
400
1100
|
return h(
|
|
401
1101
|
"div",
|
|
402
1102
|
{
|
|
403
1103
|
...rootAttrs,
|
|
404
1104
|
class: classValue,
|
|
405
1105
|
"data-tileflow-capture-id": resolvedCaptureId.value,
|
|
406
|
-
"data-tileflow-map":
|
|
1106
|
+
"data-tileflow-map": mapName.value,
|
|
1107
|
+
"data-tileflow-theme": resolvedThemeName.value,
|
|
407
1108
|
"data-tileflow-state": captureState.value,
|
|
408
1109
|
ref: containerRef,
|
|
409
1110
|
style: styleValue
|
|
410
1111
|
},
|
|
411
|
-
|
|
412
|
-
alt: props.alt,
|
|
413
|
-
decoding: "async",
|
|
414
|
-
loading: props.imageLoading,
|
|
415
|
-
onError: () => {
|
|
416
|
-
readinessRunId += 1;
|
|
417
|
-
captureState.value = "error";
|
|
418
|
-
},
|
|
419
|
-
onLoad: (event) => void markImageReady(event.currentTarget),
|
|
420
|
-
src: runtimeImageUrl.value,
|
|
421
|
-
style: {
|
|
422
|
-
display: "block",
|
|
423
|
-
height: "100%",
|
|
424
|
-
objectFit: "cover",
|
|
425
|
-
width: "100%"
|
|
426
|
-
}
|
|
427
|
-
}) : void 0
|
|
1112
|
+
[image, ...annotationTeleports, ...semanticTeleports]
|
|
428
1113
|
);
|
|
429
1114
|
};
|
|
430
1115
|
}
|
|
@@ -433,6 +1118,26 @@ var index_default = TileflowMap;
|
|
|
433
1118
|
function formatHeight(height) {
|
|
434
1119
|
return typeof height === "number" ? `${height}px` : height;
|
|
435
1120
|
}
|
|
1121
|
+
function interactionDiagnosticKey(diagnostic) {
|
|
1122
|
+
return JSON.stringify([diagnostic.code, diagnostic.message, diagnostic.path, diagnostic.target]);
|
|
1123
|
+
}
|
|
1124
|
+
function createTileflowVuePoiMap(map) {
|
|
1125
|
+
return {
|
|
1126
|
+
getStyle: () => map.getStyle(),
|
|
1127
|
+
on: (event, listener) => map.on(event, listener),
|
|
1128
|
+
queryRenderedFeatures(point, options) {
|
|
1129
|
+
return map.queryRenderedFeatures(point, {
|
|
1130
|
+
layers: [...options.layers]
|
|
1131
|
+
}).map((feature) => ({
|
|
1132
|
+
...feature.id === void 0 ? {} : { id: feature.id },
|
|
1133
|
+
layer: { id: feature.layer.id },
|
|
1134
|
+
...feature.properties === null ? {} : { properties: feature.properties },
|
|
1135
|
+
...feature.source === void 0 ? {} : { source: feature.source },
|
|
1136
|
+
...feature.sourceLayer === void 0 ? {} : { sourceLayer: feature.sourceLayer }
|
|
1137
|
+
}));
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
436
1141
|
export {
|
|
437
1142
|
TileflowMap,
|
|
438
1143
|
index_default as default
|