@tileflow/vue 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 CHANGED
@@ -16,6 +16,12 @@ npm install @tileflow/vue@alpha @tileflow/core@alpha @tileflow/maps@alpha "mapli
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:
@@ -92,7 +98,7 @@ Add `time-of-day="dusk"` beside `theme` to select a map's optional `day`, `dawn`
92
98
  `night` preset. For Streets, `theme="light"` with this preset selects the existing `dusk` palette.
93
99
  Maps without this capability ignore the option; omitting it keeps the selected theme unchanged.
94
100
  Changing it uses the existing theme transition. `themeBlend` takes precedence. See the
95
- [authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/README.md#style-with-themes-and-semantic-modules).
101
+ [authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/REFERENCE.md#style-with-themes-and-semantic-modules).
96
102
 
97
103
  Theme and `time-of-day` changes cross-fade over 450 ms by default, respecting reduced motion.
98
104
  `:theme-transition="{duration: 700}"` customizes the duration; use `duration: 0` for an immediate change.
@@ -185,6 +191,26 @@ reconcile without recreating MapLibre. Text/field descriptors render as text; `v
185
191
  an application slot and never evaluates HTML. Tooltips must remain non-interactive; use a popup for
186
192
  links, buttons, or forms.
187
193
 
194
+ ## Cluster points and draw density
195
+
196
+ Use `annotationClusters` for up to 1,000 annotations, or `dataSources` and `dataOverlays` for larger
197
+ GeoJSON sets. Clicking a cluster zooms in and reports a `cluster` target through `@interaction-event`.
198
+
199
+ <!-- docs:check -->
200
+
201
+ ```ts
202
+ import {clusterOverlay, geojsonSource} from '@tileflow/core/runtime';
203
+
204
+ export const dataSources = {stores: geojsonSource({cluster: true, clusterRadius: 50})};
205
+ export const dataOverlays = {
206
+ stores: clusterOverlay({id: 'stores', placement: 'above-labels', source: 'stores'}),
207
+ };
208
+ ```
209
+
210
+ See the
211
+ [interactions reference](https://github.com/tileflow/tileflow-sdk/blob/main/packages/interactions/REFERENCE.md)
212
+ for heatmaps, events, and polygon drawing with Terra Draw.
213
+
188
214
  ## Display an existing image
189
215
 
190
216
  `mode="image"` renders an explicit `imageUrl` or derives an image URL from the selected manifest
package/dist/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import { MapOptions, Map } from 'maplibre-gl';
2
2
  import { DefineComponent, VNodeChild } from 'vue';
3
+ import { TileflowMapLibreOverlay, TileflowGeoJsonSource } from '@tileflow/core';
3
4
  import { TileflowBrowserThemeBlendSelection, TileflowThemeTransitionOptions, TileflowViewMode, TileflowThemeTransition } from '@tileflow/core/browser';
4
5
  import { TileflowRuntimeSource, TileflowThemeSelection, TileflowTimeOfDay, TileflowAnalytics } from '@tileflow/core/runtime';
5
6
  import { TileflowAnnotation, TileflowInteractionJsonValue, TileflowInteractionState, TileflowInteractionBinding, TileflowInteractionDiagnostic, TileflowInteractionEvent, TileflowAnnotationViewContext, TileflowInteractionViewContext } from '@tileflow/interactions';
7
+ import { TileflowAnnotationClusterOptions } from '@tileflow/interactions/maplibre';
6
8
 
7
9
  type TileflowMapStyleSourceProps = {
8
10
  source: TileflowRuntimeSource;
@@ -57,13 +59,22 @@ type TileflowMapStateInput = {
57
59
  interactionState?: never;
58
60
  };
59
61
  type TileflowMapInteractiveProps<TAnnotation extends TileflowAnnotation> = TileflowMapSharedProps & TileflowMapStateInput & {
62
+ /** Group `annotations` into accessible cluster buttons. Up to 1,000 annotations. */
63
+ annotationClusters?: TileflowAnnotationClusterOptions;
60
64
  annotations?: readonly TAnnotation[];
65
+ /** Overlays drawn over `dataSources` or compiled sources, keyed as in `defineMap`. */
66
+ dataOverlays?: Readonly<Record<string, TileflowMapLibreOverlay>>;
67
+ /** Application GeoJSON, keyed as in `defineMap`. A compiled source of the same key is filled. */
68
+ dataSources?: Readonly<Record<string, TileflowGeoJsonSource>>;
61
69
  interactions?: readonly TileflowInteractionBinding[];
62
70
  interactive?: boolean;
63
71
  mode?: 'interactive';
64
72
  };
65
73
  type TileflowMapImageProps = TileflowMapSharedProps & {
74
+ annotationClusters?: never;
66
75
  annotations?: never;
76
+ dataOverlays?: never;
77
+ dataSources?: never;
67
78
  defaultInteractionState?: never;
68
79
  interactionState?: never;
69
80
  interactions?: never;
@@ -73,7 +84,10 @@ type TileflowMapImageProps = TileflowMapSharedProps & {
73
84
  type TileflowMapProps<TAnnotation extends TileflowAnnotation = TileflowAnnotation> = (TileflowMapInteractiveProps<TAnnotation> | TileflowMapImageProps) & TileflowMapStyleSourceProps;
74
85
  type RuntimeTileflowAnnotation = TileflowAnnotation<TileflowInteractionJsonValue>;
75
86
  type RuntimeTileflowMapProps = TileflowMapSharedProps & TileflowMapStyleSourceProps & {
87
+ annotationClusters?: TileflowAnnotationClusterOptions;
76
88
  annotations?: readonly RuntimeTileflowAnnotation[];
89
+ dataOverlays?: Readonly<Record<string, TileflowMapLibreOverlay>>;
90
+ dataSources?: Readonly<Record<string, TileflowGeoJsonSource>>;
77
91
  defaultInteractionState?: TileflowInteractionState;
78
92
  interactionState?: TileflowInteractionState;
79
93
  interactions?: readonly TileflowInteractionBinding[];
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  ref,
11
11
  shallowRef,
12
12
  Teleport,
13
+ toRaw,
13
14
  watch,
14
15
  watchEffect
15
16
  } from "vue";
@@ -26,6 +27,7 @@ import {
26
27
  createTileflowThemeController,
27
28
  createTileflowTransformRequest,
28
29
  getTileflowSystemColorScheme,
30
+ loadTileflowRuntimeStyle,
29
31
  loadTileflowStyleFonts,
30
32
  registerTileflowContourProtocol,
31
33
  registerTileflowPathGroundProtocol,
@@ -57,6 +59,7 @@ import {
57
59
  initialTileflowInteractionState
58
60
  } from "@tileflow/interactions";
59
61
  import {
62
+ createTileflowMapLibreDataRuntime,
60
63
  createTileflowMapLibreDomRuntime,
61
64
  createTileflowMapLibreInteractionCoordinator,
62
65
  createTileflowMapLibreSemanticDomRuntime
@@ -221,10 +224,13 @@ var TileflowMap = defineComponent({
221
224
  type: String
222
225
  },
223
226
  analytics: Object,
227
+ annotationClusters: Object,
224
228
  annotations: Array,
225
229
  center: Array,
226
230
  captureId: String,
227
231
  className: String,
232
+ dataOverlays: Object,
233
+ dataSources: Object,
228
234
  defaultInteractionState: Object,
229
235
  height: {
230
236
  default: 420,
@@ -283,6 +289,7 @@ var TileflowMap = defineComponent({
283
289
  );
284
290
  const controlledInteractionOwnership = props.interactionState !== void 0;
285
291
  let annotationRuntime = null;
292
+ let dataRuntime = null;
286
293
  let semanticRuntime = null;
287
294
  let interactionRuntimesDisposing = false;
288
295
  let initializeSemanticRuntime = null;
@@ -307,6 +314,7 @@ var TileflowMap = defineComponent({
307
314
  let themeController = null;
308
315
  let unsubscribeSystemColorScheme = null;
309
316
  let mapLoadId = 0;
317
+ let resourceLifetime;
310
318
  let manifestLoadId = 0;
311
319
  let readinessRunId = 0;
312
320
  let interactionReadinessRunId = 0;
@@ -341,7 +349,7 @@ var TileflowMap = defineComponent({
341
349
  return diagnostics;
342
350
  });
343
351
  const hasInteractionConfiguration = computed(
344
- () => props.annotations !== void 0 || props.interactions !== void 0 || props.interactionState !== void 0 || props.defaultInteractionState !== void 0 || slots.marker !== void 0 || slots.popup !== void 0 || slots.tooltip !== void 0
352
+ () => props.annotationClusters !== void 0 || props.annotations !== void 0 || props.dataOverlays !== void 0 || props.dataSources !== void 0 || props.interactions !== void 0 || props.interactionState !== void 0 || props.defaultInteractionState !== void 0 || slots.marker !== void 0 || slots.popup !== void 0 || slots.tooltip !== void 0
345
353
  );
346
354
  const interactionInputDiagnostics = computed(() => {
347
355
  const diagnostics = [
@@ -643,6 +651,8 @@ var TileflowMap = defineComponent({
643
651
  );
644
652
  const destroyMap = () => {
645
653
  mapLoadId += 1;
654
+ resourceLifetime?.abort();
655
+ resourceLifetime = void 0;
646
656
  readinessRunId += 1;
647
657
  invalidateInteractionReadiness();
648
658
  const lifecycle = mapLifecycle;
@@ -652,6 +662,8 @@ var TileflowMap = defineComponent({
652
662
  const controller = themeController;
653
663
  const scene = sceneAttachment;
654
664
  sceneAttachment = null;
665
+ dataRuntime?.dispose();
666
+ dataRuntime = null;
655
667
  const annotationInteractionsForMap = annotationRuntime;
656
668
  const semanticInteractionsForMap = semanticRuntime;
657
669
  const worldRequestBridge = mapWorldRequestBridge;
@@ -724,6 +736,24 @@ var TileflowMap = defineComponent({
724
736
  reportOverlayFailure("Unable to reconcile the Tileflow annotation runtime.");
725
737
  }
726
738
  };
739
+ const syncDataRuntime = () => {
740
+ const resolution = annotationResolution.value;
741
+ if (!dataRuntime) return;
742
+ try {
743
+ dataRuntime.reconcile({
744
+ annotationClusters: props.annotationClusters,
745
+ annotations: resolution.ok && !isImageMode.value ? resolution.annotations : [],
746
+ dataOverlays: props.dataOverlays,
747
+ dataSources: props.dataSources && Object.fromEntries(
748
+ Object.entries(toRaw(props.dataSources)).map(
749
+ ([id, source]) => [id, { ...toRaw(source), data: toRaw(source.data) }]
750
+ )
751
+ )
752
+ });
753
+ } catch {
754
+ reportOverlayFailure("Unable to reconcile the Tileflow data layers.");
755
+ }
756
+ };
727
757
  const syncSemanticRuntime = () => {
728
758
  const resolution = interactionBindingResolution.value;
729
759
  const bindings = resolution.ok && !isImageMode.value ? resolution.bindings : [];
@@ -767,16 +797,35 @@ var TileflowMap = defineComponent({
767
797
  const loadId = mapLoadId;
768
798
  const container = containerRef.value;
769
799
  const runtime = runtimeStyle.value;
800
+ const session = createTileflowSessionController({ source: "vue" });
801
+ const lifetime = new AbortController();
802
+ resourceLifetime = lifetime;
803
+ const resourceTransform = createTileflowTransformRequest({
804
+ getAnalytics: () => resolvedAnalytics.value,
805
+ sessionController: session,
806
+ sessionId: session.sessionId,
807
+ transformRequest: props.mapOptions?.transformRequest ?? void 0
808
+ });
809
+ const styleLoadOptions = {
810
+ signal: lifetime.signal,
811
+ transformRequest: (url, type) => resourceTransform(
812
+ url,
813
+ type
814
+ )
815
+ };
770
816
  let maplibregl;
817
+ let prepared;
771
818
  try {
772
- [maplibregl] = await Promise.all([
819
+ [maplibregl, prepared] = await Promise.all([
773
820
  loadTileflowMapLibre(),
774
- loadTileflowStyleFonts(runtime.style, {
775
- fontFaces: runtime.fontFaces
821
+ loadTileflowRuntimeStyle(runtime, styleLoadOptions).then(async (loaded) => {
822
+ await loadTileflowStyleFonts(loaded.style, { fontFaces: loaded.fontFaces });
823
+ return loaded;
776
824
  })
777
825
  ]);
778
826
  } catch (error) {
779
827
  if (loadId === mapLoadId) {
828
+ lifetime.abort();
780
829
  console.error("Failed to load the Tileflow map runtime", error);
781
830
  mapCaptureState.value = "error";
782
831
  }
@@ -785,7 +834,6 @@ var TileflowMap = defineComponent({
785
834
  if (loadId !== mapLoadId || containerRef.value !== container || isImageMode.value) {
786
835
  return;
787
836
  }
788
- const session = createTileflowSessionController({ source: "vue" });
789
837
  const sessionStarter = createTileflowSessionStarter({
790
838
  getSessionId: () => session.sessionId,
791
839
  sessionId: session.sessionId,
@@ -815,7 +863,7 @@ var TileflowMap = defineComponent({
815
863
  container,
816
864
  interactive: resolvedInteractive.value,
817
865
  pitch: resolvedPitch.value,
818
- style: runtime.style,
866
+ style: prepared.style,
819
867
  transformRequest: createTileflowTransformRequest({
820
868
  getAnalytics: () => resolvedAnalytics.value,
821
869
  sessionController: session,
@@ -832,7 +880,9 @@ var TileflowMap = defineComponent({
832
880
  attachTileflowLabelPlacement(map);
833
881
  attachTileflowHorizonQueries(map);
834
882
  themeController = runtime.theme === void 0 ? null : createTileflowThemeController({
835
- initial: runtime,
883
+ initial: prepared.family ? prepared : runtime,
884
+ initialStyle: prepared.style,
885
+ styleLoadOptions,
836
886
  map,
837
887
  onTransition(transition) {
838
888
  emit("themeChange", transition);
@@ -907,6 +957,17 @@ var TileflowMap = defineComponent({
907
957
  annotationRenderTargets.value = mapInteractions.getRenderTargets();
908
958
  syncInteractionRenderTargetReadiness();
909
959
  syncAnnotationRuntime();
960
+ dataRuntime = createTileflowMapLibreDataRuntime({
961
+ createMarker: ({ element }) => new maplibregl.Marker({ element }),
962
+ document: container.ownerDocument,
963
+ map,
964
+ onDiagnostic: (diagnostic) => emit("interactionDiagnostic", diagnostic),
965
+ onInteractionEvent(event) {
966
+ if (!interactionRuntimesDisposing) emit("interactionEvent", event);
967
+ },
968
+ setHiddenAnnotations: mapInteractions.setHiddenAnnotations
969
+ });
970
+ syncDataRuntime();
910
971
  } catch {
911
972
  reportOverlayFailure("Unable to initialize the Tileflow annotation runtime.");
912
973
  }
@@ -1144,9 +1205,17 @@ var TileflowMap = defineComponent({
1144
1205
  );
1145
1206
  watch(
1146
1207
  () => props.annotations,
1147
- () => syncAnnotationRuntime(),
1208
+ () => {
1209
+ syncAnnotationRuntime();
1210
+ syncDataRuntime();
1211
+ },
1148
1212
  { deep: true, flush: "post" }
1149
1213
  );
1214
+ watch(
1215
+ () => [props.annotationClusters, props.dataOverlays, props.dataSources],
1216
+ () => syncDataRuntime(),
1217
+ { flush: "post" }
1218
+ );
1150
1219
  watch(
1151
1220
  () => props.interactions,
1152
1221
  () => syncSemanticRuntime(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/vue",
3
- "version": "0.1.0-alpha.34",
3
+ "version": "0.1.0-alpha.36",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Vue component for rendering Tileflow maps with MapLibre.",
6
6
  "type": "module",
@@ -38,8 +38,8 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@tileflow/core": ">=0.1.0-alpha.43 <0.1.0-beta.0",
42
- "@tileflow/interactions": ">=0.1.0-alpha.7 <0.1.0-beta.0"
41
+ "@tileflow/core": ">=0.1.0-alpha.48 <0.1.0-beta.0",
42
+ "@tileflow/interactions": ">=0.1.0-alpha.8 <0.1.0-beta.0"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "maplibre-gl": ">=6.4.1 <7",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "devDependencies": {
49
49
  "@vue/server-renderer": "3.5.43",
50
- "maplibre-gl": "6.13.0",
50
+ "maplibre-gl": "6.16.0",
51
51
  "tsx": "^4.23.15",
52
52
  "vue": "^3.5.43"
53
53
  },