@tileflow/vue 0.1.0-alpha.25 → 0.1.0-alpha.27

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
@@ -88,6 +88,12 @@ Omitting `theme` uses `defaultTheme`. `system` needs the map's explicit light/da
88
88
  Streets supplies. Switching themes preserves the map, camera, and interactions, with rollback on
89
89
  failure. Listen to `themeChange` for transitions.
90
90
 
91
+ `:theme-transition="{duration: 450}"` cross-fades theme changes over that many milliseconds.
92
+ `:theme-blend="{themes: ['night', 'dusk', 'day'], position}"` shows a continuous blend of the
93
+ map's themes instead of `theme`: position 0 is the first theme and 1.5 is halfway from the second to
94
+ the third. Moving the position updates the map at once. See
95
+ [theme transitions and blends](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/theme-transitions-and-blends.md).
96
+
91
97
  `mapOptions` accepts native MapLibre options except `container` and `style`. Direct props take
92
98
  priority over those options, then the published view, then shared defaults. `TileflowMap` always
93
99
  selects a Tileflow map through its manifest; it has no renderer discriminator or direct-style mode.
package/dist/index.d.ts CHANGED
@@ -1,12 +1,19 @@
1
1
  import { MapOptions, Map } from 'maplibre-gl';
2
2
  import { DefineComponent, VNodeChild } from 'vue';
3
- import { TileflowThemeTransition } from '@tileflow/core/browser';
4
- import { TileflowRuntimeSource, TileflowThemeSelection, TileflowAnalytics } from '@tileflow/core/runtime';
3
+ import { TileflowThemeTransitionOptions, TileflowThemeTransition } from '@tileflow/core/browser';
4
+ import { TileflowRuntimeSource, TileflowThemeSelection, TileflowThemeBlendSelection, TileflowAnalytics } from '@tileflow/core/runtime';
5
5
  import { TileflowAnnotation, TileflowInteractionJsonValue, TileflowInteractionState, TileflowInteractionBinding, TileflowInteractionDiagnostic, TileflowInteractionEvent, TileflowAnnotationViewContext, TileflowInteractionViewContext } from '@tileflow/interactions';
6
6
 
7
7
  type TileflowMapStyleSourceProps = {
8
8
  source: TileflowRuntimeSource;
9
9
  theme?: TileflowThemeSelection;
10
+ /**
11
+ * Shows a continuous blend of the map's themes instead of `theme`. Moving `position` changes the
12
+ * map at once; changing `themes` prepares a new blend.
13
+ */
14
+ themeBlend?: TileflowThemeBlendSelection;
15
+ /** Cross-fades theme changes, including entering and leaving a blend. */
16
+ themeTransition?: TileflowThemeTransitionOptions;
10
17
  };
11
18
 
12
19
  type TileflowMapLibreConfiguration = Readonly<{
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  watchEffect
15
15
  } from "vue";
16
16
  import {
17
+ attachTileflowAtmosphere,
17
18
  attachTileflowFairUseNotice,
18
19
  attachTileflowMapLifecycle,
19
20
  createTileflowSessionStarter,
@@ -33,12 +34,14 @@ import {
33
34
  defaultTileflowRuntimeView,
34
35
  loadTileflowManifest,
35
36
  mergeTileflowAnalytics,
37
+ nearestTileflowBlendTheme,
36
38
  normalizeTileflowRuntimeCenter,
37
39
  normalizeTileflowStaticImageSize,
38
40
  resolveTileflowManifestMap,
39
41
  resolveTileflowMapMode,
40
42
  resolveTileflowRuntimeStyle,
41
43
  resolveTileflowRuntimeTheme,
44
+ resolveTileflowRuntimeThemeBlend,
42
45
  resolveTileflowRuntimeView,
43
46
  resolveTileflowStaticImageUrl
44
47
  } from "@tileflow/core/runtime";
@@ -158,6 +161,7 @@ function configureTileflowMapLibreModule(maplibregl) {
158
161
  // src/style-source.ts
159
162
  import {
160
163
  validateTileflowRuntimeSource,
164
+ validateTileflowThemeBlendSelection,
161
165
  validateTileflowThemeSelection
162
166
  } from "@tileflow/core/runtime";
163
167
  function validateTileflowMapStyleInputs(input) {
@@ -166,8 +170,25 @@ function validateTileflowMapStyleInputs(input) {
166
170
  if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
167
171
  return { error: 'theme must be a concrete portable theme name or "system"', ok: false };
168
172
  }
173
+ if (input.themeBlend !== void 0 && !validateTileflowThemeBlendSelection(input.themeBlend)) {
174
+ return {
175
+ error: "themeBlend must name two to eight concrete themes and a position from 0 to themes.length - 1",
176
+ ok: false
177
+ };
178
+ }
179
+ if (input.themeTransition !== void 0 && !isThemeTransition(input.themeTransition)) {
180
+ return {
181
+ error: "themeTransition.duration must be a number of milliseconds from 0 to 5000",
182
+ ok: false
183
+ };
184
+ }
169
185
  return { ok: true };
170
186
  }
187
+ function isThemeTransition(value) {
188
+ if (!value || typeof value !== "object") return false;
189
+ const { duration } = value;
190
+ return duration === void 0 || typeof duration === "number" && Number.isFinite(duration) && duration >= 0 && duration <= 5e3;
191
+ }
171
192
  function assertTileflowMapStyleInputs(input) {
172
193
  const validation = validateTileflowMapStyleInputs(input);
173
194
  if (!validation.ok) {
@@ -216,6 +237,8 @@ var TileflowMap = defineComponent({
216
237
  type: Object
217
238
  },
218
239
  theme: String,
240
+ themeBlend: Object,
241
+ themeTransition: Object,
219
242
  zoom: Number
220
243
  },
221
244
  emits: {
@@ -335,17 +358,24 @@ var TileflowMap = defineComponent({
335
358
  const manifestMap = computed(
336
359
  () => manifestResolutionKey.value === manifestRequestKey.value && manifestResolutionState.value === "ready" ? loadedManifestMap.value : null
337
360
  );
361
+ const themeBlendKey = computed(
362
+ () => props.themeBlend ? JSON.stringify(props.themeBlend.themes) : void 0
363
+ );
338
364
  const themeResolution = computed(() => {
365
+ const selection = props.themeBlend ? nearestTileflowBlendTheme(props.themeBlend) : props.theme;
339
366
  if (!manifestMap.value) {
340
367
  return {
341
368
  error: false,
342
- name: props.theme && props.theme !== "system" ? props.theme : void 0
369
+ name: selection && selection !== "system" ? selection : void 0
343
370
  };
344
371
  }
345
372
  try {
373
+ for (const name of props.themeBlend?.themes ?? []) {
374
+ resolveTileflowRuntimeTheme(manifestMap.value, name);
375
+ }
346
376
  return {
347
377
  error: false,
348
- name: resolveTileflowRuntimeTheme(manifestMap.value, props.theme, systemColorScheme.value).name
378
+ name: resolveTileflowRuntimeTheme(manifestMap.value, selection, systemColorScheme.value).name
349
379
  };
350
380
  } catch {
351
381
  return { error: true, name: void 0 };
@@ -386,6 +416,19 @@ var TileflowMap = defineComponent({
386
416
  theme: themeResolution.value.name
387
417
  })
388
418
  );
419
+ const blendStyles = computed(() => {
420
+ const key = themeBlendKey.value;
421
+ if (isImageMode.value || !manifestMap.value || !key) return null;
422
+ try {
423
+ return resolveTileflowRuntimeThemeBlend({
424
+ blend: { position: 0, themes: JSON.parse(key) },
425
+ manifestMap: manifestMap.value,
426
+ source: props.source
427
+ });
428
+ } catch {
429
+ return null;
430
+ }
431
+ });
389
432
  const resolvedAnalytics = computed(
390
433
  () => mergeTileflowAnalytics(props.analytics, runtimeStyle.value?.analytics)
391
434
  );
@@ -511,7 +554,12 @@ var TileflowMap = defineComponent({
511
554
  ]);
512
555
  };
513
556
  watchEffect(() => {
514
- assertTileflowMapStyleInputs({ source: props.source, theme: props.theme });
557
+ assertTileflowMapStyleInputs({
558
+ source: props.source,
559
+ theme: props.theme,
560
+ themeBlend: props.themeBlend,
561
+ themeTransition: props.themeTransition
562
+ });
515
563
  });
516
564
  watchEffect(() => {
517
565
  const diagnostics = interactionInputDiagnostics.value;
@@ -737,6 +785,7 @@ var TileflowMap = defineComponent({
737
785
  zoom: resolvedZoom.value
738
786
  });
739
787
  mapRef.value = map;
788
+ attachTileflowAtmosphere(map);
740
789
  themeController = runtime.theme === void 0 ? null : createTileflowThemeController({
741
790
  initial: runtime,
742
791
  map,
@@ -877,13 +926,30 @@ var TileflowMap = defineComponent({
877
926
  return () => subscription.unsubscribe();
878
927
  }
879
928
  });
880
- if (themeController && runtimeStyle.value && runtimeStyle.value !== runtime) {
929
+ if (themeController && blendStyles.value && props.themeBlend) {
930
+ switchBlend();
931
+ } else if (themeController && runtimeStyle.value && runtimeStyle.value !== runtime) {
881
932
  activeRuntimeResource.value = runtimeStyle.value;
882
- void themeController.setTheme(runtimeStyle.value);
933
+ void themeController.setTheme(runtimeStyle.value, { transition: props.themeTransition });
883
934
  }
884
935
  };
936
+ const switchBlend = () => {
937
+ const themes = blendStyles.value;
938
+ const position = props.themeBlend?.position;
939
+ if (!themeController || !themes || position === void 0) return;
940
+ if (runtimeStyle.value) activeRuntimeResource.value = runtimeStyle.value;
941
+ void themeController.setBlend({ position, themes }, { transition: props.themeTransition }).then((result) => {
942
+ if (result.status === "failed") {
943
+ console.error("Failed to blend the Tileflow map themes", result.error);
944
+ }
945
+ });
946
+ };
885
947
  const switchTheme = () => {
886
948
  if (!themeController) return;
949
+ if (props.themeBlend) {
950
+ if (runtimeStyle.value) activeRuntimeResource.value = runtimeStyle.value;
951
+ return;
952
+ }
887
953
  if (themeResolution.value.error) {
888
954
  void themeController.setTheme(themeController.getCurrent());
889
955
  return;
@@ -891,7 +957,7 @@ var TileflowMap = defineComponent({
891
957
  const next = runtimeStyle.value;
892
958
  if (!next) return;
893
959
  activeRuntimeResource.value = next;
894
- void themeController.setTheme(next).then((result) => {
960
+ void themeController.setTheme(next, { transition: props.themeTransition }).then((result) => {
895
961
  if (result.status === "failed") {
896
962
  console.error("Failed to change the Tileflow map theme", result.error);
897
963
  }
@@ -974,10 +1040,15 @@ var TileflowMap = defineComponent({
974
1040
  () => syncSystemColorSchemeSubscription()
975
1041
  );
976
1042
  watch(
977
- () => runtimeStyle.value,
1043
+ () => [runtimeStyle.value, themeBlendKey.value],
978
1044
  () => switchTheme(),
979
1045
  { flush: "post" }
980
1046
  );
1047
+ watch(
1048
+ () => [blendStyles.value, props.themeBlend?.position],
1049
+ () => switchBlend(),
1050
+ { flush: "post" }
1051
+ );
981
1052
  watch(
982
1053
  () => [hasInteractionConfiguration.value, isImageMode.value, runtimeImageUrl.value],
983
1054
  () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/vue",
3
- "version": "0.1.0-alpha.25",
3
+ "version": "0.1.0-alpha.27",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Vue component for rendering Tileflow maps with MapLibre.",
6
6
  "type": "module",
@@ -38,7 +38,7 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@tileflow/core": ">=0.1.0-alpha.27 <0.1.0-beta.0",
41
+ "@tileflow/core": ">=0.1.0-alpha.30 <0.1.0-beta.0",
42
42
  "@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0"
43
43
  },
44
44
  "peerDependencies": {