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