@tileflow/svelte 0.1.0-alpha.35 → 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 CHANGED
@@ -100,7 +100,7 @@ Add `timeOfDay="dusk"` beside `theme` to select a map's optional `day`, `dawn`,
100
100
  `night` preset. For Streets, `theme="light"` with this preset selects the existing `dusk` palette.
101
101
  Maps without this capability ignore the option; omitting it keeps the selected theme unchanged.
102
102
  Changing it uses the existing theme transition. `themeBlend` takes precedence. See the
103
- [authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/README.md#style-with-themes-and-semantic-modules).
103
+ [authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/REFERENCE.md#style-with-themes-and-semantic-modules).
104
104
 
105
105
  Theme and `timeOfDay` changes cross-fade over 450 ms by default, respecting reduced motion.
106
106
  `themeTransition={{duration: 700}}` customizes the duration; use `duration: 0` for an immediate change.
@@ -199,6 +199,26 @@ snippets reconciles existing runtimes rather than recreating MapLibre.
199
199
  Text/field descriptors render as text. A `view` names an application snippet; it is not serialized
200
200
  HTML. Keep tooltips non-interactive and put buttons, links, and forms in popups.
201
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
+
202
222
  ## Display an existing image
203
223
 
204
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.35",
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,8 +44,8 @@
44
44
  "access": "public"
45
45
  },
46
46
  "dependencies": {
47
- "@tileflow/core": ">=0.1.0-alpha.45 <0.1.0-beta.0",
48
- "@tileflow/interactions": ">=0.1.0-alpha.7 <0.1.0-beta.0"
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",
@@ -71,9 +71,11 @@
71
71
  type TileflowInteractionViewContext,
72
72
  } from '@tileflow/interactions';
73
73
  import {
74
+ createTileflowMapLibreDataRuntime,
74
75
  createTileflowMapLibreDomRuntime,
75
76
  createTileflowMapLibreInteractionCoordinator,
76
77
  createTileflowMapLibreSemanticDomRuntime,
78
+ type TileflowMapLibreDataRuntime,
77
79
  type TileflowMapLibreDomRenderTarget,
78
80
  type TileflowMapLibreDomRuntime,
79
81
  type TileflowMapLibreInteractionCoordinator,
@@ -87,7 +89,10 @@
87
89
 
88
90
  export let alt: NonNullable<TileflowMapProps['alt']> = '';
89
91
  export let analytics: TileflowMapProps['analytics'] = undefined;
92
+ export let annotationClusters: TileflowMapProps['annotationClusters'] = undefined;
90
93
  export let annotations: TileflowMapProps['annotations'] = undefined;
94
+ export let dataOverlays: TileflowMapProps['dataOverlays'] = undefined;
95
+ export let dataSources: TileflowMapProps['dataSources'] = undefined;
91
96
 
92
97
  export let center: TileflowMapProps['center'] = undefined;
93
98
  export let captureId: TileflowMapProps['captureId'] = undefined;
@@ -126,6 +131,7 @@
126
131
  let mapFairUseNotice: TileflowFairUseNoticeController | null = null;
127
132
  let mapLifecycleAttachment: TileflowMapLifecycleAttachment | null = null;
128
133
  let annotationRuntime: TileflowMapLibreDomRuntime<TileflowAnnotation> | null = null;
134
+ let dataRuntime: TileflowMapLibreDataRuntime<TileflowAnnotation> | null = null;
129
135
  let annotationRuntimeDiagnosticsUnsubscribe: (() => void) | null = null;
130
136
  let annotationRuntimeEventsUnsubscribe: (() => void) | null = null;
131
137
  let annotationRuntimeTargetsUnsubscribe: (() => void) | null = null;
@@ -266,7 +272,10 @@
266
272
  defaultInteractionState,
267
273
  );
268
274
  $: hasInteractionConfiguration =
275
+ annotationClusters !== undefined ||
269
276
  annotations !== undefined ||
277
+ dataOverlays !== undefined ||
278
+ dataSources !== undefined ||
270
279
  interactions !== undefined ||
271
280
  interactionState !== undefined ||
272
281
  defaultInteractionState !== undefined ||
@@ -352,6 +361,14 @@
352
361
  syncAnnotations();
353
362
  }
354
363
 
364
+ $: if (mounted) {
365
+ annotationClusters;
366
+ annotationResolution;
367
+ dataOverlays;
368
+ dataSources;
369
+ syncDataLayers();
370
+ }
371
+
355
372
  $: if (mounted) {
356
373
  interactions;
357
374
  syncSemanticInteractions();
@@ -458,6 +475,8 @@
458
475
  readinessRunId += 1;
459
476
  const lifecycleAttachment = mapLifecycleAttachment;
460
477
  const fairUseNotice = mapFairUseNotice;
478
+ dataRuntime?.dispose();
479
+ dataRuntime = null;
461
480
  const annotationInteractionsForMap = annotationRuntime;
462
481
  const semanticInteractionsForMap = semanticRuntime;
463
482
  const detachAnnotationCoordinator = annotationRuntimeCoordinatorDetach;
@@ -738,6 +757,22 @@
738
757
  annotationRuntimeDiagnostics = annotationInteractionsForMap.getDiagnostics();
739
758
  syncAnnotationRenderTargets(annotationInteractionsForMap.getRenderTargets());
740
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();
741
776
  } catch (error) {
742
777
  console.error('Failed to initialize Tileflow annotations', error);
743
778
  setBridgeInteractionDiagnostic(
@@ -924,6 +959,15 @@
924
959
  }
925
960
  }
926
961
 
962
+ function syncDataLayers() {
963
+ dataRuntime?.reconcile({
964
+ annotationClusters,
965
+ annotations: annotationResolution.ok && !isImageMode ? annotationResolution.annotations : [],
966
+ dataOverlays,
967
+ dataSources,
968
+ });
969
+ }
970
+
927
971
  function syncSemanticInteractions() {
928
972
  const bindings =
929
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;