@tileflow/svelte 0.1.0-alpha.16 → 0.1.0-alpha.18
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 +4 -0
- package/package.json +2 -2
- package/src/TileflowMap.svelte +77 -117
package/README.md
CHANGED
|
@@ -26,6 +26,10 @@ Tileflow resolves from the map name, manifest, or explicit style props. Direct
|
|
|
26
26
|
Tileflow props such as `center`, `zoom`, and `interactive` take priority when
|
|
27
27
|
provided.
|
|
28
28
|
|
|
29
|
+
Hosted maps automatically preflight a short-lived commercial session grant before eligible
|
|
30
|
+
resources. Setting `analytics={{enabled: false}}` disables the optional beacon only; it does not
|
|
31
|
+
remove hosted authorization or override a user `mapOptions.transformRequest` callback.
|
|
32
|
+
|
|
29
33
|
## Headless capture readiness
|
|
30
34
|
|
|
31
35
|
Use `captureId` to disambiguate repeated named maps:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/svelte",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.18",
|
|
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.
|
|
45
|
+
"@tileflow/core": ">=0.1.0-alpha.18 <0.1.0-beta.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"maplibre-gl": ">=5",
|
package/src/TileflowMap.svelte
CHANGED
|
@@ -3,25 +3,21 @@
|
|
|
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';
|
|
11
9
|
import {
|
|
12
|
-
|
|
10
|
+
createTileflowSessionController,
|
|
13
11
|
defaultTileflowManifestUrl,
|
|
14
12
|
loadTileflowManifest,
|
|
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
|
|
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;
|
|
@@ -72,8 +75,21 @@
|
|
|
72
75
|
let readinessRunId = 0;
|
|
73
76
|
|
|
74
77
|
const dispatch = createEventDispatcher<{load: MapLibreMap}>();
|
|
75
|
-
const
|
|
76
|
-
|
|
78
|
+
const markerController = createTileflowMarkerController<
|
|
79
|
+
MapLibreMap,
|
|
80
|
+
TileflowMapMarker,
|
|
81
|
+
maplibregl.Marker
|
|
82
|
+
>({
|
|
83
|
+
attach(markerInstance, targetMap, definition) {
|
|
84
|
+
markerInstance.setLngLat(definition.coordinates).addTo(targetMap);
|
|
85
|
+
markerInstance.getElement().title = definition.label ?? definition.id;
|
|
86
|
+
},
|
|
87
|
+
create: (definition) =>
|
|
88
|
+
new maplibregl.Marker({
|
|
89
|
+
color: definition.color ?? '#C6A15B',
|
|
90
|
+
}),
|
|
91
|
+
remove: (markerInstance) => markerInstance.remove(),
|
|
92
|
+
});
|
|
77
93
|
|
|
78
94
|
$: resolvedMode = resolveTileflowMapMode({imageUrl, mode, preferLocalDev});
|
|
79
95
|
$: resolvedCaptureId = normalizeTileflowCaptureId(captureId);
|
|
@@ -225,11 +241,22 @@
|
|
|
225
241
|
|
|
226
242
|
function destroyMap() {
|
|
227
243
|
readinessRunId += 1;
|
|
244
|
+
const lifecycleAttachment = mapLifecycleAttachment;
|
|
245
|
+
const currentMap = mapInstance;
|
|
246
|
+
mapLifecycleAttachment = null;
|
|
247
|
+
mapInstance = null;
|
|
228
248
|
mapResizeObserver?.disconnect();
|
|
229
249
|
mapResizeObserver = null;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
lifecycleAttachment?.dispose();
|
|
253
|
+
} finally {
|
|
254
|
+
try {
|
|
255
|
+
markerController.dispose();
|
|
256
|
+
} finally {
|
|
257
|
+
currentMap?.remove();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
233
260
|
}
|
|
234
261
|
|
|
235
262
|
function recreateMap() {
|
|
@@ -241,6 +268,12 @@
|
|
|
241
268
|
}
|
|
242
269
|
|
|
243
270
|
const runtime = runtimeStyle;
|
|
271
|
+
const session = createTileflowSessionController({source: 'svelte'});
|
|
272
|
+
const sessionStarter = createTileflowSessionStarter({
|
|
273
|
+
getSessionId: () => session.sessionId,
|
|
274
|
+
sessionId: session.sessionId,
|
|
275
|
+
source: 'svelte',
|
|
276
|
+
});
|
|
244
277
|
const maplibreMap = new maplibregl.Map({
|
|
245
278
|
...mapOptions,
|
|
246
279
|
attributionControl: mapOptions?.attributionControl ?? {compact: true},
|
|
@@ -248,11 +281,14 @@
|
|
|
248
281
|
container,
|
|
249
282
|
interactive: resolvedInteractive,
|
|
250
283
|
style: runtime.style as StyleSpecification | string,
|
|
251
|
-
transformRequest:
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
284
|
+
transformRequest: createTileflowTransformRequest({
|
|
285
|
+
always: true,
|
|
286
|
+
asyncAnalyticsTiming: 'request',
|
|
287
|
+
getAnalytics: () => resolvedAnalytics,
|
|
288
|
+
sessionController: session,
|
|
289
|
+
sessionId: session.sessionId,
|
|
290
|
+
transformRequest: mapOptions?.transformRequest ?? undefined,
|
|
291
|
+
}),
|
|
256
292
|
zoom: resolvedZoom,
|
|
257
293
|
});
|
|
258
294
|
|
|
@@ -267,47 +303,29 @@
|
|
|
267
303
|
});
|
|
268
304
|
mapResizeObserver.observe(container);
|
|
269
305
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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';
|
|
306
|
+
mapLifecycleAttachment = attachTileflowMapLifecycle<MapLibreMap, number>({
|
|
307
|
+
getSession: () => {
|
|
308
|
+
const analyticsForLoad = resolvedAnalytics;
|
|
309
|
+
return {
|
|
310
|
+
analytics: analyticsForLoad,
|
|
311
|
+
styleId:
|
|
312
|
+
analyticsForLoad?.styleId ?? (typeof runtime.style === 'string' ? runtime.style : map),
|
|
313
|
+
};
|
|
314
|
+
},
|
|
315
|
+
map: maplibreMap,
|
|
316
|
+
onLoad: () => dispatch('load', maplibreMap),
|
|
317
|
+
scheduler: {
|
|
318
|
+
cancelFrame: (frame) => cancelAnimationFrame(frame),
|
|
319
|
+
requestFrame: (callback) => requestAnimationFrame(callback),
|
|
320
|
+
},
|
|
321
|
+
sessionStarter,
|
|
322
|
+
setState: (state) => {
|
|
323
|
+
captureState = state;
|
|
324
|
+
},
|
|
325
|
+
subscribe: (targetMap, event, listener) => {
|
|
326
|
+
const subscription = targetMap.on(event, listener);
|
|
327
|
+
return () => subscription.unsubscribe();
|
|
328
|
+
},
|
|
311
329
|
});
|
|
312
330
|
|
|
313
331
|
syncMarkers();
|
|
@@ -321,30 +339,12 @@
|
|
|
321
339
|
}
|
|
322
340
|
|
|
323
341
|
function syncMarkers() {
|
|
324
|
-
clearMarkers();
|
|
325
|
-
|
|
326
342
|
if (!mapInstance) {
|
|
343
|
+
markerController.clear();
|
|
327
344
|
return;
|
|
328
345
|
}
|
|
329
346
|
|
|
330
|
-
|
|
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 = [];
|
|
347
|
+
markerController.replace(mapInstance, markers);
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
async function handleImageLoad(event: Event) {
|
|
@@ -383,46 +383,6 @@
|
|
|
383
383
|
captureState = 'error';
|
|
384
384
|
}
|
|
385
385
|
|
|
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
386
|
function formatHeight(value: number | string): string {
|
|
427
387
|
return typeof value === 'number' ? `${value}px` : value;
|
|
428
388
|
}
|