@tileflow/svelte 0.1.0-alpha.34 → 0.1.0-alpha.36
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 +27 -1
- package/package.json +4 -4
- package/src/TileflowMap.svelte +76 -6
- package/src/index.d.ts +15 -0
package/README.md
CHANGED
|
@@ -16,6 +16,12 @@ npm install @tileflow/svelte@alpha @tileflow/core@alpha @tileflow/maps@alpha "ma
|
|
|
16
16
|
npm install --save-dev --save-exact tileflow@alpha
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
Prepared styles may retain relative asset URLs. The component loads them through Core's shared
|
|
20
|
+
resource loader before creating the map and before changing or blending themes. Sprites, fonts,
|
|
21
|
+
data and model resources resolve against their declaring documents. Pending document loads are
|
|
22
|
+
cancelled when the map is replaced or unmounted; `mapOptions.transformRequest` also applies to
|
|
23
|
+
style and HTTP TileJSON requests.
|
|
24
|
+
|
|
19
25
|
## Prepare a map
|
|
20
26
|
|
|
21
27
|
Create `tileflow.config.ts` at the application root:
|
|
@@ -94,7 +100,7 @@ Add `timeOfDay="dusk"` beside `theme` to select a map's optional `day`, `dawn`,
|
|
|
94
100
|
`night` preset. For Streets, `theme="light"` with this preset selects the existing `dusk` palette.
|
|
95
101
|
Maps without this capability ignore the option; omitting it keeps the selected theme unchanged.
|
|
96
102
|
Changing it uses the existing theme transition. `themeBlend` takes precedence. See the
|
|
97
|
-
[authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/
|
|
103
|
+
[authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/REFERENCE.md#style-with-themes-and-semantic-modules).
|
|
98
104
|
|
|
99
105
|
Theme and `timeOfDay` changes cross-fade over 450 ms by default, respecting reduced motion.
|
|
100
106
|
`themeTransition={{duration: 700}}` customizes the duration; use `duration: 0` for an immediate change.
|
|
@@ -193,6 +199,26 @@ snippets reconciles existing runtimes rather than recreating MapLibre.
|
|
|
193
199
|
Text/field descriptors render as text. A `view` names an application snippet; it is not serialized
|
|
194
200
|
HTML. Keep tooltips non-interactive and put buttons, links, and forms in popups.
|
|
195
201
|
|
|
202
|
+
## Cluster points and draw density
|
|
203
|
+
|
|
204
|
+
Use `annotationClusters` for up to 1,000 annotations, or `dataSources` and `dataOverlays` for larger
|
|
205
|
+
GeoJSON sets. Clicking a cluster zooms in and reports a `cluster` target through `onInteractionEvent`.
|
|
206
|
+
|
|
207
|
+
<!-- docs:check -->
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import {clusterOverlay, geojsonSource} from '@tileflow/core/runtime';
|
|
211
|
+
|
|
212
|
+
export const dataSources = {stores: geojsonSource({cluster: true, clusterRadius: 50})};
|
|
213
|
+
export const dataOverlays = {
|
|
214
|
+
stores: clusterOverlay({id: 'stores', placement: 'above-labels', source: 'stores'}),
|
|
215
|
+
};
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Pass plain data: `$state` proxies cannot reach MapLibre's worker, so use `$state.raw`. See the
|
|
219
|
+
[interactions reference](https://github.com/tileflow/tileflow-sdk/blob/main/packages/interactions/REFERENCE.md)
|
|
220
|
+
for heatmaps, events, and polygon drawing with Terra Draw.
|
|
221
|
+
|
|
196
222
|
## Display an existing image
|
|
197
223
|
|
|
198
224
|
`mode="image"` displays an explicit `imageUrl` or derives an image URL from the selected manifest
|
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.36",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Svelte component for rendering Tileflow maps with MapLibre.",
|
|
6
6
|
"type": "module",
|
|
@@ -44,15 +44,15 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@tileflow/core": ">=0.1.0-alpha.
|
|
48
|
-
"@tileflow/interactions": ">=0.1.0-alpha.
|
|
47
|
+
"@tileflow/core": ">=0.1.0-alpha.48 <0.1.0-beta.0",
|
|
48
|
+
"@tileflow/interactions": ">=0.1.0-alpha.8 <0.1.0-beta.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"maplibre-gl": ">=6.4.1 <7",
|
|
52
52
|
"svelte": ">=5 <6"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"maplibre-gl": "6.
|
|
55
|
+
"maplibre-gl": "6.16.0",
|
|
56
56
|
"svelte": "^5.57.2",
|
|
57
57
|
"svelte-check": "^4.7.7"
|
|
58
58
|
},
|
package/src/TileflowMap.svelte
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
type TileflowMap3DAttachment,
|
|
43
43
|
createTileflowTransformRequest,
|
|
44
44
|
getTileflowSystemColorScheme,
|
|
45
|
+
loadTileflowRuntimeStyle,
|
|
45
46
|
loadTileflowStyleFonts,
|
|
46
47
|
registerTileflowContourProtocol,
|
|
47
48
|
registerTileflowPathGroundProtocol,
|
|
@@ -70,9 +71,11 @@
|
|
|
70
71
|
type TileflowInteractionViewContext,
|
|
71
72
|
} from '@tileflow/interactions';
|
|
72
73
|
import {
|
|
74
|
+
createTileflowMapLibreDataRuntime,
|
|
73
75
|
createTileflowMapLibreDomRuntime,
|
|
74
76
|
createTileflowMapLibreInteractionCoordinator,
|
|
75
77
|
createTileflowMapLibreSemanticDomRuntime,
|
|
78
|
+
type TileflowMapLibreDataRuntime,
|
|
76
79
|
type TileflowMapLibreDomRenderTarget,
|
|
77
80
|
type TileflowMapLibreDomRuntime,
|
|
78
81
|
type TileflowMapLibreInteractionCoordinator,
|
|
@@ -86,7 +89,10 @@
|
|
|
86
89
|
|
|
87
90
|
export let alt: NonNullable<TileflowMapProps['alt']> = '';
|
|
88
91
|
export let analytics: TileflowMapProps['analytics'] = undefined;
|
|
92
|
+
export let annotationClusters: TileflowMapProps['annotationClusters'] = undefined;
|
|
89
93
|
export let annotations: TileflowMapProps['annotations'] = undefined;
|
|
94
|
+
export let dataOverlays: TileflowMapProps['dataOverlays'] = undefined;
|
|
95
|
+
export let dataSources: TileflowMapProps['dataSources'] = undefined;
|
|
90
96
|
|
|
91
97
|
export let center: TileflowMapProps['center'] = undefined;
|
|
92
98
|
export let captureId: TileflowMapProps['captureId'] = undefined;
|
|
@@ -125,6 +131,7 @@
|
|
|
125
131
|
let mapFairUseNotice: TileflowFairUseNoticeController | null = null;
|
|
126
132
|
let mapLifecycleAttachment: TileflowMapLifecycleAttachment | null = null;
|
|
127
133
|
let annotationRuntime: TileflowMapLibreDomRuntime<TileflowAnnotation> | null = null;
|
|
134
|
+
let dataRuntime: TileflowMapLibreDataRuntime<TileflowAnnotation> | null = null;
|
|
128
135
|
let annotationRuntimeDiagnosticsUnsubscribe: (() => void) | null = null;
|
|
129
136
|
let annotationRuntimeEventsUnsubscribe: (() => void) | null = null;
|
|
130
137
|
let annotationRuntimeTargetsUnsubscribe: (() => void) | null = null;
|
|
@@ -166,6 +173,7 @@
|
|
|
166
173
|
let manifestResolutionKey = '';
|
|
167
174
|
let manifestResolutionState: 'error' | 'loading' | 'ready' = 'loading';
|
|
168
175
|
let mapLoadId = 0;
|
|
176
|
+
let resourceLifetime: AbortController | undefined;
|
|
169
177
|
let mounted = false;
|
|
170
178
|
let refreshRunId = 0;
|
|
171
179
|
let readinessRunId = 0;
|
|
@@ -264,7 +272,10 @@
|
|
|
264
272
|
defaultInteractionState,
|
|
265
273
|
);
|
|
266
274
|
$: hasInteractionConfiguration =
|
|
275
|
+
annotationClusters !== undefined ||
|
|
267
276
|
annotations !== undefined ||
|
|
277
|
+
dataOverlays !== undefined ||
|
|
278
|
+
dataSources !== undefined ||
|
|
268
279
|
interactions !== undefined ||
|
|
269
280
|
interactionState !== undefined ||
|
|
270
281
|
defaultInteractionState !== undefined ||
|
|
@@ -350,6 +361,14 @@
|
|
|
350
361
|
syncAnnotations();
|
|
351
362
|
}
|
|
352
363
|
|
|
364
|
+
$: if (mounted) {
|
|
365
|
+
annotationClusters;
|
|
366
|
+
annotationResolution;
|
|
367
|
+
dataOverlays;
|
|
368
|
+
dataSources;
|
|
369
|
+
syncDataLayers();
|
|
370
|
+
}
|
|
371
|
+
|
|
353
372
|
$: if (mounted) {
|
|
354
373
|
interactions;
|
|
355
374
|
syncSemanticInteractions();
|
|
@@ -451,9 +470,13 @@
|
|
|
451
470
|
|
|
452
471
|
function destroyMap() {
|
|
453
472
|
mapLoadId += 1;
|
|
473
|
+
resourceLifetime?.abort();
|
|
474
|
+
resourceLifetime = undefined;
|
|
454
475
|
readinessRunId += 1;
|
|
455
476
|
const lifecycleAttachment = mapLifecycleAttachment;
|
|
456
477
|
const fairUseNotice = mapFairUseNotice;
|
|
478
|
+
dataRuntime?.dispose();
|
|
479
|
+
dataRuntime = null;
|
|
457
480
|
const annotationInteractionsForMap = annotationRuntime;
|
|
458
481
|
const semanticInteractionsForMap = semanticRuntime;
|
|
459
482
|
const detachAnnotationCoordinator = annotationRuntimeCoordinatorDetach;
|
|
@@ -552,15 +575,36 @@
|
|
|
552
575
|
const loadId = mapLoadId;
|
|
553
576
|
const targetContainer = container;
|
|
554
577
|
const runtime = runtimeStyle;
|
|
578
|
+
const session = createTileflowSessionController({source: 'svelte'});
|
|
579
|
+
const lifetime = new AbortController();
|
|
580
|
+
resourceLifetime = lifetime;
|
|
581
|
+
const resourceTransform = createTileflowTransformRequest({
|
|
582
|
+
getAnalytics: () => resolvedAnalytics,
|
|
583
|
+
sessionController: session,
|
|
584
|
+
sessionId: session.sessionId,
|
|
585
|
+
transformRequest: mapOptions?.transformRequest ?? undefined,
|
|
586
|
+
});
|
|
587
|
+
const styleLoadOptions = {
|
|
588
|
+
signal: lifetime.signal,
|
|
589
|
+
transformRequest: (url: string, type: 'Style' | 'Source') =>
|
|
590
|
+
resourceTransform(
|
|
591
|
+
url,
|
|
592
|
+
type as Parameters<NonNullable<TileflowMapOptions['transformRequest']>>[1],
|
|
593
|
+
),
|
|
594
|
+
};
|
|
555
595
|
let maplibregl: Awaited<ReturnType<typeof loadTileflowMapLibre>>;
|
|
556
|
-
|
|
596
|
+
let prepared: Awaited<ReturnType<typeof loadTileflowRuntimeStyle>>;
|
|
557
597
|
try {
|
|
558
|
-
[maplibregl] = await Promise.all([
|
|
598
|
+
[maplibregl, prepared] = await Promise.all([
|
|
559
599
|
loadTileflowMapLibre(),
|
|
560
|
-
|
|
600
|
+
loadTileflowRuntimeStyle(runtime, styleLoadOptions).then(async (loaded) => {
|
|
601
|
+
await loadTileflowStyleFonts(loaded.style, {fontFaces: loaded.fontFaces});
|
|
602
|
+
return loaded;
|
|
603
|
+
}),
|
|
561
604
|
]);
|
|
562
605
|
} catch (error) {
|
|
563
606
|
if (mounted && runId === refreshRunId && loadId === mapLoadId) {
|
|
607
|
+
lifetime.abort();
|
|
564
608
|
console.error('Failed to load the Tileflow map runtime', error);
|
|
565
609
|
mapCaptureState = 'error';
|
|
566
610
|
}
|
|
@@ -577,7 +621,6 @@
|
|
|
577
621
|
return;
|
|
578
622
|
}
|
|
579
623
|
|
|
580
|
-
const session = createTileflowSessionController({source: 'svelte'});
|
|
581
624
|
const sessionStarter = createTileflowSessionStarter({
|
|
582
625
|
getSessionId: () => session.sessionId,
|
|
583
626
|
sessionId: session.sessionId,
|
|
@@ -603,7 +646,7 @@
|
|
|
603
646
|
container: targetContainer,
|
|
604
647
|
interactive: resolvedInteractive,
|
|
605
648
|
pitch: resolvedPitch,
|
|
606
|
-
style:
|
|
649
|
+
style: prepared.style as StyleSpecification,
|
|
607
650
|
transformRequest: createTileflowTransformRequest({
|
|
608
651
|
getAnalytics: () => resolvedAnalytics,
|
|
609
652
|
sessionController: session,
|
|
@@ -625,7 +668,9 @@
|
|
|
625
668
|
runtime.theme === undefined
|
|
626
669
|
? null
|
|
627
670
|
: createTileflowThemeController({
|
|
628
|
-
initial: runtime,
|
|
671
|
+
initial: prepared.family ? prepared : runtime,
|
|
672
|
+
initialStyle: prepared.style,
|
|
673
|
+
styleLoadOptions,
|
|
629
674
|
map: maplibreMap,
|
|
630
675
|
onTransition(transition) {
|
|
631
676
|
onThemeChange?.(transition);
|
|
@@ -712,6 +757,22 @@
|
|
|
712
757
|
annotationRuntimeDiagnostics = annotationInteractionsForMap.getDiagnostics();
|
|
713
758
|
syncAnnotationRenderTargets(annotationInteractionsForMap.getRenderTargets());
|
|
714
759
|
syncAnnotations();
|
|
760
|
+
// Created ahead of semantic POIs: a click on a cluster belongs to the cluster.
|
|
761
|
+
dataRuntime = createTileflowMapLibreDataRuntime<
|
|
762
|
+
MapLibreMap,
|
|
763
|
+
MapLibreMarker,
|
|
764
|
+
TileflowAnnotation
|
|
765
|
+
>({
|
|
766
|
+
createMarker: ({element}) => new maplibregl.Marker({element}),
|
|
767
|
+
document: targetContainer.ownerDocument,
|
|
768
|
+
map: maplibreMap,
|
|
769
|
+
onDiagnostic: (diagnostic) => onInteractionDiagnostic?.(diagnostic),
|
|
770
|
+
onInteractionEvent(event) {
|
|
771
|
+
if (!interactionRuntimesDisposing) onInteractionEvent?.(event);
|
|
772
|
+
},
|
|
773
|
+
setHiddenAnnotations: annotationInteractionsForMap.setHiddenAnnotations,
|
|
774
|
+
});
|
|
775
|
+
syncDataLayers();
|
|
715
776
|
} catch (error) {
|
|
716
777
|
console.error('Failed to initialize Tileflow annotations', error);
|
|
717
778
|
setBridgeInteractionDiagnostic(
|
|
@@ -898,6 +959,15 @@
|
|
|
898
959
|
}
|
|
899
960
|
}
|
|
900
961
|
|
|
962
|
+
function syncDataLayers() {
|
|
963
|
+
dataRuntime?.reconcile({
|
|
964
|
+
annotationClusters,
|
|
965
|
+
annotations: annotationResolution.ok && !isImageMode ? annotationResolution.annotations : [],
|
|
966
|
+
dataOverlays,
|
|
967
|
+
dataSources,
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
|
|
901
971
|
function syncSemanticInteractions() {
|
|
902
972
|
const bindings =
|
|
903
973
|
interactionBindingResolution.ok && !isImageMode ? interactionBindingResolution.bindings : [];
|
package/src/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type {TileflowGeoJsonSource, TileflowMapLibreOverlay} from '@tileflow/core';
|
|
1
2
|
import type {TileflowTimeOfDay} from '@tileflow/core/runtime';
|
|
3
|
+
import type {TileflowAnnotationClusterOptions} from '@tileflow/interactions/maplibre';
|
|
2
4
|
import type {Map as MapLibreMap, MapOptions as MapLibreMapOptions} from 'maplibre-gl';
|
|
3
5
|
import type {Snippet, SvelteComponentTyped} from 'svelte';
|
|
4
6
|
import type {
|
|
@@ -68,9 +70,18 @@ type TileflowMapBaseProps = {
|
|
|
68
70
|
export type TileflowMapAnnotationProps<
|
|
69
71
|
TAnnotation extends TileflowAnnotation = TileflowAnnotation,
|
|
70
72
|
> = {
|
|
73
|
+
/** Group `annotations` into accessible cluster buttons. Up to 1,000 annotations. */
|
|
74
|
+
annotationClusters?: TileflowAnnotationClusterOptions;
|
|
71
75
|
annotations?: readonly TAnnotation[];
|
|
72
76
|
};
|
|
73
77
|
|
|
78
|
+
export type TileflowMapDataProps = {
|
|
79
|
+
/** Overlays drawn over `dataSources` or compiled sources, keyed as in `defineMap`. */
|
|
80
|
+
dataOverlays?: Readonly<Record<string, TileflowMapLibreOverlay>>;
|
|
81
|
+
/** Application GeoJSON, keyed as in `defineMap`. A compiled source of the same key is filled. */
|
|
82
|
+
dataSources?: Readonly<Record<string, TileflowGeoJsonSource>>;
|
|
83
|
+
};
|
|
84
|
+
|
|
74
85
|
export type TileflowMapInteractionStateProps =
|
|
75
86
|
| {
|
|
76
87
|
defaultInteractionState?: TileflowInteractionState;
|
|
@@ -87,6 +98,7 @@ type TileflowMapStyleSourceProps = {
|
|
|
87
98
|
|
|
88
99
|
type TileflowMapInteractiveProps<TAnnotation extends TileflowAnnotation> =
|
|
89
100
|
TileflowMapAnnotationProps<TAnnotation> &
|
|
101
|
+
TileflowMapDataProps &
|
|
90
102
|
TileflowMapInteractionStateProps & {
|
|
91
103
|
interactions?: readonly TileflowInteractionBinding[];
|
|
92
104
|
marker?: TileflowMapAnnotationSnippet<TAnnotation>;
|
|
@@ -99,7 +111,10 @@ type TileflowMapInteractiveProps<TAnnotation extends TileflowAnnotation> =
|
|
|
99
111
|
};
|
|
100
112
|
|
|
101
113
|
type TileflowMapImageProps = {
|
|
114
|
+
annotationClusters?: never;
|
|
102
115
|
annotations?: never;
|
|
116
|
+
dataOverlays?: never;
|
|
117
|
+
dataSources?: never;
|
|
103
118
|
defaultInteractionState?: never;
|
|
104
119
|
interactions?: never;
|
|
105
120
|
interactionState?: never;
|