@tileflow/react 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 -2
- package/dist/index.js +81 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -99,6 +99,12 @@ for assets.
|
|
|
99
99
|
explicit light/dark mapping, which Streets supplies. Theme changes preserve the MapLibre instance,
|
|
100
100
|
camera, and interactions, and roll back on failure. `onThemeChange` reports transitions.
|
|
101
101
|
|
|
102
|
+
`themeTransition={{duration: 450}}` cross-fades theme changes over that many milliseconds.
|
|
103
|
+
`themeBlend={{themes: ['night', 'dusk', 'day'], position}}` shows a continuous blend of the map's
|
|
104
|
+
themes instead of `theme`: position 0 is the first theme and 1.5 is halfway from the second to the
|
|
105
|
+
third. Moving the position updates the map at once. See
|
|
106
|
+
[theme transitions and blends](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/theme-transitions-and-blends.md).
|
|
107
|
+
|
|
102
108
|
`mapOptions` accepts native MapLibre options except `container` and `style`. Direct camera props
|
|
103
109
|
take priority over `mapOptions`, then the manifest view, then shared defaults. `Map` always selects
|
|
104
110
|
a Tileflow map through its manifest; it has no renderer discriminator or direct-style mode.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { CSSProperties, ImgHTMLAttributes, ReactNode } from 'react';
|
|
3
3
|
import { MapOptions, Map as Map$1 } from 'maplibre-gl';
|
|
4
|
-
import { TileflowThemeTransition } from '@tileflow/core/browser';
|
|
5
|
-
import { TileflowRuntimeSource, TileflowThemeSelection, TileflowAnalytics } from '@tileflow/core/runtime';
|
|
4
|
+
import { TileflowThemeTransitionOptions, TileflowThemeTransition } from '@tileflow/core/browser';
|
|
5
|
+
import { TileflowRuntimeSource, TileflowThemeSelection, TileflowThemeBlendSelection, TileflowAnalytics } from '@tileflow/core/runtime';
|
|
6
6
|
import { TileflowAnnotation, TileflowInteractionState, TileflowAnnotationViewContext, TileflowInteractionBinding, TileflowInteractionViewContext, TileflowInteractionDiagnostic, TileflowInteractionEvent } from '@tileflow/interactions';
|
|
7
7
|
|
|
8
8
|
type TileflowMapStyleSourceProps = {
|
|
9
9
|
source: TileflowRuntimeSource;
|
|
10
10
|
theme?: TileflowThemeSelection;
|
|
11
|
+
/**
|
|
12
|
+
* Shows a continuous blend of the map's themes instead of `theme`. Moving `position` changes the
|
|
13
|
+
* map at once; changing `themes` prepares a new blend.
|
|
14
|
+
*/
|
|
15
|
+
themeBlend?: TileflowThemeBlendSelection;
|
|
16
|
+
/** Cross-fades theme changes, including entering and leaving a blend. */
|
|
17
|
+
themeTransition?: TileflowThemeTransitionOptions;
|
|
11
18
|
};
|
|
12
19
|
|
|
13
20
|
type TileflowMapSource = TileflowRuntimeSource;
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "react";
|
|
12
12
|
import { createPortal } from "react-dom";
|
|
13
13
|
import {
|
|
14
|
+
attachTileflowAtmosphere,
|
|
14
15
|
attachTileflowFairUseNotice,
|
|
15
16
|
attachTileflowMapLifecycle,
|
|
16
17
|
createTileflowSessionStarter,
|
|
@@ -30,12 +31,14 @@ import {
|
|
|
30
31
|
defaultTileflowRuntimeView,
|
|
31
32
|
loadTileflowManifest,
|
|
32
33
|
mergeTileflowAnalytics,
|
|
34
|
+
nearestTileflowBlendTheme,
|
|
33
35
|
normalizeTileflowRuntimeCenter,
|
|
34
36
|
normalizeTileflowStaticImageSize,
|
|
35
37
|
resolveTileflowManifestMap,
|
|
36
38
|
resolveTileflowMapMode,
|
|
37
39
|
resolveTileflowRuntimeStyle,
|
|
38
40
|
resolveTileflowRuntimeTheme,
|
|
41
|
+
resolveTileflowRuntimeThemeBlend,
|
|
39
42
|
resolveTileflowRuntimeView,
|
|
40
43
|
resolveTileflowStaticImageUrl
|
|
41
44
|
} from "@tileflow/core/runtime";
|
|
@@ -166,6 +169,7 @@ function prepareTileflowReactInteractionState(interactionState, defaultInteracti
|
|
|
166
169
|
// src/map-style-inputs.ts
|
|
167
170
|
import {
|
|
168
171
|
validateTileflowRuntimeSource,
|
|
172
|
+
validateTileflowThemeBlendSelection,
|
|
169
173
|
validateTileflowThemeSelection
|
|
170
174
|
} from "@tileflow/core/runtime";
|
|
171
175
|
function validateTileflowMapStyleInputs(input) {
|
|
@@ -174,8 +178,25 @@ function validateTileflowMapStyleInputs(input) {
|
|
|
174
178
|
if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
|
|
175
179
|
return { error: 'theme must be a concrete portable theme name or "system"', ok: false };
|
|
176
180
|
}
|
|
181
|
+
if (input.themeBlend !== void 0 && !validateTileflowThemeBlendSelection(input.themeBlend)) {
|
|
182
|
+
return {
|
|
183
|
+
error: "themeBlend must name two to eight concrete themes and a position from 0 to themes.length - 1",
|
|
184
|
+
ok: false
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (input.themeTransition !== void 0 && !isThemeTransition(input.themeTransition)) {
|
|
188
|
+
return {
|
|
189
|
+
error: "themeTransition.duration must be a number of milliseconds from 0 to 5000",
|
|
190
|
+
ok: false
|
|
191
|
+
};
|
|
192
|
+
}
|
|
177
193
|
return { ok: true };
|
|
178
194
|
}
|
|
195
|
+
function isThemeTransition(value) {
|
|
196
|
+
if (!value || typeof value !== "object") return false;
|
|
197
|
+
const { duration } = value;
|
|
198
|
+
return duration === void 0 || typeof duration === "number" && Number.isFinite(duration) && duration >= 0 && duration <= 5e3;
|
|
199
|
+
}
|
|
179
200
|
function assertTileflowMapStyleInputs(input) {
|
|
180
201
|
const validation = validateTileflowMapStyleInputs(input);
|
|
181
202
|
if (!validation.ok) {
|
|
@@ -237,6 +258,8 @@ function Map2(props) {
|
|
|
237
258
|
mapOptions,
|
|
238
259
|
analytics,
|
|
239
260
|
theme,
|
|
261
|
+
themeBlend,
|
|
262
|
+
themeTransition,
|
|
240
263
|
interactionState,
|
|
241
264
|
interactions = defaultInteractions,
|
|
242
265
|
onInteractionDiagnostic,
|
|
@@ -325,19 +348,31 @@ function Map2(props) {
|
|
|
325
348
|
const manifestRequestKey = JSON.stringify([manifestUrl, mapName]);
|
|
326
349
|
const currentManifestResolution = manifestResolution.key === manifestRequestKey ? manifestResolution : { key: manifestRequestKey, map: null, state: "loading" };
|
|
327
350
|
const manifestMap = currentManifestResolution.state === "ready" ? currentManifestResolution.map : null;
|
|
351
|
+
const themeBlendKey = themeBlend ? JSON.stringify(themeBlend.themes) : void 0;
|
|
352
|
+
const themeBlendPosition = themeBlend?.position;
|
|
353
|
+
const blendThemeName = themeBlend ? nearestTileflowBlendTheme(themeBlend) : void 0;
|
|
328
354
|
const themeResolution = useMemo(() => {
|
|
355
|
+
const selection = blendThemeName ?? theme;
|
|
329
356
|
if (!manifestMap) {
|
|
330
|
-
return {
|
|
357
|
+
return {
|
|
358
|
+
error: false,
|
|
359
|
+
name: selection && selection !== "system" ? selection : void 0
|
|
360
|
+
};
|
|
331
361
|
}
|
|
332
362
|
try {
|
|
363
|
+
if (themeBlendKey) {
|
|
364
|
+
for (const name of JSON.parse(themeBlendKey)) {
|
|
365
|
+
resolveTileflowRuntimeTheme(manifestMap, name);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
333
368
|
return {
|
|
334
369
|
error: false,
|
|
335
|
-
name: resolveTileflowRuntimeTheme(manifestMap,
|
|
370
|
+
name: resolveTileflowRuntimeTheme(manifestMap, selection, systemColorScheme).name
|
|
336
371
|
};
|
|
337
372
|
} catch {
|
|
338
373
|
return { error: true, name: void 0 };
|
|
339
374
|
}
|
|
340
|
-
}, [manifestMap, systemColorScheme, theme]);
|
|
375
|
+
}, [blendThemeName, manifestMap, systemColorScheme, theme, themeBlendKey]);
|
|
341
376
|
const resolvedThemeName = themeResolution.name;
|
|
342
377
|
const manifestView = resolveTileflowRuntimeView({ manifestMap });
|
|
343
378
|
const manifestCenter = useStableMapOptionValue(
|
|
@@ -476,6 +511,22 @@ function Map2(props) {
|
|
|
476
511
|
});
|
|
477
512
|
}, [isImageMode, manifestMap, runtimeSource, systemColorScheme, themeResolution]);
|
|
478
513
|
runtimeStyleRef.current = runtimeStyle;
|
|
514
|
+
const blendStyles = useMemo(() => {
|
|
515
|
+
if (isImageMode || !manifestMap || !themeBlendKey) return null;
|
|
516
|
+
try {
|
|
517
|
+
return resolveTileflowRuntimeThemeBlend({
|
|
518
|
+
blend: { position: 0, themes: JSON.parse(themeBlendKey) },
|
|
519
|
+
manifestMap,
|
|
520
|
+
source: runtimeSource
|
|
521
|
+
});
|
|
522
|
+
} catch {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
}, [isImageMode, manifestMap, runtimeSource, themeBlendKey]);
|
|
526
|
+
const blendRequestRef = useRef(null);
|
|
527
|
+
blendRequestRef.current = blendStyles && themeBlendPosition !== void 0 ? { position: themeBlendPosition, themes: blendStyles } : null;
|
|
528
|
+
const themeTransitionRef = useRef(themeTransition);
|
|
529
|
+
themeTransitionRef.current = themeTransition;
|
|
479
530
|
const runtimeMapIdentity = `${manifestUrl}\0${runtimeSource.map}`;
|
|
480
531
|
const runtimeStyleReady = runtimeStyle !== null || themeResolution.error && mapRef.current !== null;
|
|
481
532
|
const resolvedAnalytics = useMemo(
|
|
@@ -719,6 +770,7 @@ function Map2(props) {
|
|
|
719
770
|
mapRef.current = map;
|
|
720
771
|
setupResources.map = map;
|
|
721
772
|
registerCleanup(() => map.remove());
|
|
773
|
+
registerCleanup(attachTileflowAtmosphere(map));
|
|
722
774
|
const themeController = initialRuntimeStyle.theme === void 0 ? void 0 : createTileflowThemeController({
|
|
723
775
|
initial: initialRuntimeStyle,
|
|
724
776
|
map,
|
|
@@ -917,9 +969,15 @@ function Map2(props) {
|
|
|
917
969
|
});
|
|
918
970
|
registerCleanup(() => lifecycle.dispose());
|
|
919
971
|
const latestRuntimeStyle = runtimeStyleRef.current;
|
|
920
|
-
|
|
972
|
+
const latestBlend = blendRequestRef.current;
|
|
973
|
+
if (themeController && latestBlend) {
|
|
974
|
+
if (latestRuntimeStyle) setActiveRuntimeResource(latestRuntimeStyle);
|
|
975
|
+
void themeController.setBlend(latestBlend, { transition: themeTransitionRef.current });
|
|
976
|
+
} else if (themeController && latestRuntimeStyle && latestRuntimeStyle !== initialRuntimeStyle) {
|
|
921
977
|
setActiveRuntimeResource(latestRuntimeStyle);
|
|
922
|
-
void themeController.setTheme(latestRuntimeStyle
|
|
978
|
+
void themeController.setTheme(latestRuntimeStyle, {
|
|
979
|
+
transition: themeTransitionRef.current
|
|
980
|
+
});
|
|
923
981
|
}
|
|
924
982
|
}).catch((error) => {
|
|
925
983
|
try {
|
|
@@ -940,19 +998,33 @@ function Map2(props) {
|
|
|
940
998
|
}, [resolvedInteractive, runtimeMapIdentity, runtimeStyleReady, stableMapOptions]);
|
|
941
999
|
useEffect(() => {
|
|
942
1000
|
const controller = themeControllerRef.current;
|
|
943
|
-
if (!controller) return;
|
|
1001
|
+
if (!controller || themeBlendKey) return;
|
|
944
1002
|
if (themeResolution.error) {
|
|
945
1003
|
void controller.setTheme(controller.getCurrent());
|
|
946
1004
|
return;
|
|
947
1005
|
}
|
|
948
|
-
if (!runtimeStyle || controller.getCurrent() === runtimeStyle)
|
|
1006
|
+
if (!runtimeStyle || controller.getCurrent() === runtimeStyle && !controller.getBlend())
|
|
1007
|
+
return;
|
|
949
1008
|
setActiveRuntimeResource(runtimeStyle);
|
|
950
|
-
void controller.setTheme(runtimeStyle).then((result) => {
|
|
1009
|
+
void controller.setTheme(runtimeStyle, { transition: themeTransitionRef.current }).then((result) => {
|
|
951
1010
|
if (result.status === "failed") {
|
|
952
1011
|
console.error("Failed to change the Tileflow map theme", result.error);
|
|
953
1012
|
}
|
|
954
1013
|
});
|
|
955
|
-
}, [runtimeStyle, themeResolution.error]);
|
|
1014
|
+
}, [runtimeStyle, themeBlendKey, themeResolution.error]);
|
|
1015
|
+
useEffect(() => {
|
|
1016
|
+
const controller = themeControllerRef.current;
|
|
1017
|
+
if (!controller || !blendStyles || themeBlendPosition === void 0) return;
|
|
1018
|
+
if (runtimeStyle) setActiveRuntimeResource(runtimeStyle);
|
|
1019
|
+
void controller.setBlend(
|
|
1020
|
+
{ position: themeBlendPosition, themes: blendStyles },
|
|
1021
|
+
{ transition: themeTransitionRef.current }
|
|
1022
|
+
).then((result) => {
|
|
1023
|
+
if (result.status === "failed") {
|
|
1024
|
+
console.error("Failed to blend the Tileflow map themes", result.error);
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
}, [blendStyles, runtimeStyle, themeBlendPosition]);
|
|
956
1028
|
useLayoutEffect(() => {
|
|
957
1029
|
if (!isImageMode) return;
|
|
958
1030
|
readinessRunRef.current += 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/react",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.28",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "React components for rendering Tileflow maps with MapLibre.",
|
|
6
6
|
"type": "module",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@tileflow/core": ">=0.1.0-alpha.
|
|
46
|
+
"@tileflow/core": ">=0.1.0-alpha.30 <0.1.0-beta.0",
|
|
47
47
|
"@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0",
|
|
48
48
|
"@tileflow/static": ">=0.1.0-alpha.27 <0.1.0-beta.0"
|
|
49
49
|
},
|