@tileflow/vue 0.1.0-alpha.26 → 0.1.0-alpha.28

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,20 @@
1
1
  import { MapOptions, Map } from 'maplibre-gl';
2
2
  import { DefineComponent, VNodeChild } from 'vue';
3
- import { TileflowThemeTransition } from '@tileflow/core/browser';
3
+ import { TileflowBrowserThemeBlendSelection, TileflowThemeTransitionOptions, TileflowThemeTransition } from '@tileflow/core/browser';
4
4
  import { TileflowRuntimeSource, TileflowThemeSelection, 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. `hold` keeps labels, icons, and mixed
13
+ * artwork back during a camera move; clearing it brings them in at once.
14
+ */
15
+ themeBlend?: TileflowBrowserThemeBlendSelection;
16
+ /** Cross-fades theme changes, including entering and leaving a blend. */
17
+ themeTransition?: TileflowThemeTransitionOptions;
10
18
  };
11
19
 
12
20
  type TileflowMapLibreConfiguration = Readonly<{
package/dist/index.js CHANGED
@@ -34,12 +34,14 @@ import {
34
34
  defaultTileflowRuntimeView,
35
35
  loadTileflowManifest,
36
36
  mergeTileflowAnalytics,
37
+ nearestTileflowBlendTheme,
37
38
  normalizeTileflowRuntimeCenter,
38
39
  normalizeTileflowStaticImageSize,
39
40
  resolveTileflowManifestMap,
40
41
  resolveTileflowMapMode,
41
42
  resolveTileflowRuntimeStyle,
42
43
  resolveTileflowRuntimeTheme,
44
+ resolveTileflowRuntimeThemeBlend,
43
45
  resolveTileflowRuntimeView,
44
46
  resolveTileflowStaticImageUrl
45
47
  } from "@tileflow/core/runtime";
@@ -157,6 +159,9 @@ function configureTileflowMapLibreModule(maplibregl) {
157
159
  }
158
160
 
159
161
  // src/style-source.ts
162
+ import {
163
+ validateTileflowBrowserThemeBlendSelection
164
+ } from "@tileflow/core/browser";
160
165
  import {
161
166
  validateTileflowRuntimeSource,
162
167
  validateTileflowThemeSelection
@@ -167,8 +172,25 @@ function validateTileflowMapStyleInputs(input) {
167
172
  if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
168
173
  return { error: 'theme must be a concrete portable theme name or "system"', ok: false };
169
174
  }
175
+ if (input.themeBlend !== void 0 && !validateTileflowBrowserThemeBlendSelection(input.themeBlend)) {
176
+ return {
177
+ error: "themeBlend must name two to eight concrete themes, a position from 0 to themes.length - 1, and an optional boolean hold",
178
+ ok: false
179
+ };
180
+ }
181
+ if (input.themeTransition !== void 0 && !isThemeTransition(input.themeTransition)) {
182
+ return {
183
+ error: "themeTransition.duration must be a number of milliseconds from 0 to 5000",
184
+ ok: false
185
+ };
186
+ }
170
187
  return { ok: true };
171
188
  }
189
+ function isThemeTransition(value) {
190
+ if (!value || typeof value !== "object") return false;
191
+ const { duration } = value;
192
+ return duration === void 0 || typeof duration === "number" && Number.isFinite(duration) && duration >= 0 && duration <= 5e3;
193
+ }
172
194
  function assertTileflowMapStyleInputs(input) {
173
195
  const validation = validateTileflowMapStyleInputs(input);
174
196
  if (!validation.ok) {
@@ -217,6 +239,8 @@ var TileflowMap = defineComponent({
217
239
  type: Object
218
240
  },
219
241
  theme: String,
242
+ themeBlend: Object,
243
+ themeTransition: Object,
220
244
  zoom: Number
221
245
  },
222
246
  emits: {
@@ -336,17 +360,24 @@ var TileflowMap = defineComponent({
336
360
  const manifestMap = computed(
337
361
  () => manifestResolutionKey.value === manifestRequestKey.value && manifestResolutionState.value === "ready" ? loadedManifestMap.value : null
338
362
  );
363
+ const themeBlendKey = computed(
364
+ () => props.themeBlend ? JSON.stringify(props.themeBlend.themes) : void 0
365
+ );
339
366
  const themeResolution = computed(() => {
367
+ const selection = props.themeBlend ? nearestTileflowBlendTheme(props.themeBlend) : props.theme;
340
368
  if (!manifestMap.value) {
341
369
  return {
342
370
  error: false,
343
- name: props.theme && props.theme !== "system" ? props.theme : void 0
371
+ name: selection && selection !== "system" ? selection : void 0
344
372
  };
345
373
  }
346
374
  try {
375
+ for (const name of props.themeBlend?.themes ?? []) {
376
+ resolveTileflowRuntimeTheme(manifestMap.value, name);
377
+ }
347
378
  return {
348
379
  error: false,
349
- name: resolveTileflowRuntimeTheme(manifestMap.value, props.theme, systemColorScheme.value).name
380
+ name: resolveTileflowRuntimeTheme(manifestMap.value, selection, systemColorScheme.value).name
350
381
  };
351
382
  } catch {
352
383
  return { error: true, name: void 0 };
@@ -387,6 +418,19 @@ var TileflowMap = defineComponent({
387
418
  theme: themeResolution.value.name
388
419
  })
389
420
  );
421
+ const blendStyles = computed(() => {
422
+ const key = themeBlendKey.value;
423
+ if (isImageMode.value || !manifestMap.value || !key) return null;
424
+ try {
425
+ return resolveTileflowRuntimeThemeBlend({
426
+ blend: { position: 0, themes: JSON.parse(key) },
427
+ manifestMap: manifestMap.value,
428
+ source: props.source
429
+ });
430
+ } catch {
431
+ return null;
432
+ }
433
+ });
390
434
  const resolvedAnalytics = computed(
391
435
  () => mergeTileflowAnalytics(props.analytics, runtimeStyle.value?.analytics)
392
436
  );
@@ -512,7 +556,12 @@ var TileflowMap = defineComponent({
512
556
  ]);
513
557
  };
514
558
  watchEffect(() => {
515
- assertTileflowMapStyleInputs({ source: props.source, theme: props.theme });
559
+ assertTileflowMapStyleInputs({
560
+ source: props.source,
561
+ theme: props.theme,
562
+ themeBlend: props.themeBlend,
563
+ themeTransition: props.themeTransition
564
+ });
516
565
  });
517
566
  watchEffect(() => {
518
567
  const diagnostics = interactionInputDiagnostics.value;
@@ -879,13 +928,31 @@ var TileflowMap = defineComponent({
879
928
  return () => subscription.unsubscribe();
880
929
  }
881
930
  });
882
- if (themeController && runtimeStyle.value && runtimeStyle.value !== runtime) {
931
+ if (themeController && blendStyles.value && props.themeBlend) {
932
+ switchBlend();
933
+ } else if (themeController && runtimeStyle.value && runtimeStyle.value !== runtime) {
883
934
  activeRuntimeResource.value = runtimeStyle.value;
884
- void themeController.setTheme(runtimeStyle.value);
935
+ void themeController.setTheme(runtimeStyle.value, { transition: props.themeTransition });
885
936
  }
886
937
  };
938
+ const switchBlend = () => {
939
+ const themes = blendStyles.value;
940
+ const position = props.themeBlend?.position;
941
+ const hold = props.themeBlend?.hold === true;
942
+ if (!themeController || !themes || position === void 0) return;
943
+ if (runtimeStyle.value) activeRuntimeResource.value = runtimeStyle.value;
944
+ void themeController.setBlend({ hold, position, themes }, { transition: props.themeTransition }).then((result) => {
945
+ if (result.status === "failed") {
946
+ console.error("Failed to blend the Tileflow map themes", result.error);
947
+ }
948
+ });
949
+ };
887
950
  const switchTheme = () => {
888
951
  if (!themeController) return;
952
+ if (props.themeBlend) {
953
+ if (runtimeStyle.value) activeRuntimeResource.value = runtimeStyle.value;
954
+ return;
955
+ }
889
956
  if (themeResolution.value.error) {
890
957
  void themeController.setTheme(themeController.getCurrent());
891
958
  return;
@@ -893,7 +960,7 @@ var TileflowMap = defineComponent({
893
960
  const next = runtimeStyle.value;
894
961
  if (!next) return;
895
962
  activeRuntimeResource.value = next;
896
- void themeController.setTheme(next).then((result) => {
963
+ void themeController.setTheme(next, { transition: props.themeTransition }).then((result) => {
897
964
  if (result.status === "failed") {
898
965
  console.error("Failed to change the Tileflow map theme", result.error);
899
966
  }
@@ -976,10 +1043,15 @@ var TileflowMap = defineComponent({
976
1043
  () => syncSystemColorSchemeSubscription()
977
1044
  );
978
1045
  watch(
979
- () => runtimeStyle.value,
1046
+ () => [runtimeStyle.value, themeBlendKey.value],
980
1047
  () => switchTheme(),
981
1048
  { flush: "post" }
982
1049
  );
1050
+ watch(
1051
+ () => [blendStyles.value, props.themeBlend?.position, props.themeBlend?.hold === true],
1052
+ () => switchBlend(),
1053
+ { flush: "post" }
1054
+ );
983
1055
  watch(
984
1056
  () => [hasInteractionConfiguration.value, isImageMode.value, runtimeImageUrl.value],
985
1057
  () => {
@@ -993,14 +1065,7 @@ var TileflowMap = defineComponent({
993
1065
  { flush: "post" }
994
1066
  );
995
1067
  watch(
996
- () => [
997
- props.analytics,
998
- props.interactive,
999
- props.mapOptions,
1000
- props.mode,
1001
- props.source,
1002
- manifestMap.value
1003
- ],
1068
+ () => [props.interactive, props.mapOptions, props.mode, props.source, manifestMap.value],
1004
1069
  () => void recreateMap(),
1005
1070
  { flush: "post" }
1006
1071
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/vue",
3
- "version": "0.1.0-alpha.26",
3
+ "version": "0.1.0-alpha.28",
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.29 <0.1.0-beta.0",
41
+ "@tileflow/core": ">=0.1.0-alpha.32 <0.1.0-beta.0",
42
42
  "@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0"
43
43
  },
44
44
  "peerDependencies": {
@@ -46,10 +46,10 @@
46
46
  "vue": ">=3.3 <4"
47
47
  },
48
48
  "devDependencies": {
49
- "@vue/server-renderer": "3.5.39",
50
- "maplibre-gl": "6.9.0",
49
+ "@vue/server-renderer": "3.5.43",
50
+ "maplibre-gl": "6.13.0",
51
51
  "tsx": "^4.23.12",
52
- "vue": "^3.5.26"
52
+ "vue": "^3.5.43"
53
53
  },
54
54
  "scripts": {
55
55
  "build": "tsup src/index.ts --format esm --dts --clean --external vue --external maplibre-gl",