@tileflow/vue 0.1.0-alpha.18 → 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 -127
- package/dist/index.js +882 -157
- 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,129 +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,
|
|
83
|
-
tileBaseUrl: String,
|
|
225
|
+
theme: String,
|
|
84
226
|
zoom: Number
|
|
85
227
|
},
|
|
86
228
|
emits: {
|
|
87
|
-
|
|
229
|
+
interactionDiagnostic: (_diagnostic) => true,
|
|
230
|
+
interactionEvent: (_event) => true,
|
|
231
|
+
load: (_map) => true,
|
|
232
|
+
themeChange: (_transition) => true,
|
|
233
|
+
"update:interactionState": (_state) => true
|
|
88
234
|
},
|
|
89
|
-
setup(props, { attrs, emit }) {
|
|
235
|
+
setup(props, { attrs, emit, slots }) {
|
|
90
236
|
const containerRef = ref(null);
|
|
91
|
-
const
|
|
237
|
+
const loadedManifestMap = shallowRef(null);
|
|
238
|
+
const manifestResolutionKey = ref("");
|
|
239
|
+
const manifestResolutionState = ref("loading");
|
|
92
240
|
const imageSize = shallowRef(null);
|
|
93
241
|
const mapRef = shallowRef(null);
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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();
|
|
105
269
|
let imageResizeObserver = null;
|
|
270
|
+
let mapFairUseNotice = null;
|
|
106
271
|
let mapLifecycle = null;
|
|
107
272
|
let mapResizeObserver = null;
|
|
273
|
+
let mapWorldRequestBridge = null;
|
|
274
|
+
let themeController = null;
|
|
275
|
+
let unsubscribeSystemColorScheme = null;
|
|
276
|
+
let mapLoadId = 0;
|
|
108
277
|
let manifestLoadId = 0;
|
|
109
278
|
let readinessRunId = 0;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
|
115
289
|
})
|
|
116
290
|
);
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
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
|
+
);
|
|
120
390
|
const resolvedCenter = computed(
|
|
121
|
-
() => props.center ?? props.mapOptions?.center ??
|
|
391
|
+
() => props.center ?? props.mapOptions?.center ?? manifestCenter.value
|
|
122
392
|
);
|
|
123
|
-
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);
|
|
124
398
|
const resolvedInteractive = computed(
|
|
125
399
|
() => props.interactive ?? props.mapOptions?.interactive ?? true
|
|
126
400
|
);
|
|
127
401
|
const runtimeStyle = computed(
|
|
128
|
-
() => isImageMode.value ? null : resolveTileflowRuntimeStyle({
|
|
129
|
-
|
|
402
|
+
() => isImageMode.value || themeResolution.value.error ? null : resolveTileflowRuntimeStyle({
|
|
403
|
+
colorScheme: systemColorScheme.value,
|
|
130
404
|
manifestMap: manifestMap.value,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
style: props.mapStyle,
|
|
134
|
-
styleBaseUrl: props.styleBaseUrl,
|
|
135
|
-
styleUrl: props.styleUrl,
|
|
136
|
-
themes: props.themes,
|
|
137
|
-
tileBaseUrl: props.tileBaseUrl
|
|
405
|
+
source: props.source,
|
|
406
|
+
theme: themeResolution.value.name
|
|
138
407
|
})
|
|
139
408
|
);
|
|
140
409
|
const resolvedAnalytics = computed(
|
|
141
410
|
() => mergeTileflowAnalytics(props.analytics, runtimeStyle.value?.analytics)
|
|
142
411
|
);
|
|
143
412
|
const runtimeImageUrl = computed(
|
|
144
|
-
() => props.imageUrl ?? (isImageMode.value ? resolveTileflowStaticImageUrl({
|
|
413
|
+
() => props.imageUrl ?? (isImageMode.value && !themeResolution.value.error ? resolveTileflowStaticImageUrl({
|
|
145
414
|
center: imageCenter.value,
|
|
415
|
+
colorScheme: systemColorScheme.value,
|
|
146
416
|
imageSize: imageSize.value,
|
|
147
417
|
manifestMap: manifestMap.value,
|
|
418
|
+
theme: themeResolution.value.name,
|
|
148
419
|
zoom: imageZoom.value
|
|
149
420
|
}) : void 0)
|
|
150
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
|
+
});
|
|
151
445
|
const frameStyle = computed(() => ({
|
|
152
|
-
height: formatHeight(props.height),
|
|
446
|
+
height: formatHeight(props.height ?? 420),
|
|
153
447
|
minHeight: "240px",
|
|
154
448
|
overflow: "hidden",
|
|
155
449
|
position: "relative",
|
|
156
450
|
width: "100%"
|
|
157
451
|
}));
|
|
158
452
|
const resolvedCaptureId = computed(() => normalizeTileflowCaptureId(props.captureId));
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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);
|
|
168
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;
|
|
169
548
|
if (!shouldLoad) {
|
|
170
|
-
|
|
549
|
+
manifestLoadId += 1;
|
|
550
|
+
loadedManifestMap.value = null;
|
|
551
|
+
manifestResolutionKey.value = requestKey;
|
|
552
|
+
manifestResolutionState.value = "not-needed";
|
|
171
553
|
return;
|
|
172
554
|
}
|
|
173
555
|
const loadId = ++manifestLoadId;
|
|
174
|
-
|
|
556
|
+
loadedManifestMap.value = null;
|
|
557
|
+
manifestResolutionKey.value = requestKey;
|
|
558
|
+
manifestResolutionState.value = "loading";
|
|
175
559
|
try {
|
|
176
|
-
const manifest = await loadTileflowManifest(
|
|
560
|
+
const manifest = await loadTileflowManifest(manifestUrl.value);
|
|
177
561
|
if (loadId === manifestLoadId) {
|
|
178
|
-
|
|
562
|
+
const resolvedMap = manifest && mapName.value ? resolveTileflowManifestMap(manifest, mapName.value) : null;
|
|
563
|
+
loadedManifestMap.value = resolvedMap;
|
|
564
|
+
manifestResolutionState.value = resolvedMap ? "ready" : "error";
|
|
179
565
|
}
|
|
180
566
|
} catch (error) {
|
|
181
567
|
if (loadId === manifestLoadId) {
|
|
182
568
|
console.error("Failed to load Tileflow manifest", error);
|
|
183
|
-
|
|
569
|
+
loadedManifestMap.value = null;
|
|
570
|
+
manifestResolutionState.value = "error";
|
|
184
571
|
}
|
|
185
572
|
}
|
|
186
573
|
};
|
|
@@ -204,70 +591,303 @@ var TileflowMap = defineComponent({
|
|
|
204
591
|
updateImageSize();
|
|
205
592
|
};
|
|
206
593
|
const destroyMap = () => {
|
|
594
|
+
mapLoadId += 1;
|
|
207
595
|
readinessRunId += 1;
|
|
596
|
+
invalidateInteractionReadiness();
|
|
208
597
|
const lifecycle = mapLifecycle;
|
|
598
|
+
const fairUseNotice = mapFairUseNotice;
|
|
209
599
|
const resizeObserver = mapResizeObserver;
|
|
210
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;
|
|
211
642
|
mapLifecycle = null;
|
|
212
643
|
mapResizeObserver = null;
|
|
213
644
|
mapRef.value = null;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
645
|
+
mapWorldRequestBridge = null;
|
|
646
|
+
themeController = null;
|
|
647
|
+
interactionRuntimesDisposing = true;
|
|
648
|
+
let teardownError;
|
|
649
|
+
for (const teardown of teardownSteps) {
|
|
217
650
|
try {
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
markerController.clear();
|
|
222
|
-
} finally {
|
|
223
|
-
map?.remove();
|
|
224
|
-
}
|
|
651
|
+
teardown?.();
|
|
652
|
+
} catch (error) {
|
|
653
|
+
teardownError ??= error;
|
|
225
654
|
}
|
|
226
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
|
+
}
|
|
227
684
|
};
|
|
228
|
-
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 () => {
|
|
229
701
|
destroyMap();
|
|
230
|
-
|
|
231
|
-
|
|
702
|
+
interactionOverlayFailed = false;
|
|
703
|
+
settleInteractionReadiness();
|
|
704
|
+
mapCaptureState.value = "loading";
|
|
705
|
+
themeTransitionState.value = "idle";
|
|
706
|
+
if (isImageMode.value) {
|
|
707
|
+
await resetImageReadiness();
|
|
232
708
|
return;
|
|
233
709
|
}
|
|
710
|
+
activeRuntimeResource.value = runtimeStyle.value;
|
|
711
|
+
if (!containerRef.value || !runtimeStyle.value) {
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
const loadId = mapLoadId;
|
|
715
|
+
const container = containerRef.value;
|
|
234
716
|
const runtime = runtimeStyle.value;
|
|
235
|
-
|
|
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
|
+
}
|
|
236
735
|
const session = createTileflowSessionController({ source: "vue" });
|
|
237
736
|
const sessionStarter = createTileflowSessionStarter({
|
|
238
737
|
getSessionId: () => session.sessionId,
|
|
239
738
|
sessionId: session.sessionId,
|
|
240
739
|
source: "vue"
|
|
241
740
|
});
|
|
741
|
+
mapFairUseNotice = attachTileflowFairUseNotice(container);
|
|
742
|
+
registerTileflowContourProtocol({ addProtocol: maplibregl.addProtocol });
|
|
743
|
+
mapWorldRequestBridge = registerTileflowWorldRequestBridge({
|
|
744
|
+
addProtocol: maplibregl.addProtocol,
|
|
745
|
+
onNotice: mapFairUseNotice.update
|
|
746
|
+
});
|
|
242
747
|
const map = new maplibregl.Map({
|
|
243
748
|
...props.mapOptions,
|
|
244
|
-
attributionControl: props.mapOptions?.attributionControl ?? {
|
|
749
|
+
attributionControl: props.mapOptions?.attributionControl ?? {
|
|
750
|
+
compact: true
|
|
751
|
+
},
|
|
752
|
+
bearing: resolvedBearing.value,
|
|
245
753
|
center: resolvedCenter.value,
|
|
246
|
-
container
|
|
754
|
+
container,
|
|
247
755
|
interactive: resolvedInteractive.value,
|
|
756
|
+
pitch: resolvedPitch.value,
|
|
248
757
|
style: runtime.style,
|
|
249
758
|
transformRequest: createTileflowTransformRequest({
|
|
250
|
-
getAnalytics: () =>
|
|
759
|
+
getAnalytics: () => resolvedAnalytics.value,
|
|
251
760
|
sessionController: session,
|
|
252
761
|
sessionId: session.sessionId,
|
|
253
|
-
transformRequest: props.mapOptions?.transformRequest ?? void 0
|
|
762
|
+
transformRequest: props.mapOptions?.transformRequest ?? void 0,
|
|
763
|
+
worldRequestBridge: mapWorldRequestBridge
|
|
254
764
|
}),
|
|
255
765
|
zoom: resolvedZoom.value
|
|
256
766
|
});
|
|
257
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();
|
|
258
878
|
if (resolvedInteractive.value) {
|
|
259
879
|
map.addControl(new maplibregl.NavigationControl(), "top-right");
|
|
260
880
|
}
|
|
261
881
|
mapResizeObserver = new ResizeObserver(() => {
|
|
262
882
|
map.resize();
|
|
263
883
|
});
|
|
264
|
-
mapResizeObserver.observe(
|
|
884
|
+
mapResizeObserver.observe(container);
|
|
265
885
|
mapLifecycle = attachTileflowMapLifecycle({
|
|
266
886
|
getSession: () => {
|
|
267
887
|
const analyticsForLoad = resolvedAnalytics.value;
|
|
268
888
|
return {
|
|
269
889
|
analytics: analyticsForLoad,
|
|
270
|
-
styleId: analyticsForLoad?.styleId ?? (typeof runtime.style === "string" ? runtime.style :
|
|
890
|
+
styleId: analyticsForLoad?.styleId ?? (typeof runtime.style === "string" ? runtime.style : mapName.value)
|
|
271
891
|
};
|
|
272
892
|
},
|
|
273
893
|
map,
|
|
@@ -278,121 +898,174 @@ var TileflowMap = defineComponent({
|
|
|
278
898
|
},
|
|
279
899
|
sessionStarter,
|
|
280
900
|
setState: (state) => {
|
|
281
|
-
|
|
901
|
+
mapCaptureState.value = state;
|
|
282
902
|
},
|
|
283
903
|
subscribe: (subscribedMap, event, listener) => {
|
|
284
904
|
const subscription = subscribedMap.on(event, listener);
|
|
285
905
|
return () => subscription.unsubscribe();
|
|
286
906
|
}
|
|
287
907
|
});
|
|
288
|
-
|
|
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
|
+
});
|
|
289
936
|
};
|
|
290
937
|
const syncView = () => {
|
|
291
938
|
mapRef.value?.jumpTo({
|
|
939
|
+
bearing: resolvedBearing.value,
|
|
292
940
|
center: resolvedCenter.value,
|
|
941
|
+
pitch: resolvedPitch.value,
|
|
293
942
|
zoom: resolvedZoom.value
|
|
294
943
|
});
|
|
295
944
|
};
|
|
296
|
-
const syncMarkers = () => {
|
|
297
|
-
const map = mapRef.value;
|
|
298
|
-
if (!map) {
|
|
299
|
-
markerController.clear();
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
markerController.replace(map, props.markers);
|
|
303
|
-
};
|
|
304
945
|
const markImageReady = async (image) => {
|
|
305
946
|
const runId = ++readinessRunId;
|
|
306
947
|
try {
|
|
307
948
|
if (typeof image.decode === "function") await image.decode();
|
|
308
949
|
} catch {
|
|
309
950
|
if (!image.complete || image.naturalWidth === 0) {
|
|
310
|
-
if (runId === readinessRunId)
|
|
951
|
+
if (runId === readinessRunId) mapCaptureState.value = "error";
|
|
311
952
|
return;
|
|
312
953
|
}
|
|
313
954
|
}
|
|
314
|
-
if (runId === readinessRunId)
|
|
955
|
+
if (runId === readinessRunId) {
|
|
956
|
+
mapCaptureState.value = "idle";
|
|
957
|
+
}
|
|
315
958
|
};
|
|
316
959
|
const resetImageReadiness = async () => {
|
|
317
960
|
if (!isImageMode.value) return;
|
|
318
961
|
const runId = ++readinessRunId;
|
|
319
|
-
|
|
962
|
+
activeRuntimeResource.value = runtimeImageUrl.value;
|
|
963
|
+
if (hasInteractionConfiguration.value) {
|
|
964
|
+
mapCaptureState.value = "idle";
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
mapCaptureState.value = "loading";
|
|
320
968
|
await nextTick();
|
|
321
969
|
if (runId !== readinessRunId) return;
|
|
322
970
|
const image = containerRef.value?.querySelector("img");
|
|
323
971
|
if (!image?.complete) return;
|
|
324
972
|
if (image.naturalWidth === 0) {
|
|
325
|
-
|
|
973
|
+
mapCaptureState.value = "error";
|
|
326
974
|
return;
|
|
327
975
|
}
|
|
328
976
|
await markImageReady(image);
|
|
329
977
|
};
|
|
330
978
|
onMounted(() => {
|
|
979
|
+
syncSystemColorSchemeSubscription();
|
|
331
980
|
void refreshManifest();
|
|
332
981
|
updateImageResizeObserver();
|
|
333
|
-
recreateMap();
|
|
982
|
+
void recreateMap();
|
|
334
983
|
void resetImageReadiness();
|
|
335
984
|
});
|
|
336
985
|
onBeforeUnmount(() => {
|
|
986
|
+
unsubscribeSystemColorScheme?.();
|
|
337
987
|
imageResizeObserver?.disconnect();
|
|
338
988
|
destroyMap();
|
|
989
|
+
interactionCoordinator.dispose();
|
|
990
|
+
});
|
|
991
|
+
onUpdated(() => {
|
|
992
|
+
syncCustomRenderers();
|
|
339
993
|
});
|
|
340
994
|
watch(
|
|
341
|
-
() => [
|
|
342
|
-
props.config,
|
|
343
|
-
props.imageUrl,
|
|
344
|
-
props.manifestUrl,
|
|
345
|
-
props.map,
|
|
346
|
-
props.mapStyle,
|
|
347
|
-
props.mode,
|
|
348
|
-
props.preferLocalDev,
|
|
349
|
-
props.styleBaseUrl,
|
|
350
|
-
props.styleUrl
|
|
351
|
-
],
|
|
995
|
+
() => [props.imageUrl, props.mode, props.source],
|
|
352
996
|
() => {
|
|
353
997
|
void refreshManifest();
|
|
354
998
|
}
|
|
355
999
|
);
|
|
356
1000
|
watch(
|
|
357
|
-
() =>
|
|
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],
|
|
358
1011
|
() => {
|
|
359
1012
|
void resetImageReadiness();
|
|
360
1013
|
},
|
|
361
1014
|
{ flush: "post" }
|
|
362
1015
|
);
|
|
363
1016
|
watch(
|
|
364
|
-
() => [props.imageUrl, props.mode
|
|
1017
|
+
() => [props.imageUrl, props.mode],
|
|
365
1018
|
() => updateImageResizeObserver(),
|
|
366
1019
|
{ flush: "post" }
|
|
367
1020
|
);
|
|
368
1021
|
watch(
|
|
369
1022
|
() => [
|
|
370
|
-
props.config,
|
|
371
1023
|
props.analytics,
|
|
372
1024
|
props.interactive,
|
|
373
|
-
props.map,
|
|
374
1025
|
props.mapOptions,
|
|
375
|
-
props.mapStyle,
|
|
376
1026
|
props.mode,
|
|
377
|
-
props.
|
|
378
|
-
props.styleBaseUrl,
|
|
379
|
-
props.styleUrl,
|
|
380
|
-
props.themes,
|
|
381
|
-
props.tileBaseUrl,
|
|
1027
|
+
props.source,
|
|
382
1028
|
manifestMap.value
|
|
383
1029
|
],
|
|
384
|
-
() => recreateMap(),
|
|
1030
|
+
() => void recreateMap(),
|
|
385
1031
|
{ flush: "post" }
|
|
386
1032
|
);
|
|
387
1033
|
watch(
|
|
388
|
-
() => [
|
|
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
|
+
],
|
|
389
1043
|
() => syncView(),
|
|
390
1044
|
{ flush: "post" }
|
|
391
1045
|
);
|
|
392
1046
|
watch(
|
|
393
|
-
() => props.markers,
|
|
394
|
-
() =>
|
|
395
|
-
{ 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" }
|
|
396
1069
|
);
|
|
397
1070
|
return () => {
|
|
398
1071
|
const rootAttrs = { ...attrs };
|
|
@@ -400,34 +1073,66 @@ var TileflowMap = defineComponent({
|
|
|
400
1073
|
const styleValue = [attrs.style, frameStyle.value];
|
|
401
1074
|
delete rootAttrs.class;
|
|
402
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
|
+
});
|
|
403
1123
|
return h(
|
|
404
1124
|
"div",
|
|
405
1125
|
{
|
|
406
1126
|
...rootAttrs,
|
|
407
1127
|
class: classValue,
|
|
408
1128
|
"data-tileflow-capture-id": resolvedCaptureId.value,
|
|
409
|
-
"data-tileflow-map":
|
|
1129
|
+
"data-tileflow-map": mapName.value,
|
|
1130
|
+
"data-tileflow-theme": resolvedThemeName.value,
|
|
410
1131
|
"data-tileflow-state": captureState.value,
|
|
411
1132
|
ref: containerRef,
|
|
412
1133
|
style: styleValue
|
|
413
1134
|
},
|
|
414
|
-
|
|
415
|
-
alt: props.alt,
|
|
416
|
-
decoding: "async",
|
|
417
|
-
loading: props.imageLoading,
|
|
418
|
-
onError: () => {
|
|
419
|
-
readinessRunId += 1;
|
|
420
|
-
captureState.value = "error";
|
|
421
|
-
},
|
|
422
|
-
onLoad: (event) => void markImageReady(event.currentTarget),
|
|
423
|
-
src: runtimeImageUrl.value,
|
|
424
|
-
style: {
|
|
425
|
-
display: "block",
|
|
426
|
-
height: "100%",
|
|
427
|
-
objectFit: "cover",
|
|
428
|
-
width: "100%"
|
|
429
|
-
}
|
|
430
|
-
}) : void 0
|
|
1135
|
+
[image, ...annotationTeleports, ...semanticTeleports]
|
|
431
1136
|
);
|
|
432
1137
|
};
|
|
433
1138
|
}
|
|
@@ -436,6 +1141,26 @@ var index_default = TileflowMap;
|
|
|
436
1141
|
function formatHeight(height) {
|
|
437
1142
|
return typeof height === "number" ? `${height}px` : height;
|
|
438
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
|
+
}
|
|
439
1164
|
export {
|
|
440
1165
|
TileflowMap,
|
|
441
1166
|
index_default as default
|