@tileflow/vue 0.1.0-alpha.26 → 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
@@ -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";
@@ -159,6 +161,7 @@ function configureTileflowMapLibreModule(maplibregl) {
159
161
  // src/style-source.ts
160
162
  import {
161
163
  validateTileflowRuntimeSource,
164
+ validateTileflowThemeBlendSelection,
162
165
  validateTileflowThemeSelection
163
166
  } from "@tileflow/core/runtime";
164
167
  function validateTileflowMapStyleInputs(input) {
@@ -167,8 +170,25 @@ function validateTileflowMapStyleInputs(input) {
167
170
  if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
168
171
  return { error: 'theme must be a concrete portable theme name or "system"', ok: false };
169
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
+ }
170
185
  return { ok: true };
171
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
+ }
172
192
  function assertTileflowMapStyleInputs(input) {
173
193
  const validation = validateTileflowMapStyleInputs(input);
174
194
  if (!validation.ok) {
@@ -217,6 +237,8 @@ var TileflowMap = defineComponent({
217
237
  type: Object
218
238
  },
219
239
  theme: String,
240
+ themeBlend: Object,
241
+ themeTransition: Object,
220
242
  zoom: Number
221
243
  },
222
244
  emits: {
@@ -336,17 +358,24 @@ var TileflowMap = defineComponent({
336
358
  const manifestMap = computed(
337
359
  () => manifestResolutionKey.value === manifestRequestKey.value && manifestResolutionState.value === "ready" ? loadedManifestMap.value : null
338
360
  );
361
+ const themeBlendKey = computed(
362
+ () => props.themeBlend ? JSON.stringify(props.themeBlend.themes) : void 0
363
+ );
339
364
  const themeResolution = computed(() => {
365
+ const selection = props.themeBlend ? nearestTileflowBlendTheme(props.themeBlend) : props.theme;
340
366
  if (!manifestMap.value) {
341
367
  return {
342
368
  error: false,
343
- name: props.theme && props.theme !== "system" ? props.theme : void 0
369
+ name: selection && selection !== "system" ? selection : void 0
344
370
  };
345
371
  }
346
372
  try {
373
+ for (const name of props.themeBlend?.themes ?? []) {
374
+ resolveTileflowRuntimeTheme(manifestMap.value, name);
375
+ }
347
376
  return {
348
377
  error: false,
349
- name: resolveTileflowRuntimeTheme(manifestMap.value, props.theme, systemColorScheme.value).name
378
+ name: resolveTileflowRuntimeTheme(manifestMap.value, selection, systemColorScheme.value).name
350
379
  };
351
380
  } catch {
352
381
  return { error: true, name: void 0 };
@@ -387,6 +416,19 @@ var TileflowMap = defineComponent({
387
416
  theme: themeResolution.value.name
388
417
  })
389
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
+ });
390
432
  const resolvedAnalytics = computed(
391
433
  () => mergeTileflowAnalytics(props.analytics, runtimeStyle.value?.analytics)
392
434
  );
@@ -512,7 +554,12 @@ var TileflowMap = defineComponent({
512
554
  ]);
513
555
  };
514
556
  watchEffect(() => {
515
- 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
+ });
516
563
  });
517
564
  watchEffect(() => {
518
565
  const diagnostics = interactionInputDiagnostics.value;
@@ -879,13 +926,30 @@ var TileflowMap = defineComponent({
879
926
  return () => subscription.unsubscribe();
880
927
  }
881
928
  });
882
- 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) {
883
932
  activeRuntimeResource.value = runtimeStyle.value;
884
- void themeController.setTheme(runtimeStyle.value);
933
+ void themeController.setTheme(runtimeStyle.value, { transition: props.themeTransition });
885
934
  }
886
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
+ };
887
947
  const switchTheme = () => {
888
948
  if (!themeController) return;
949
+ if (props.themeBlend) {
950
+ if (runtimeStyle.value) activeRuntimeResource.value = runtimeStyle.value;
951
+ return;
952
+ }
889
953
  if (themeResolution.value.error) {
890
954
  void themeController.setTheme(themeController.getCurrent());
891
955
  return;
@@ -893,7 +957,7 @@ var TileflowMap = defineComponent({
893
957
  const next = runtimeStyle.value;
894
958
  if (!next) return;
895
959
  activeRuntimeResource.value = next;
896
- void themeController.setTheme(next).then((result) => {
960
+ void themeController.setTheme(next, { transition: props.themeTransition }).then((result) => {
897
961
  if (result.status === "failed") {
898
962
  console.error("Failed to change the Tileflow map theme", result.error);
899
963
  }
@@ -976,10 +1040,15 @@ var TileflowMap = defineComponent({
976
1040
  () => syncSystemColorSchemeSubscription()
977
1041
  );
978
1042
  watch(
979
- () => runtimeStyle.value,
1043
+ () => [runtimeStyle.value, themeBlendKey.value],
980
1044
  () => switchTheme(),
981
1045
  { flush: "post" }
982
1046
  );
1047
+ watch(
1048
+ () => [blendStyles.value, props.themeBlend?.position],
1049
+ () => switchBlend(),
1050
+ { flush: "post" }
1051
+ );
983
1052
  watch(
984
1053
  () => [hasInteractionConfiguration.value, isImageMode.value, runtimeImageUrl.value],
985
1054
  () => {
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.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.29 <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": {