@tileflow/vue 0.1.0-alpha.13 → 0.1.0-alpha.16

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 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,6 +16,7 @@ import {
15
16
  defaultTileflowManifestUrl,
16
17
  loadTileflowManifest,
17
18
  mergeTileflowAnalytics,
19
+ normalizeTileflowCaptureId,
18
20
  normalizeTileflowStaticImageSize,
19
21
  resolveTileflowAnalyticsRequestUrl,
20
22
  resolveTileflowManifestMap,
@@ -36,6 +38,7 @@ var TileflowMap = defineComponent({
36
38
  },
37
39
  analytics: Object,
38
40
  center: Array,
41
+ captureId: String,
39
42
  className: String,
40
43
  config: Object,
41
44
  height: {
@@ -84,12 +87,14 @@ var TileflowMap = defineComponent({
84
87
  const manifestMap = shallowRef(null);
85
88
  const imageSize = shallowRef(null);
86
89
  const mapRef = shallowRef(null);
90
+ const captureState = ref("loading");
87
91
  const sessionId = createTileflowSessionId();
88
92
  const markerInstances = [];
89
93
  const sessionStarts = /* @__PURE__ */ new Set();
90
94
  let imageResizeObserver = null;
91
95
  let mapResizeObserver = null;
92
96
  let manifestLoadId = 0;
97
+ let readinessRunId = 0;
93
98
  const resolvedMode = computed(
94
99
  () => resolveTileflowMapMode({
95
100
  imageUrl: props.imageUrl,
@@ -138,6 +143,7 @@ var TileflowMap = defineComponent({
138
143
  position: "relative",
139
144
  width: "100%"
140
145
  }));
146
+ const resolvedCaptureId = computed(() => normalizeTileflowCaptureId(props.captureId));
141
147
  const refreshManifest = async () => {
142
148
  const shouldLoad = shouldLoadTileflowManifest({
143
149
  config: props.config,
@@ -186,6 +192,7 @@ var TileflowMap = defineComponent({
186
192
  updateImageSize();
187
193
  };
188
194
  const destroyMap = () => {
195
+ readinessRunId += 1;
189
196
  mapResizeObserver?.disconnect();
190
197
  mapResizeObserver = null;
191
198
  clearMarkers(markerInstances);
@@ -194,6 +201,7 @@ var TileflowMap = defineComponent({
194
201
  };
195
202
  const recreateMap = () => {
196
203
  destroyMap();
204
+ captureState.value = "loading";
197
205
  if (!containerRef.value || !runtimeStyle.value || isImageMode.value) {
198
206
  return;
199
207
  }
@@ -237,6 +245,28 @@ var TileflowMap = defineComponent({
237
245
  }
238
246
  }
239
247
  });
248
+ map.on("dataloading", () => {
249
+ readinessRunId += 1;
250
+ captureState.value = "loading";
251
+ });
252
+ map.on("styledataloading", () => {
253
+ readinessRunId += 1;
254
+ captureState.value = "loading";
255
+ });
256
+ map.on("idle", () => {
257
+ const runId = ++readinessRunId;
258
+ requestAnimationFrame(() => {
259
+ requestAnimationFrame(() => {
260
+ if (runId === readinessRunId && mapRef.value === map) {
261
+ captureState.value = "idle";
262
+ }
263
+ });
264
+ });
265
+ });
266
+ map.on("error", () => {
267
+ readinessRunId += 1;
268
+ captureState.value = "error";
269
+ });
240
270
  syncMarkers();
241
271
  };
242
272
  const syncView = () => {
@@ -259,10 +289,37 @@ var TileflowMap = defineComponent({
259
289
  markerInstances.push(markerInstance);
260
290
  }
261
291
  };
292
+ const markImageReady = async (image) => {
293
+ const runId = ++readinessRunId;
294
+ try {
295
+ if (typeof image.decode === "function") await image.decode();
296
+ } catch {
297
+ if (!image.complete || image.naturalWidth === 0) {
298
+ if (runId === readinessRunId) captureState.value = "error";
299
+ return;
300
+ }
301
+ }
302
+ if (runId === readinessRunId) captureState.value = "idle";
303
+ };
304
+ const resetImageReadiness = async () => {
305
+ if (!isImageMode.value) return;
306
+ const runId = ++readinessRunId;
307
+ captureState.value = "loading";
308
+ await nextTick();
309
+ if (runId !== readinessRunId) return;
310
+ const image = containerRef.value?.querySelector("img");
311
+ if (!image?.complete) return;
312
+ if (image.naturalWidth === 0) {
313
+ captureState.value = "error";
314
+ return;
315
+ }
316
+ await markImageReady(image);
317
+ };
262
318
  onMounted(() => {
263
319
  void refreshManifest();
264
320
  updateImageResizeObserver();
265
321
  recreateMap();
322
+ void resetImageReadiness();
266
323
  });
267
324
  onBeforeUnmount(() => {
268
325
  imageResizeObserver?.disconnect();
@@ -284,6 +341,13 @@ var TileflowMap = defineComponent({
284
341
  void refreshManifest();
285
342
  }
286
343
  );
344
+ watch(
345
+ () => [isImageMode.value, runtimeImageUrl.value],
346
+ () => {
347
+ void resetImageReadiness();
348
+ },
349
+ { flush: "post" }
350
+ );
287
351
  watch(
288
352
  () => [props.imageUrl, props.mode, props.preferLocalDev],
289
353
  () => updateImageResizeObserver(),
@@ -329,6 +393,9 @@ var TileflowMap = defineComponent({
329
393
  {
330
394
  ...rootAttrs,
331
395
  class: classValue,
396
+ "data-tileflow-capture-id": resolvedCaptureId.value,
397
+ "data-tileflow-map": props.map,
398
+ "data-tileflow-state": captureState.value,
332
399
  ref: containerRef,
333
400
  style: styleValue
334
401
  },
@@ -336,6 +403,11 @@ var TileflowMap = defineComponent({
336
403
  alt: props.alt,
337
404
  decoding: "async",
338
405
  loading: props.imageLoading,
406
+ onError: () => {
407
+ readinessRunId += 1;
408
+ captureState.value = "error";
409
+ },
410
+ onLoad: (event) => void markImageReady(event.currentTarget),
339
411
  src: runtimeImageUrl.value,
340
412
  style: {
341
413
  display: "block",
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@tileflow/vue",
3
- "version": "0.1.0-alpha.13",
3
+ "version": "0.1.0-alpha.16",
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.13"
39
+ "@tileflow/core": "0.1.0-alpha.16"
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
  }