@tileflow/react 0.1.0-alpha.36 → 0.1.0-alpha.38
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 +27 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +60 -7
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -19,6 +19,12 @@ npm install --save-dev --save-exact tileflow@alpha
|
|
|
19
19
|
The supported peer ranges are React/React DOM 18–19 and MapLibre GL JS `>=6.4.1 <7`. The worker
|
|
20
20
|
must come from the same MapLibre installation as the main module.
|
|
21
21
|
|
|
22
|
+
Prepared styles may retain relative asset URLs. The component loads them through Core's shared
|
|
23
|
+
resource loader before creating the map and before changing or blending themes. Sprites, fonts,
|
|
24
|
+
data and model resources resolve against their declaring documents. Pending document loads are
|
|
25
|
+
cancelled when the map is replaced or unmounted; `mapOptions.transformRequest` also applies to
|
|
26
|
+
style and HTTP TileJSON requests.
|
|
27
|
+
|
|
22
28
|
## Prepare a map
|
|
23
29
|
|
|
24
30
|
Create `tileflow.config.ts` at the application root:
|
|
@@ -103,7 +109,7 @@ Add `timeOfDay="dusk"` beside `theme` to select a map's optional `day`, `dawn`,
|
|
|
103
109
|
`night` preset. For Streets, `theme="light"` with this preset selects the existing `dusk` palette.
|
|
104
110
|
Maps without this capability ignore the option; omitting it keeps the selected theme unchanged.
|
|
105
111
|
Changing it uses the existing theme transition. `themeBlend` takes precedence. See the
|
|
106
|
-
[authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/
|
|
112
|
+
[authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/REFERENCE.md#style-with-themes-and-semantic-modules).
|
|
107
113
|
|
|
108
114
|
Theme and `timeOfDay` changes cross-fade over 450 ms by default, respecting reduced motion.
|
|
109
115
|
`themeTransition={{duration: 700}}` customizes the duration; use `duration: 0` for an immediate change.
|
|
@@ -239,6 +245,26 @@ annotation-only. See the
|
|
|
239
245
|
Text/field content uses safe text defaults. A `view` is an application renderer name, not HTML.
|
|
240
246
|
Keep tooltips non-interactive; put buttons, links, and forms in popups.
|
|
241
247
|
|
|
248
|
+
## Cluster points and draw density
|
|
249
|
+
|
|
250
|
+
Use `annotationClusters` for up to 1,000 annotations, or `dataSources` and `dataOverlays` for larger
|
|
251
|
+
GeoJSON sets. Clicking a cluster zooms in and reports a `cluster` target through `onInteractionEvent`.
|
|
252
|
+
|
|
253
|
+
<!-- docs:check -->
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
import {clusterOverlay, geojsonSource} from '@tileflow/core/runtime';
|
|
257
|
+
|
|
258
|
+
export const dataSources = {stores: geojsonSource({cluster: true, clusterRadius: 50})};
|
|
259
|
+
export const dataOverlays = {
|
|
260
|
+
stores: clusterOverlay({id: 'stores', placement: 'above-labels', source: 'stores'}),
|
|
261
|
+
};
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
See the
|
|
265
|
+
[interactions reference](https://github.com/tileflow/tileflow-sdk/blob/main/packages/interactions/REFERENCE.md)
|
|
266
|
+
for heatmaps, events, and polygon drawing with Terra Draw.
|
|
267
|
+
|
|
242
268
|
## Display an image or create a render
|
|
243
269
|
|
|
244
270
|
`<Map mode="image">` displays an explicit `imageUrl` or derives an image URL from the selected
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Map as Map$1, MapOptions } from 'maplibre-gl';
|
|
2
|
-
import { TileflowBoundaryValues } from '@tileflow/core';
|
|
2
|
+
import { TileflowBoundaryValues, TileflowMapLibreOverlay, TileflowGeoJsonSource } from '@tileflow/core';
|
|
3
3
|
import { CreateBoundaryFeatureStateOptions, TileflowBrowserThemeBlendSelection, TileflowThemeTransitionOptions, TileflowViewMode, TileflowThemeTransition } from '@tileflow/core/browser';
|
|
4
4
|
import * as react from 'react';
|
|
5
5
|
import { CSSProperties, ImgHTMLAttributes, ReactNode } from 'react';
|
|
6
6
|
import { TileflowRuntimeSource, TileflowThemeSelection, TileflowTimeOfDay, TileflowAnalytics } from '@tileflow/core/runtime';
|
|
7
7
|
import { TileflowAnnotation, TileflowInteractionState, TileflowAnnotationViewContext, TileflowInteractionBinding, TileflowInteractionViewContext, TileflowInteractionDiagnostic, TileflowInteractionEvent } from '@tileflow/interactions';
|
|
8
|
+
import { TileflowAnnotationClusterOptions } from '@tileflow/interactions/maplibre';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Keep a map's Boundaries feature-state equal to `values`. Pass the map from `<Map onLoad>` and
|
|
@@ -65,7 +66,13 @@ type TileflowInteractionRendererProps<TAnnotation extends TileflowAnnotation> =
|
|
|
65
66
|
renderTooltip?: TileflowInteractionRenderer<TAnnotation>;
|
|
66
67
|
}>;
|
|
67
68
|
type MapInteractiveProps<TAnnotation extends TileflowAnnotation> = MapSharedProps & TileflowInteractionStateInputProps & TileflowInteractionRendererProps<TAnnotation> & {
|
|
69
|
+
/** Group `annotations` into accessible cluster buttons. Up to 1,000 annotations. */
|
|
70
|
+
annotationClusters?: TileflowAnnotationClusterOptions;
|
|
68
71
|
annotations?: readonly TAnnotation[];
|
|
72
|
+
/** Overlays drawn over `dataSources` or compiled sources, keyed as in `defineMap`. */
|
|
73
|
+
dataOverlays?: Readonly<Record<string, TileflowMapLibreOverlay>>;
|
|
74
|
+
/** Application GeoJSON, keyed as in `defineMap`. A compiled source of the same key is filled. */
|
|
75
|
+
dataSources?: Readonly<Record<string, TileflowGeoJsonSource>>;
|
|
69
76
|
mode?: 'interactive';
|
|
70
77
|
onInteractionDiagnostic?: (diagnostic: TileflowInteractionDiagnostic) => void;
|
|
71
78
|
onInteractionEvent?: (event: TileflowInteractionEvent<TAnnotation>) => void;
|
|
@@ -73,7 +80,10 @@ type MapInteractiveProps<TAnnotation extends TileflowAnnotation> = MapSharedProp
|
|
|
73
80
|
renderMarker?: TileflowAnnotationRenderer<TAnnotation>;
|
|
74
81
|
};
|
|
75
82
|
type MapImageProps = MapSharedProps & {
|
|
83
|
+
annotationClusters?: never;
|
|
76
84
|
annotations?: never;
|
|
85
|
+
dataOverlays?: never;
|
|
86
|
+
dataSources?: never;
|
|
77
87
|
defaultInteractionState?: never;
|
|
78
88
|
interactionState?: never;
|
|
79
89
|
interactions?: never;
|
package/dist/index.js
CHANGED
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
createTileflowThemeController,
|
|
52
52
|
createTileflowTransformRequest,
|
|
53
53
|
getTileflowSystemColorScheme,
|
|
54
|
+
loadTileflowRuntimeStyle,
|
|
54
55
|
loadTileflowStyleFonts,
|
|
55
56
|
registerTileflowContourProtocol,
|
|
56
57
|
registerTileflowPathGroundProtocol,
|
|
@@ -79,6 +80,7 @@ import {
|
|
|
79
80
|
resolveTileflowStaticImageUrl
|
|
80
81
|
} from "@tileflow/core/runtime";
|
|
81
82
|
import {
|
|
83
|
+
createTileflowMapLibreDataRuntime,
|
|
82
84
|
createTileflowMapLibreDomRuntime,
|
|
83
85
|
createTileflowMapLibreInteractionCoordinator,
|
|
84
86
|
createTileflowMapLibreSemanticDomRuntime
|
|
@@ -284,7 +286,10 @@ var defaultInteractions = [];
|
|
|
284
286
|
function Map2(props) {
|
|
285
287
|
assertTileflowMapStyleInputs(props);
|
|
286
288
|
const {
|
|
289
|
+
annotationClusters,
|
|
287
290
|
annotations,
|
|
291
|
+
dataOverlays,
|
|
292
|
+
dataSources,
|
|
288
293
|
captureId,
|
|
289
294
|
defaultInteractionState,
|
|
290
295
|
mode = "interactive",
|
|
@@ -361,6 +366,8 @@ function Map2(props) {
|
|
|
361
366
|
const interactionCoordinatorRef = useRef(
|
|
362
367
|
void 0
|
|
363
368
|
);
|
|
369
|
+
const dataRuntimeRef = useRef(void 0);
|
|
370
|
+
const dataInputRef = useRef({});
|
|
364
371
|
const annotationsRef = useRef(preparedAnnotations.annotations);
|
|
365
372
|
const annotationTitlesRef = useRef(preparedAnnotations.titles);
|
|
366
373
|
const preparedInteractionsRef = useRef(preparedInteractions);
|
|
@@ -455,7 +462,7 @@ function Map2(props) {
|
|
|
455
462
|
const mapNameRef = useRef(mapName);
|
|
456
463
|
const resolvedAnalyticsRef = useRef(void 0);
|
|
457
464
|
const readinessRunRef = useRef(0);
|
|
458
|
-
const hasAdditionalInteractionConfiguration = annotations !== void 0 || defaultInteractionState !== void 0 || interactionState !== void 0 || onInteractionDiagnostic !== void 0 || onInteractionEvent !== void 0 || onInteractionStateChange !== void 0 || renderMarker !== void 0 || renderPopup !== void 0 || renderTooltip !== void 0 || props.interactions !== void 0;
|
|
465
|
+
const hasAdditionalInteractionConfiguration = annotationClusters !== void 0 || annotations !== void 0 || dataOverlays !== void 0 || dataSources !== void 0 || defaultInteractionState !== void 0 || interactionState !== void 0 || onInteractionDiagnostic !== void 0 || onInteractionEvent !== void 0 || onInteractionStateChange !== void 0 || renderMarker !== void 0 || renderPopup !== void 0 || renderTooltip !== void 0 || props.interactions !== void 0;
|
|
459
466
|
const modeDiagnostic = useMemo(
|
|
460
467
|
() => isImageMode ? imageModeMapInteractionDiagnostic(
|
|
461
468
|
preparedAnnotations.annotations.length,
|
|
@@ -682,6 +689,15 @@ function Map2(props) {
|
|
|
682
689
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
683
690
|
}
|
|
684
691
|
}, [preparedAnnotations]);
|
|
692
|
+
dataInputRef.current = {
|
|
693
|
+
annotationClusters,
|
|
694
|
+
annotations: preparedAnnotations.annotations,
|
|
695
|
+
dataOverlays,
|
|
696
|
+
dataSources
|
|
697
|
+
};
|
|
698
|
+
useEffect2(() => {
|
|
699
|
+
dataRuntimeRef.current?.reconcile(dataInputRef.current);
|
|
700
|
+
}, [annotationClusters, dataOverlays, dataSources, preparedAnnotations]);
|
|
685
701
|
useEffect2(() => {
|
|
686
702
|
const runtime = semanticRuntimeRef.current;
|
|
687
703
|
if (!runtime || !preparedInteractions.ok) return;
|
|
@@ -748,12 +764,28 @@ function Map2(props) {
|
|
|
748
764
|
const container = containerRef.current;
|
|
749
765
|
let cancelled = false;
|
|
750
766
|
let disposeMap;
|
|
767
|
+
const resourceLifetime = new AbortController();
|
|
768
|
+
const session = createTileflowSessionController({ source: "react" });
|
|
769
|
+
const resourceTransform = createTileflowTransformRequest({
|
|
770
|
+
getAnalytics: () => resolvedAnalyticsRef.current,
|
|
771
|
+
sessionController: session,
|
|
772
|
+
sessionId: session.sessionId,
|
|
773
|
+
transformRequest: stableMapOptions?.transformRequest ?? void 0
|
|
774
|
+
});
|
|
775
|
+
const styleLoadOptions = {
|
|
776
|
+
signal: resourceLifetime.signal,
|
|
777
|
+
transformRequest: (url, type) => resourceTransform(
|
|
778
|
+
url,
|
|
779
|
+
type
|
|
780
|
+
)
|
|
781
|
+
};
|
|
751
782
|
void Promise.all([
|
|
752
783
|
loadTileflowMapLibre(),
|
|
753
|
-
|
|
754
|
-
fontFaces:
|
|
784
|
+
loadTileflowRuntimeStyle(initialRuntimeStyle, styleLoadOptions).then(async (prepared) => {
|
|
785
|
+
await loadTileflowStyleFonts(prepared.style, { fontFaces: prepared.fontFaces });
|
|
786
|
+
return prepared;
|
|
755
787
|
})
|
|
756
|
-
]).then(([maplibregl]) => {
|
|
788
|
+
]).then(([maplibregl, prepared]) => {
|
|
757
789
|
if (cancelled || containerRef.current !== container) {
|
|
758
790
|
return;
|
|
759
791
|
}
|
|
@@ -787,7 +819,6 @@ function Map2(props) {
|
|
|
787
819
|
setRendererBridgeDiagnostic(void 0);
|
|
788
820
|
}
|
|
789
821
|
};
|
|
790
|
-
const session = createTileflowSessionController({ source: "react" });
|
|
791
822
|
const sessionStarter = createTileflowSessionStarter({
|
|
792
823
|
getSessionId: () => session.sessionId,
|
|
793
824
|
sessionId: session.sessionId,
|
|
@@ -816,7 +847,7 @@ function Map2(props) {
|
|
|
816
847
|
stableMapOptions?.canvasContextAttributes
|
|
817
848
|
),
|
|
818
849
|
container,
|
|
819
|
-
style:
|
|
850
|
+
style: prepared.style,
|
|
820
851
|
bearing: bearingRef.current,
|
|
821
852
|
center: centerRef.current,
|
|
822
853
|
pitch: pitchRef.current,
|
|
@@ -839,7 +870,9 @@ function Map2(props) {
|
|
|
839
870
|
registerCleanup(attachTileflowLabelPlacement(map));
|
|
840
871
|
registerCleanup(attachTileflowHorizonQueries(map));
|
|
841
872
|
const themeController = initialRuntimeStyle.theme === void 0 ? void 0 : createTileflowThemeController({
|
|
842
|
-
initial: initialRuntimeStyle,
|
|
873
|
+
initial: prepared.family ? prepared : initialRuntimeStyle,
|
|
874
|
+
initialStyle: prepared.style,
|
|
875
|
+
styleLoadOptions,
|
|
843
876
|
map,
|
|
844
877
|
onTransition(transition) {
|
|
845
878
|
onThemeChangeRef.current?.(transition);
|
|
@@ -933,6 +966,23 @@ function Map2(props) {
|
|
|
933
966
|
interactionRuntimeRef.current = interactionRuntime;
|
|
934
967
|
setupResources.annotationRuntime = interactionRuntime;
|
|
935
968
|
registerCleanup(() => interactionRuntime.dispose());
|
|
969
|
+
const dataRuntime = createTileflowMapLibreDataRuntime({
|
|
970
|
+
createMarker: ({ element }) => wrapMapLibrePositioned(new maplibregl.Marker({ element })),
|
|
971
|
+
document: container.ownerDocument,
|
|
972
|
+
map,
|
|
973
|
+
onDiagnostic(diagnostic) {
|
|
974
|
+
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
975
|
+
},
|
|
976
|
+
onInteractionEvent(event) {
|
|
977
|
+
onInteractionEventRef.current?.(event);
|
|
978
|
+
},
|
|
979
|
+
setHiddenAnnotations: interactionRuntime.setHiddenAnnotations
|
|
980
|
+
});
|
|
981
|
+
dataRuntimeRef.current = dataRuntime;
|
|
982
|
+
registerCleanup(() => {
|
|
983
|
+
dataRuntime.dispose();
|
|
984
|
+
if (dataRuntimeRef.current === dataRuntime) dataRuntimeRef.current = void 0;
|
|
985
|
+
});
|
|
936
986
|
const semanticRuntime = createTileflowMapLibreSemanticDomRuntime({
|
|
937
987
|
cancelFrame: (frame) => browserWindow.cancelAnimationFrame(frame),
|
|
938
988
|
createOverlay({ container: overlayContainer, kind }) {
|
|
@@ -1011,6 +1061,7 @@ function Map2(props) {
|
|
|
1011
1061
|
setAnnotationBridgeDiagnostic(diagnostic);
|
|
1012
1062
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
1013
1063
|
}
|
|
1064
|
+
dataRuntime.reconcile(dataInputRef.current);
|
|
1014
1065
|
const initialInteractions = preparedInteractionsRef.current;
|
|
1015
1066
|
if (initialInteractions.ok) {
|
|
1016
1067
|
try {
|
|
@@ -1070,6 +1121,7 @@ function Map2(props) {
|
|
|
1070
1121
|
});
|
|
1071
1122
|
}
|
|
1072
1123
|
}).catch((error) => {
|
|
1124
|
+
resourceLifetime.abort();
|
|
1073
1125
|
try {
|
|
1074
1126
|
disposeMap?.();
|
|
1075
1127
|
} catch (cleanupError) {
|
|
@@ -1082,6 +1134,7 @@ function Map2(props) {
|
|
|
1082
1134
|
});
|
|
1083
1135
|
return () => {
|
|
1084
1136
|
cancelled = true;
|
|
1137
|
+
resourceLifetime.abort();
|
|
1085
1138
|
readinessRunRef.current += 1;
|
|
1086
1139
|
disposeMap?.();
|
|
1087
1140
|
};
|
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.38",
|
|
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.
|
|
47
|
-
"@tileflow/interactions": ">=0.1.0-alpha.
|
|
48
|
-
"@tileflow/static": ">=0.1.0-alpha.
|
|
46
|
+
"@tileflow/core": ">=0.1.0-alpha.48 <0.1.0-beta.0",
|
|
47
|
+
"@tileflow/interactions": ">=0.1.0-alpha.8 <0.1.0-beta.0",
|
|
48
|
+
"@tileflow/static": ">=0.1.0-alpha.30 <0.1.0-beta.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"maplibre-gl": ">=6.4.1 <7",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/react": "^19.3.0",
|
|
57
57
|
"@types/react-dom": "^19.3.0",
|
|
58
|
-
"maplibre-gl": "6.
|
|
58
|
+
"maplibre-gl": "6.16.0",
|
|
59
59
|
"react": "^19.3.0",
|
|
60
60
|
"react-dom": "^19.3.0"
|
|
61
61
|
},
|