@tileflow/react 0.1.0-alpha.27 → 0.1.0-alpha.29
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 +7 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +82 -11
- package/package.json +8 -8
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.
|
|
@@ -202,7 +208,7 @@ Tileflow credentials on that server, not in React props or a browser bundle.
|
|
|
202
208
|
```tsx
|
|
203
209
|
import {useState} from 'react';
|
|
204
210
|
import {StaticMap} from '@tileflow/react/static';
|
|
205
|
-
import type {StaticMapAttributionEntry} from '@tileflow/static';
|
|
211
|
+
import type {StaticMapAttributionEntry} from '@tileflow/static/client';
|
|
206
212
|
|
|
207
213
|
export function StaticPreview({idempotencyKey}: {idempotencyKey: string}) {
|
|
208
214
|
const [entries, setEntries] = useState<readonly StaticMapAttributionEntry[]>([]);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
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';
|
|
4
|
+
import { TileflowBrowserThemeBlendSelection, TileflowThemeTransitionOptions, TileflowThemeTransition } from '@tileflow/core/browser';
|
|
5
5
|
import { TileflowRuntimeSource, TileflowThemeSelection, 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. `hold` keeps labels, icons, and mixed
|
|
14
|
+
* artwork back during a camera move; clearing it brings them in at once.
|
|
15
|
+
*/
|
|
16
|
+
themeBlend?: TileflowBrowserThemeBlendSelection;
|
|
17
|
+
/** Cross-fades theme changes, including entering and leaving a blend. */
|
|
18
|
+
themeTransition?: TileflowThemeTransitionOptions;
|
|
11
19
|
};
|
|
12
20
|
|
|
13
21
|
type TileflowMapSource = TileflowRuntimeSource;
|
package/dist/index.js
CHANGED
|
@@ -31,12 +31,14 @@ import {
|
|
|
31
31
|
defaultTileflowRuntimeView,
|
|
32
32
|
loadTileflowManifest,
|
|
33
33
|
mergeTileflowAnalytics,
|
|
34
|
+
nearestTileflowBlendTheme,
|
|
34
35
|
normalizeTileflowRuntimeCenter,
|
|
35
36
|
normalizeTileflowStaticImageSize,
|
|
36
37
|
resolveTileflowManifestMap,
|
|
37
38
|
resolveTileflowMapMode,
|
|
38
39
|
resolveTileflowRuntimeStyle,
|
|
39
40
|
resolveTileflowRuntimeTheme,
|
|
41
|
+
resolveTileflowRuntimeThemeBlend,
|
|
40
42
|
resolveTileflowRuntimeView,
|
|
41
43
|
resolveTileflowStaticImageUrl
|
|
42
44
|
} from "@tileflow/core/runtime";
|
|
@@ -165,6 +167,9 @@ function prepareTileflowReactInteractionState(interactionState, defaultInteracti
|
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
// src/map-style-inputs.ts
|
|
170
|
+
import {
|
|
171
|
+
validateTileflowBrowserThemeBlendSelection
|
|
172
|
+
} from "@tileflow/core/browser";
|
|
168
173
|
import {
|
|
169
174
|
validateTileflowRuntimeSource,
|
|
170
175
|
validateTileflowThemeSelection
|
|
@@ -175,8 +180,25 @@ function validateTileflowMapStyleInputs(input) {
|
|
|
175
180
|
if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
|
|
176
181
|
return { error: 'theme must be a concrete portable theme name or "system"', ok: false };
|
|
177
182
|
}
|
|
183
|
+
if (input.themeBlend !== void 0 && !validateTileflowBrowserThemeBlendSelection(input.themeBlend)) {
|
|
184
|
+
return {
|
|
185
|
+
error: "themeBlend must name two to eight concrete themes, a position from 0 to themes.length - 1, and an optional boolean hold",
|
|
186
|
+
ok: false
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (input.themeTransition !== void 0 && !isThemeTransition(input.themeTransition)) {
|
|
190
|
+
return {
|
|
191
|
+
error: "themeTransition.duration must be a number of milliseconds from 0 to 5000",
|
|
192
|
+
ok: false
|
|
193
|
+
};
|
|
194
|
+
}
|
|
178
195
|
return { ok: true };
|
|
179
196
|
}
|
|
197
|
+
function isThemeTransition(value) {
|
|
198
|
+
if (!value || typeof value !== "object") return false;
|
|
199
|
+
const { duration } = value;
|
|
200
|
+
return duration === void 0 || typeof duration === "number" && Number.isFinite(duration) && duration >= 0 && duration <= 5e3;
|
|
201
|
+
}
|
|
180
202
|
function assertTileflowMapStyleInputs(input) {
|
|
181
203
|
const validation = validateTileflowMapStyleInputs(input);
|
|
182
204
|
if (!validation.ok) {
|
|
@@ -238,6 +260,8 @@ function Map2(props) {
|
|
|
238
260
|
mapOptions,
|
|
239
261
|
analytics,
|
|
240
262
|
theme,
|
|
263
|
+
themeBlend,
|
|
264
|
+
themeTransition,
|
|
241
265
|
interactionState,
|
|
242
266
|
interactions = defaultInteractions,
|
|
243
267
|
onInteractionDiagnostic,
|
|
@@ -326,19 +350,32 @@ function Map2(props) {
|
|
|
326
350
|
const manifestRequestKey = JSON.stringify([manifestUrl, mapName]);
|
|
327
351
|
const currentManifestResolution = manifestResolution.key === manifestRequestKey ? manifestResolution : { key: manifestRequestKey, map: null, state: "loading" };
|
|
328
352
|
const manifestMap = currentManifestResolution.state === "ready" ? currentManifestResolution.map : null;
|
|
353
|
+
const themeBlendKey = themeBlend ? JSON.stringify(themeBlend.themes) : void 0;
|
|
354
|
+
const themeBlendPosition = themeBlend?.position;
|
|
355
|
+
const themeBlendHold = themeBlend?.hold === true;
|
|
356
|
+
const blendThemeName = themeBlend ? nearestTileflowBlendTheme(themeBlend) : void 0;
|
|
329
357
|
const themeResolution = useMemo(() => {
|
|
358
|
+
const selection = blendThemeName ?? theme;
|
|
330
359
|
if (!manifestMap) {
|
|
331
|
-
return {
|
|
360
|
+
return {
|
|
361
|
+
error: false,
|
|
362
|
+
name: selection && selection !== "system" ? selection : void 0
|
|
363
|
+
};
|
|
332
364
|
}
|
|
333
365
|
try {
|
|
366
|
+
if (themeBlendKey) {
|
|
367
|
+
for (const name of JSON.parse(themeBlendKey)) {
|
|
368
|
+
resolveTileflowRuntimeTheme(manifestMap, name);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
334
371
|
return {
|
|
335
372
|
error: false,
|
|
336
|
-
name: resolveTileflowRuntimeTheme(manifestMap,
|
|
373
|
+
name: resolveTileflowRuntimeTheme(manifestMap, selection, systemColorScheme).name
|
|
337
374
|
};
|
|
338
375
|
} catch {
|
|
339
376
|
return { error: true, name: void 0 };
|
|
340
377
|
}
|
|
341
|
-
}, [manifestMap, systemColorScheme, theme]);
|
|
378
|
+
}, [blendThemeName, manifestMap, systemColorScheme, theme, themeBlendKey]);
|
|
342
379
|
const resolvedThemeName = themeResolution.name;
|
|
343
380
|
const manifestView = resolveTileflowRuntimeView({ manifestMap });
|
|
344
381
|
const manifestCenter = useStableMapOptionValue(
|
|
@@ -477,6 +514,22 @@ function Map2(props) {
|
|
|
477
514
|
});
|
|
478
515
|
}, [isImageMode, manifestMap, runtimeSource, systemColorScheme, themeResolution]);
|
|
479
516
|
runtimeStyleRef.current = runtimeStyle;
|
|
517
|
+
const blendStyles = useMemo(() => {
|
|
518
|
+
if (isImageMode || !manifestMap || !themeBlendKey) return null;
|
|
519
|
+
try {
|
|
520
|
+
return resolveTileflowRuntimeThemeBlend({
|
|
521
|
+
blend: { position: 0, themes: JSON.parse(themeBlendKey) },
|
|
522
|
+
manifestMap,
|
|
523
|
+
source: runtimeSource
|
|
524
|
+
});
|
|
525
|
+
} catch {
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
}, [isImageMode, manifestMap, runtimeSource, themeBlendKey]);
|
|
529
|
+
const blendRequestRef = useRef(null);
|
|
530
|
+
blendRequestRef.current = blendStyles && themeBlendPosition !== void 0 ? { hold: themeBlendHold, position: themeBlendPosition, themes: blendStyles } : null;
|
|
531
|
+
const themeTransitionRef = useRef(themeTransition);
|
|
532
|
+
themeTransitionRef.current = themeTransition;
|
|
480
533
|
const runtimeMapIdentity = `${manifestUrl}\0${runtimeSource.map}`;
|
|
481
534
|
const runtimeStyleReady = runtimeStyle !== null || themeResolution.error && mapRef.current !== null;
|
|
482
535
|
const resolvedAnalytics = useMemo(
|
|
@@ -695,8 +748,6 @@ function Map2(props) {
|
|
|
695
748
|
});
|
|
696
749
|
registerCleanup(() => worldRequestBridge.dispose());
|
|
697
750
|
const transformRequest = createTileflowTransformRequest({
|
|
698
|
-
always: true,
|
|
699
|
-
asyncAnalyticsTiming: "resolution",
|
|
700
751
|
getAnalytics: () => resolvedAnalyticsRef.current,
|
|
701
752
|
sessionController: session,
|
|
702
753
|
sessionId: session.sessionId,
|
|
@@ -919,9 +970,15 @@ function Map2(props) {
|
|
|
919
970
|
});
|
|
920
971
|
registerCleanup(() => lifecycle.dispose());
|
|
921
972
|
const latestRuntimeStyle = runtimeStyleRef.current;
|
|
922
|
-
|
|
973
|
+
const latestBlend = blendRequestRef.current;
|
|
974
|
+
if (themeController && latestBlend) {
|
|
975
|
+
if (latestRuntimeStyle) setActiveRuntimeResource(latestRuntimeStyle);
|
|
976
|
+
void themeController.setBlend(latestBlend, { transition: themeTransitionRef.current });
|
|
977
|
+
} else if (themeController && latestRuntimeStyle && latestRuntimeStyle !== initialRuntimeStyle) {
|
|
923
978
|
setActiveRuntimeResource(latestRuntimeStyle);
|
|
924
|
-
void themeController.setTheme(latestRuntimeStyle
|
|
979
|
+
void themeController.setTheme(latestRuntimeStyle, {
|
|
980
|
+
transition: themeTransitionRef.current
|
|
981
|
+
});
|
|
925
982
|
}
|
|
926
983
|
}).catch((error) => {
|
|
927
984
|
try {
|
|
@@ -942,19 +999,33 @@ function Map2(props) {
|
|
|
942
999
|
}, [resolvedInteractive, runtimeMapIdentity, runtimeStyleReady, stableMapOptions]);
|
|
943
1000
|
useEffect(() => {
|
|
944
1001
|
const controller = themeControllerRef.current;
|
|
945
|
-
if (!controller) return;
|
|
1002
|
+
if (!controller || themeBlendKey) return;
|
|
946
1003
|
if (themeResolution.error) {
|
|
947
1004
|
void controller.setTheme(controller.getCurrent());
|
|
948
1005
|
return;
|
|
949
1006
|
}
|
|
950
|
-
if (!runtimeStyle || controller.getCurrent() === runtimeStyle)
|
|
1007
|
+
if (!runtimeStyle || controller.getCurrent() === runtimeStyle && !controller.getBlend())
|
|
1008
|
+
return;
|
|
951
1009
|
setActiveRuntimeResource(runtimeStyle);
|
|
952
|
-
void controller.setTheme(runtimeStyle).then((result) => {
|
|
1010
|
+
void controller.setTheme(runtimeStyle, { transition: themeTransitionRef.current }).then((result) => {
|
|
953
1011
|
if (result.status === "failed") {
|
|
954
1012
|
console.error("Failed to change the Tileflow map theme", result.error);
|
|
955
1013
|
}
|
|
956
1014
|
});
|
|
957
|
-
}, [runtimeStyle, themeResolution.error]);
|
|
1015
|
+
}, [runtimeStyle, themeBlendKey, themeResolution.error]);
|
|
1016
|
+
useEffect(() => {
|
|
1017
|
+
const controller = themeControllerRef.current;
|
|
1018
|
+
if (!controller || !blendStyles || themeBlendPosition === void 0) return;
|
|
1019
|
+
if (runtimeStyle) setActiveRuntimeResource(runtimeStyle);
|
|
1020
|
+
void controller.setBlend(
|
|
1021
|
+
{ hold: themeBlendHold, position: themeBlendPosition, themes: blendStyles },
|
|
1022
|
+
{ transition: themeTransitionRef.current }
|
|
1023
|
+
).then((result) => {
|
|
1024
|
+
if (result.status === "failed") {
|
|
1025
|
+
console.error("Failed to blend the Tileflow map themes", result.error);
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
}, [blendStyles, runtimeStyle, themeBlendHold, themeBlendPosition]);
|
|
958
1029
|
useLayoutEffect(() => {
|
|
959
1030
|
if (!isImageMode) return;
|
|
960
1031
|
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.29",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "React components for rendering Tileflow maps with MapLibre.",
|
|
6
6
|
"type": "module",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@tileflow/core": ">=0.1.0-alpha.
|
|
46
|
+
"@tileflow/core": ">=0.1.0-alpha.32 <0.1.0-beta.0",
|
|
47
47
|
"@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0",
|
|
48
|
-
"@tileflow/static": ">=0.1.0-alpha.
|
|
48
|
+
"@tileflow/static": ">=0.1.0-alpha.28 <0.1.0-beta.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"maplibre-gl": ">=6.4.1 <7",
|
|
@@ -53,11 +53,11 @@
|
|
|
53
53
|
"react-dom": ">=18 <20"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@types/react": "^19.
|
|
57
|
-
"@types/react-dom": "^19.
|
|
58
|
-
"maplibre-gl": "6.
|
|
59
|
-
"react": "^19.
|
|
60
|
-
"react-dom": "^19.
|
|
56
|
+
"@types/react": "^19.3.0",
|
|
57
|
+
"@types/react-dom": "^19.3.0",
|
|
58
|
+
"maplibre-gl": "6.13.0",
|
|
59
|
+
"react": "^19.3.0",
|
|
60
|
+
"react-dom": "^19.3.0"
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"build": "tsup",
|