@tileflow/vue 0.1.0-alpha.14 → 0.1.0-alpha.17
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/README.md +13 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +116 -65
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -28,4 +28,17 @@ Tileflow resolves from the map name, manifest, or explicit style props. Direct
|
|
|
28
28
|
Tileflow props such as `center`, `zoom`, and `interactive` take priority when
|
|
29
29
|
provided.
|
|
30
30
|
|
|
31
|
+
## Headless capture readiness
|
|
32
|
+
|
|
33
|
+
Pass `capture-id` when a page contains multiple copies of the same named map:
|
|
34
|
+
|
|
35
|
+
```vue
|
|
36
|
+
<TileflowMap map="madrid" capture-id="checkout-map" />
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The root exposes `data-tileflow-map`, optional `data-tileflow-capture-id`, and
|
|
40
|
+
`data-tileflow-state="loading|idle|error"`. It becomes idle only after MapLibre idle plus two
|
|
41
|
+
animation frames (or image decode/load in image mode), returns to loading for new style/data work,
|
|
42
|
+
and marks terminal load errors. Application capture selects exactly one ready target.
|
|
43
|
+
|
|
31
44
|
Docs: https://tileflow.dev/docs
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ type TileflowMapProps = {
|
|
|
9
9
|
alt?: string;
|
|
10
10
|
analytics?: TileflowAnalytics;
|
|
11
11
|
center?: [number, number];
|
|
12
|
+
captureId?: string;
|
|
12
13
|
className?: string;
|
|
13
14
|
config?: TileflowConfig;
|
|
14
15
|
height?: number | string;
|
|
@@ -35,6 +36,7 @@ declare const TileflowMap: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
35
36
|
};
|
|
36
37
|
analytics: PropType<TileflowAnalytics>;
|
|
37
38
|
center: PropType<[number, number]>;
|
|
39
|
+
captureId: StringConstructor;
|
|
38
40
|
className: StringConstructor;
|
|
39
41
|
config: PropType<TileflowConfig>;
|
|
40
42
|
height: {
|
|
@@ -85,6 +87,7 @@ declare const TileflowMap: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
85
87
|
};
|
|
86
88
|
analytics: PropType<TileflowAnalytics>;
|
|
87
89
|
center: PropType<[number, number]>;
|
|
90
|
+
captureId: StringConstructor;
|
|
88
91
|
className: StringConstructor;
|
|
89
92
|
config: PropType<TileflowConfig>;
|
|
90
93
|
height: {
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
computed,
|
|
5
5
|
defineComponent,
|
|
6
6
|
h,
|
|
7
|
+
nextTick,
|
|
7
8
|
onBeforeUnmount,
|
|
8
9
|
onMounted,
|
|
9
10
|
ref,
|
|
@@ -15,15 +16,20 @@ import {
|
|
|
15
16
|
defaultTileflowManifestUrl,
|
|
16
17
|
loadTileflowManifest,
|
|
17
18
|
mergeTileflowAnalytics,
|
|
19
|
+
normalizeTileflowCaptureId,
|
|
18
20
|
normalizeTileflowStaticImageSize,
|
|
19
|
-
resolveTileflowAnalyticsRequestUrl,
|
|
20
21
|
resolveTileflowManifestMap,
|
|
21
22
|
resolveTileflowMapMode,
|
|
22
23
|
resolveTileflowRuntimeStyle,
|
|
23
24
|
resolveTileflowStaticImageUrl,
|
|
24
|
-
shouldLoadTileflowManifest
|
|
25
|
-
startTileflowSession
|
|
25
|
+
shouldLoadTileflowManifest
|
|
26
26
|
} from "@tileflow/core";
|
|
27
|
+
import {
|
|
28
|
+
attachTileflowMapLifecycle,
|
|
29
|
+
createTileflowMarkerController,
|
|
30
|
+
createTileflowSessionStarter,
|
|
31
|
+
createTileflowTransformRequest
|
|
32
|
+
} from "@tileflow/core/browser";
|
|
27
33
|
var defaultCenter = [0, 20];
|
|
28
34
|
var defaultZoom = 2;
|
|
29
35
|
var TileflowMap = defineComponent({
|
|
@@ -36,6 +42,7 @@ var TileflowMap = defineComponent({
|
|
|
36
42
|
},
|
|
37
43
|
analytics: Object,
|
|
38
44
|
center: Array,
|
|
45
|
+
captureId: String,
|
|
39
46
|
className: String,
|
|
40
47
|
config: Object,
|
|
41
48
|
height: {
|
|
@@ -84,12 +91,27 @@ var TileflowMap = defineComponent({
|
|
|
84
91
|
const manifestMap = shallowRef(null);
|
|
85
92
|
const imageSize = shallowRef(null);
|
|
86
93
|
const mapRef = shallowRef(null);
|
|
94
|
+
const captureState = ref("loading");
|
|
87
95
|
const sessionId = createTileflowSessionId();
|
|
88
|
-
const
|
|
89
|
-
|
|
96
|
+
const markerController = createTileflowMarkerController({
|
|
97
|
+
attach(markerInstance, map, marker) {
|
|
98
|
+
markerInstance.setLngLat(marker.coordinates).addTo(map);
|
|
99
|
+
markerInstance.getElement().title = marker.label ?? marker.id;
|
|
100
|
+
},
|
|
101
|
+
create: (marker) => new maplibregl.Marker({
|
|
102
|
+
color: marker.color ?? "#C6A15B"
|
|
103
|
+
}),
|
|
104
|
+
remove: (marker) => marker.remove()
|
|
105
|
+
});
|
|
106
|
+
const sessionStarter = createTileflowSessionStarter({
|
|
107
|
+
sessionId,
|
|
108
|
+
source: "vue"
|
|
109
|
+
});
|
|
90
110
|
let imageResizeObserver = null;
|
|
111
|
+
let mapLifecycle = null;
|
|
91
112
|
let mapResizeObserver = null;
|
|
92
113
|
let manifestLoadId = 0;
|
|
114
|
+
let readinessRunId = 0;
|
|
93
115
|
const resolvedMode = computed(
|
|
94
116
|
() => resolveTileflowMapMode({
|
|
95
117
|
imageUrl: props.imageUrl,
|
|
@@ -138,6 +160,7 @@ var TileflowMap = defineComponent({
|
|
|
138
160
|
position: "relative",
|
|
139
161
|
width: "100%"
|
|
140
162
|
}));
|
|
163
|
+
const resolvedCaptureId = computed(() => normalizeTileflowCaptureId(props.captureId));
|
|
141
164
|
const refreshManifest = async () => {
|
|
142
165
|
const shouldLoad = shouldLoadTileflowManifest({
|
|
143
166
|
config: props.config,
|
|
@@ -186,18 +209,35 @@ var TileflowMap = defineComponent({
|
|
|
186
209
|
updateImageSize();
|
|
187
210
|
};
|
|
188
211
|
const destroyMap = () => {
|
|
189
|
-
|
|
212
|
+
readinessRunId += 1;
|
|
213
|
+
const lifecycle = mapLifecycle;
|
|
214
|
+
const resizeObserver = mapResizeObserver;
|
|
215
|
+
const map = mapRef.value;
|
|
216
|
+
mapLifecycle = null;
|
|
190
217
|
mapResizeObserver = null;
|
|
191
|
-
clearMarkers(markerInstances);
|
|
192
|
-
mapRef.value?.remove();
|
|
193
218
|
mapRef.value = null;
|
|
219
|
+
try {
|
|
220
|
+
lifecycle?.dispose();
|
|
221
|
+
} finally {
|
|
222
|
+
try {
|
|
223
|
+
resizeObserver?.disconnect();
|
|
224
|
+
} finally {
|
|
225
|
+
try {
|
|
226
|
+
markerController.clear();
|
|
227
|
+
} finally {
|
|
228
|
+
map?.remove();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
194
232
|
};
|
|
195
233
|
const recreateMap = () => {
|
|
196
234
|
destroyMap();
|
|
235
|
+
captureState.value = "loading";
|
|
197
236
|
if (!containerRef.value || !runtimeStyle.value || isImageMode.value) {
|
|
198
237
|
return;
|
|
199
238
|
}
|
|
200
239
|
const runtime = runtimeStyle.value;
|
|
240
|
+
const analyticsForMap = resolvedAnalytics.value;
|
|
201
241
|
const map = new maplibregl.Map({
|
|
202
242
|
...props.mapOptions,
|
|
203
243
|
attributionControl: props.mapOptions?.attributionControl ?? { compact: true },
|
|
@@ -205,11 +245,11 @@ var TileflowMap = defineComponent({
|
|
|
205
245
|
container: containerRef.value,
|
|
206
246
|
interactive: resolvedInteractive.value,
|
|
207
247
|
style: runtime.style,
|
|
208
|
-
transformRequest:
|
|
209
|
-
|
|
248
|
+
transformRequest: createTileflowTransformRequest({
|
|
249
|
+
getAnalytics: () => analyticsForMap,
|
|
210
250
|
sessionId,
|
|
211
|
-
props.mapOptions?.transformRequest
|
|
212
|
-
),
|
|
251
|
+
transformRequest: props.mapOptions?.transformRequest ?? void 0
|
|
252
|
+
}),
|
|
213
253
|
zoom: resolvedZoom.value
|
|
214
254
|
});
|
|
215
255
|
mapRef.value = map;
|
|
@@ -220,21 +260,27 @@ var TileflowMap = defineComponent({
|
|
|
220
260
|
map.resize();
|
|
221
261
|
});
|
|
222
262
|
mapResizeObserver.observe(containerRef.value);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
263
|
+
mapLifecycle = attachTileflowMapLifecycle({
|
|
264
|
+
getSession: () => {
|
|
265
|
+
const analyticsForLoad = resolvedAnalytics.value;
|
|
266
|
+
return {
|
|
267
|
+
analytics: analyticsForLoad,
|
|
268
|
+
styleId: analyticsForLoad?.styleId ?? (typeof runtime.style === "string" ? runtime.style : props.map)
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
map,
|
|
272
|
+
onLoad: (loadedMap) => emit("load", loadedMap),
|
|
273
|
+
scheduler: {
|
|
274
|
+
cancelFrame: (frame) => cancelAnimationFrame(frame),
|
|
275
|
+
requestFrame: (callback) => requestAnimationFrame(callback)
|
|
276
|
+
},
|
|
277
|
+
sessionStarter,
|
|
278
|
+
setState: (state) => {
|
|
279
|
+
captureState.value = state;
|
|
280
|
+
},
|
|
281
|
+
subscribe: (subscribedMap, event, listener) => {
|
|
282
|
+
const subscription = subscribedMap.on(event, listener);
|
|
283
|
+
return () => subscription.unsubscribe();
|
|
238
284
|
}
|
|
239
285
|
});
|
|
240
286
|
syncMarkers();
|
|
@@ -247,22 +293,43 @@ var TileflowMap = defineComponent({
|
|
|
247
293
|
};
|
|
248
294
|
const syncMarkers = () => {
|
|
249
295
|
const map = mapRef.value;
|
|
250
|
-
clearMarkers(markerInstances);
|
|
251
296
|
if (!map) {
|
|
297
|
+
markerController.clear();
|
|
252
298
|
return;
|
|
253
299
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
300
|
+
markerController.replace(map, props.markers);
|
|
301
|
+
};
|
|
302
|
+
const markImageReady = async (image) => {
|
|
303
|
+
const runId = ++readinessRunId;
|
|
304
|
+
try {
|
|
305
|
+
if (typeof image.decode === "function") await image.decode();
|
|
306
|
+
} catch {
|
|
307
|
+
if (!image.complete || image.naturalWidth === 0) {
|
|
308
|
+
if (runId === readinessRunId) captureState.value = "error";
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (runId === readinessRunId) captureState.value = "idle";
|
|
313
|
+
};
|
|
314
|
+
const resetImageReadiness = async () => {
|
|
315
|
+
if (!isImageMode.value) return;
|
|
316
|
+
const runId = ++readinessRunId;
|
|
317
|
+
captureState.value = "loading";
|
|
318
|
+
await nextTick();
|
|
319
|
+
if (runId !== readinessRunId) return;
|
|
320
|
+
const image = containerRef.value?.querySelector("img");
|
|
321
|
+
if (!image?.complete) return;
|
|
322
|
+
if (image.naturalWidth === 0) {
|
|
323
|
+
captureState.value = "error";
|
|
324
|
+
return;
|
|
260
325
|
}
|
|
326
|
+
await markImageReady(image);
|
|
261
327
|
};
|
|
262
328
|
onMounted(() => {
|
|
263
329
|
void refreshManifest();
|
|
264
330
|
updateImageResizeObserver();
|
|
265
331
|
recreateMap();
|
|
332
|
+
void resetImageReadiness();
|
|
266
333
|
});
|
|
267
334
|
onBeforeUnmount(() => {
|
|
268
335
|
imageResizeObserver?.disconnect();
|
|
@@ -284,6 +351,13 @@ var TileflowMap = defineComponent({
|
|
|
284
351
|
void refreshManifest();
|
|
285
352
|
}
|
|
286
353
|
);
|
|
354
|
+
watch(
|
|
355
|
+
() => [isImageMode.value, runtimeImageUrl.value],
|
|
356
|
+
() => {
|
|
357
|
+
void resetImageReadiness();
|
|
358
|
+
},
|
|
359
|
+
{ flush: "post" }
|
|
360
|
+
);
|
|
287
361
|
watch(
|
|
288
362
|
() => [props.imageUrl, props.mode, props.preferLocalDev],
|
|
289
363
|
() => updateImageResizeObserver(),
|
|
@@ -329,6 +403,9 @@ var TileflowMap = defineComponent({
|
|
|
329
403
|
{
|
|
330
404
|
...rootAttrs,
|
|
331
405
|
class: classValue,
|
|
406
|
+
"data-tileflow-capture-id": resolvedCaptureId.value,
|
|
407
|
+
"data-tileflow-map": props.map,
|
|
408
|
+
"data-tileflow-state": captureState.value,
|
|
332
409
|
ref: containerRef,
|
|
333
410
|
style: styleValue
|
|
334
411
|
},
|
|
@@ -336,6 +413,11 @@ var TileflowMap = defineComponent({
|
|
|
336
413
|
alt: props.alt,
|
|
337
414
|
decoding: "async",
|
|
338
415
|
loading: props.imageLoading,
|
|
416
|
+
onError: () => {
|
|
417
|
+
readinessRunId += 1;
|
|
418
|
+
captureState.value = "error";
|
|
419
|
+
},
|
|
420
|
+
onLoad: (event) => void markImageReady(event.currentTarget),
|
|
339
421
|
src: runtimeImageUrl.value,
|
|
340
422
|
style: {
|
|
341
423
|
display: "block",
|
|
@@ -349,40 +431,9 @@ var TileflowMap = defineComponent({
|
|
|
349
431
|
}
|
|
350
432
|
});
|
|
351
433
|
var index_default = TileflowMap;
|
|
352
|
-
function createTransformRequest(analytics, sessionId, userTransformRequest) {
|
|
353
|
-
if ((!analytics || analytics.enabled === false) && !userTransformRequest) {
|
|
354
|
-
return void 0;
|
|
355
|
-
}
|
|
356
|
-
return (url, resourceType) => {
|
|
357
|
-
const request = userTransformRequest?.(url, resourceType);
|
|
358
|
-
if (isPromiseLike(request)) {
|
|
359
|
-
return request.then(
|
|
360
|
-
(resolvedRequest) => applyTileflowAnalyticsRequest(url, resolvedRequest, analytics, sessionId) ?? resolvedRequest
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
return applyTileflowAnalyticsRequest(url, request, analytics, sessionId);
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
function applyTileflowAnalyticsRequest(url, request, analytics, sessionId) {
|
|
367
|
-
const requestUrl = request?.url ?? url;
|
|
368
|
-
const nextUrl = resolveTileflowAnalyticsRequestUrl(requestUrl, analytics, sessionId);
|
|
369
|
-
if (!nextUrl) {
|
|
370
|
-
return request;
|
|
371
|
-
}
|
|
372
|
-
return request ? { ...request, url: nextUrl } : { url: nextUrl };
|
|
373
|
-
}
|
|
374
|
-
function isPromiseLike(value) {
|
|
375
|
-
return Boolean(value && typeof value.then === "function");
|
|
376
|
-
}
|
|
377
434
|
function formatHeight(height) {
|
|
378
435
|
return typeof height === "number" ? `${height}px` : height;
|
|
379
436
|
}
|
|
380
|
-
function clearMarkers(markerInstances) {
|
|
381
|
-
for (const marker of markerInstances) {
|
|
382
|
-
marker.remove();
|
|
383
|
-
}
|
|
384
|
-
markerInstances.splice(0);
|
|
385
|
-
}
|
|
386
437
|
export {
|
|
387
438
|
TileflowMap,
|
|
388
439
|
index_default as default
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/vue",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.17",
|
|
4
4
|
"description": "Vue component for rendering Tileflow maps with MapLibre.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"homepage": "https://tileflow.dev",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/tileflow/tileflow.git",
|
|
10
|
+
"url": "git+https://github.com/tileflow/tileflow-sdk.git",
|
|
11
11
|
"directory": "packages/vue"
|
|
12
12
|
},
|
|
13
13
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/tileflow/tileflow/issues"
|
|
14
|
+
"url": "https://github.com/tileflow/tileflow-sdk/issues"
|
|
15
15
|
},
|
|
16
16
|
"keywords": [
|
|
17
17
|
"tileflow",
|
|
@@ -36,18 +36,22 @@
|
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@tileflow/core": "0.1.0-alpha.
|
|
39
|
+
"@tileflow/core": ">=0.1.0-alpha.17 <0.1.0-beta.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"maplibre-gl": ">=5",
|
|
43
43
|
"vue": ">=3.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
+
"@vue/server-renderer": "3.5.39",
|
|
46
47
|
"maplibre-gl": "^5.24.0",
|
|
48
|
+
"tsx": "^4.23.1",
|
|
47
49
|
"vue": "^3.5.26"
|
|
48
50
|
},
|
|
49
51
|
"scripts": {
|
|
50
52
|
"build": "tsup src/index.ts --format esm --dts --clean --external vue --external maplibre-gl",
|
|
53
|
+
"test": "tsx --test test/*.test.ts",
|
|
54
|
+
"verify": "pnpm run build && pnpm run test",
|
|
51
55
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
52
56
|
"lint": "tsc -p tsconfig.json --noEmit"
|
|
53
57
|
}
|