@tileflow/svelte 0.1.0-alpha.16 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/svelte",
3
- "version": "0.1.0-alpha.16",
3
+ "version": "0.1.0-alpha.17",
4
4
  "description": "Svelte component for rendering Tileflow maps with MapLibre.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -42,7 +42,7 @@
42
42
  "access": "public"
43
43
  },
44
44
  "dependencies": {
45
- "@tileflow/core": "0.1.0-alpha.16"
45
+ "@tileflow/core": ">=0.1.0-alpha.17 <0.1.0-beta.0"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "maplibre-gl": ">=5",
@@ -3,8 +3,6 @@
3
3
  type LngLatLike,
4
4
  type Map as MapLibreMap,
5
5
  type MapOptions as MapLibreMapOptions,
6
- type RequestParameters,
7
- type RequestTransformFunction,
8
6
  type StyleSpecification,
9
7
  } from 'maplibre-gl';
10
8
  import {createEventDispatcher, onMount, tick} from 'svelte';
@@ -15,13 +13,11 @@
15
13
  mergeTileflowAnalytics,
16
14
  normalizeTileflowCaptureId,
17
15
  normalizeTileflowStaticImageSize,
18
- resolveTileflowAnalyticsRequestUrl,
19
16
  resolveTileflowManifestMap,
20
17
  resolveTileflowMapMode,
21
18
  resolveTileflowRuntimeStyle,
22
19
  resolveTileflowStaticImageUrl,
23
20
  shouldLoadTileflowManifest,
24
- startTileflowSession,
25
21
  type MapLibreStyle,
26
22
  type TileflowAnalytics,
27
23
  type TileflowConfig,
@@ -29,6 +25,13 @@
29
25
  type TileflowProjectThemes,
30
26
  type TileflowRuntimeManifestMap,
31
27
  } from '@tileflow/core';
28
+ import {
29
+ attachTileflowMapLifecycle,
30
+ createTileflowMarkerController,
31
+ createTileflowSessionStarter,
32
+ createTileflowTransformRequest,
33
+ type TileflowMapLifecycleAttachment,
34
+ } from '@tileflow/core/browser';
32
35
 
33
36
  export let alt = '';
34
37
  export let analytics: TileflowAnalytics | undefined = undefined;
@@ -63,7 +66,7 @@
63
66
  let imageSize: {height: number; width: number} | null = null;
64
67
  let imageResizeObserver: ResizeObserver | null = null;
65
68
  let mapInstance: MapLibreMap | null = null;
66
- let markerInstances: maplibregl.Marker[] = [];
69
+ let mapLifecycleAttachment: TileflowMapLifecycleAttachment | null = null;
67
70
  let mapResizeObserver: ResizeObserver | null = null;
68
71
  let manifestMap: TileflowRuntimeManifestMap | null = null;
69
72
  let manifestLoadId = 0;
@@ -73,7 +76,22 @@
73
76
 
74
77
  const dispatch = createEventDispatcher<{load: MapLibreMap}>();
75
78
  const sessionId = createTileflowSessionId();
76
- const sessionStarts = new Set<string>();
79
+ const sessionStarter = createTileflowSessionStarter({sessionId, source: 'svelte'});
80
+ const markerController = createTileflowMarkerController<
81
+ MapLibreMap,
82
+ TileflowMapMarker,
83
+ maplibregl.Marker
84
+ >({
85
+ attach(markerInstance, targetMap, definition) {
86
+ markerInstance.setLngLat(definition.coordinates).addTo(targetMap);
87
+ markerInstance.getElement().title = definition.label ?? definition.id;
88
+ },
89
+ create: (definition) =>
90
+ new maplibregl.Marker({
91
+ color: definition.color ?? '#C6A15B',
92
+ }),
93
+ remove: (markerInstance) => markerInstance.remove(),
94
+ });
77
95
 
78
96
  $: resolvedMode = resolveTileflowMapMode({imageUrl, mode, preferLocalDev});
79
97
  $: resolvedCaptureId = normalizeTileflowCaptureId(captureId);
@@ -225,11 +243,22 @@
225
243
 
226
244
  function destroyMap() {
227
245
  readinessRunId += 1;
246
+ const lifecycleAttachment = mapLifecycleAttachment;
247
+ const currentMap = mapInstance;
248
+ mapLifecycleAttachment = null;
249
+ mapInstance = null;
228
250
  mapResizeObserver?.disconnect();
229
251
  mapResizeObserver = null;
230
- clearMarkers();
231
- mapInstance?.remove();
232
- mapInstance = null;
252
+
253
+ try {
254
+ lifecycleAttachment?.dispose();
255
+ } finally {
256
+ try {
257
+ markerController.dispose();
258
+ } finally {
259
+ currentMap?.remove();
260
+ }
261
+ }
233
262
  }
234
263
 
235
264
  function recreateMap() {
@@ -248,11 +277,13 @@
248
277
  container,
249
278
  interactive: resolvedInteractive,
250
279
  style: runtime.style as StyleSpecification | string,
251
- transformRequest: createTransformRequest(
252
- () => resolvedAnalytics,
280
+ transformRequest: createTileflowTransformRequest({
281
+ always: true,
282
+ asyncAnalyticsTiming: 'request',
283
+ getAnalytics: () => resolvedAnalytics,
253
284
  sessionId,
254
- mapOptions?.transformRequest,
255
- ),
285
+ transformRequest: mapOptions?.transformRequest ?? undefined,
286
+ }),
256
287
  zoom: resolvedZoom,
257
288
  });
258
289
 
@@ -267,47 +298,29 @@
267
298
  });
268
299
  mapResizeObserver.observe(container);
269
300
 
270
- maplibreMap.on('load', () => {
271
- dispatch('load', maplibreMap);
272
- const analyticsForLoad = resolvedAnalytics;
273
- const styleId =
274
- analyticsForLoad?.styleId ?? (typeof runtime.style === 'string' ? runtime.style : map);
275
-
276
- if (analyticsForLoad?.mapId) {
277
- const sessionKey = `${sessionId}:${analyticsForLoad.mapId}:${styleId ?? ''}`;
278
-
279
- if (!sessionStarts.has(sessionKey)) {
280
- sessionStarts.add(sessionKey);
281
- startTileflowSession(analyticsForLoad, {
282
- mapId: analyticsForLoad.mapId,
283
- sessionId,
284
- source: 'svelte',
285
- styleId,
286
- });
287
- }
288
- }
289
- });
290
- maplibreMap.on('dataloading', () => {
291
- readinessRunId += 1;
292
- captureState = 'loading';
293
- });
294
- maplibreMap.on('styledataloading', () => {
295
- readinessRunId += 1;
296
- captureState = 'loading';
297
- });
298
- maplibreMap.on('idle', () => {
299
- const runId = ++readinessRunId;
300
- requestAnimationFrame(() => {
301
- requestAnimationFrame(() => {
302
- if (runId === readinessRunId && mapInstance === maplibreMap) {
303
- captureState = 'idle';
304
- }
305
- });
306
- });
307
- });
308
- maplibreMap.on('error', () => {
309
- readinessRunId += 1;
310
- captureState = 'error';
301
+ mapLifecycleAttachment = attachTileflowMapLifecycle<MapLibreMap, number>({
302
+ getSession: () => {
303
+ const analyticsForLoad = resolvedAnalytics;
304
+ return {
305
+ analytics: analyticsForLoad,
306
+ styleId:
307
+ analyticsForLoad?.styleId ?? (typeof runtime.style === 'string' ? runtime.style : map),
308
+ };
309
+ },
310
+ map: maplibreMap,
311
+ onLoad: () => dispatch('load', maplibreMap),
312
+ scheduler: {
313
+ cancelFrame: (frame) => cancelAnimationFrame(frame),
314
+ requestFrame: (callback) => requestAnimationFrame(callback),
315
+ },
316
+ sessionStarter,
317
+ setState: (state) => {
318
+ captureState = state;
319
+ },
320
+ subscribe: (targetMap, event, listener) => {
321
+ const subscription = targetMap.on(event, listener);
322
+ return () => subscription.unsubscribe();
323
+ },
311
324
  });
312
325
 
313
326
  syncMarkers();
@@ -321,30 +334,12 @@
321
334
  }
322
335
 
323
336
  function syncMarkers() {
324
- clearMarkers();
325
-
326
337
  if (!mapInstance) {
338
+ markerController.clear();
327
339
  return;
328
340
  }
329
341
 
330
- markerInstances = markers.map((marker) => {
331
- const markerInstance = new maplibregl.Marker({
332
- color: marker.color ?? '#C6A15B',
333
- })
334
- .setLngLat(marker.coordinates)
335
- .addTo(mapInstance!);
336
-
337
- markerInstance.getElement().title = marker.label ?? marker.id;
338
- return markerInstance;
339
- });
340
- }
341
-
342
- function clearMarkers() {
343
- for (const marker of markerInstances) {
344
- marker.remove();
345
- }
346
-
347
- markerInstances = [];
342
+ markerController.replace(mapInstance, markers);
348
343
  }
349
344
 
350
345
  async function handleImageLoad(event: Event) {
@@ -383,46 +378,6 @@
383
378
  captureState = 'error';
384
379
  }
385
380
 
386
- function createTransformRequest(
387
- getAnalytics: () => TileflowAnalytics | undefined,
388
- session: string,
389
- userTransformRequest?: TileflowMapOptions['transformRequest'],
390
- ): RequestTransformFunction | undefined {
391
- return (url, resourceType) => {
392
- const request = userTransformRequest?.(url, resourceType);
393
- const analyticsInput = getAnalytics();
394
-
395
- if (isPromiseLike(request)) {
396
- return request.then((resolvedRequest) =>
397
- applyTileflowAnalyticsRequest(url, resolvedRequest, analyticsInput, session) ??
398
- resolvedRequest,
399
- );
400
- }
401
-
402
- return applyTileflowAnalyticsRequest(url, request, analyticsInput, session);
403
- };
404
- }
405
-
406
- function applyTileflowAnalyticsRequest(
407
- url: string,
408
- request: RequestParameters | undefined,
409
- analyticsInput: TileflowAnalytics | undefined,
410
- session: string,
411
- ): RequestParameters | undefined {
412
- const requestUrl = request?.url ?? url;
413
- const nextUrl = resolveTileflowAnalyticsRequestUrl(requestUrl, analyticsInput, session);
414
-
415
- if (!nextUrl) {
416
- return request;
417
- }
418
-
419
- return request ? {...request, url: nextUrl} : {url: nextUrl};
420
- }
421
-
422
- function isPromiseLike<T>(value: T | Promise<T> | undefined): value is Promise<T> {
423
- return Boolean(value && typeof (value as Promise<T>).then === 'function');
424
- }
425
-
426
381
  function formatHeight(value: number | string): string {
427
382
  return typeof value === 'number' ? `${value}px` : value;
428
383
  }