@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 +6 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +80 -15
- package/package.json +5 -5
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:
|
|
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,
|
|
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({
|
|
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 &&
|
|
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.
|
|
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.
|
|
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.
|
|
50
|
-
"maplibre-gl": "6.
|
|
49
|
+
"@vue/server-renderer": "3.5.43",
|
|
50
|
+
"maplibre-gl": "6.13.0",
|
|
51
51
|
"tsx": "^4.23.12",
|
|
52
|
-
"vue": "^3.5.
|
|
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",
|