@tileflow/react 0.1.0-alpha.30 → 0.1.0-alpha.31
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 +32 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +64 -35
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -124,6 +124,38 @@ a Tileflow map through its manifest; it has no renderer discriminator or direct-
|
|
|
124
124
|
The obsolete source fields `kind` and `style` are rejected. For a completely unmanaged map, use
|
|
125
125
|
`maplibre-gl` directly rather than a Tileflow component.
|
|
126
126
|
|
|
127
|
+
## Color boundary areas from application data
|
|
128
|
+
|
|
129
|
+
For a map that declares `boundaryAreas` and a `boundaryChoropleth()` overlay without `values`,
|
|
130
|
+
`useBoundaryFeatureState` keeps its feature-state equal to your rows. Take the map from `onLoad`
|
|
131
|
+
and memoize the values; each new object replaces the previous ones, and `null` clears them:
|
|
132
|
+
|
|
133
|
+
<!-- docs:check -->
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import {useMemo, useState} from 'react';
|
|
137
|
+
import type {Map as MapLibreMap} from 'maplibre-gl';
|
|
138
|
+
import {Map, useBoundaryFeatureState} from '@tileflow/react';
|
|
139
|
+
|
|
140
|
+
export function Unemployment({rates}: {rates: {ine: string; rate: number}[]}) {
|
|
141
|
+
const [map, setMap] = useState<MapLibreMap | null>(null);
|
|
142
|
+
const values = useMemo(
|
|
143
|
+
() => ({
|
|
144
|
+
joinBy: 'ref' as const,
|
|
145
|
+
rows: rates.map(({ine, rate}) => ({key: `ES:INE:${ine}`, value: rate})),
|
|
146
|
+
}),
|
|
147
|
+
[rates],
|
|
148
|
+
);
|
|
149
|
+
useBoundaryFeatureState(map, values);
|
|
150
|
+
return <Map source={{map: 'madrid-unemployment'}} onLoad={setMap} />;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Rows join by `ref` here, so this map declares `boundaryAreas: {family: 'boundaries', promoteId:
|
|
155
|
+
'ref'}`. Without `promoteId`, rows join by registry `id`. See
|
|
156
|
+
[boundary areas and choropleths](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/boundary-areas-and-choropleths.md)
|
|
157
|
+
for scales, joins and the lookup table.
|
|
158
|
+
|
|
127
159
|
## Add annotations and popups
|
|
128
160
|
|
|
129
161
|
Install the contract package when importing its types directly:
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import { Map as Map$1, MapOptions } from 'maplibre-gl';
|
|
2
|
+
import { TileflowBoundaryValues } from '@tileflow/core';
|
|
3
|
+
import { CreateBoundaryFeatureStateOptions, TileflowBrowserThemeBlendSelection, TileflowThemeTransitionOptions, TileflowViewMode, TileflowThemeTransition } from '@tileflow/core/browser';
|
|
1
4
|
import * as react from 'react';
|
|
2
5
|
import { CSSProperties, ImgHTMLAttributes, ReactNode } from 'react';
|
|
3
|
-
import { MapOptions, Map as Map$1 } from 'maplibre-gl';
|
|
4
|
-
import { TileflowBrowserThemeBlendSelection, TileflowThemeTransitionOptions, TileflowViewMode, TileflowThemeTransition } from '@tileflow/core/browser';
|
|
5
6
|
import { TileflowRuntimeSource, TileflowThemeSelection, TileflowTimeOfDay, TileflowAnalytics } from '@tileflow/core/runtime';
|
|
6
7
|
import { TileflowAnnotation, TileflowInteractionState, TileflowAnnotationViewContext, TileflowInteractionBinding, TileflowInteractionViewContext, TileflowInteractionDiagnostic, TileflowInteractionEvent } from '@tileflow/interactions';
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Keep a map's Boundaries feature-state equal to `values`. Pass the map from `<Map onLoad>` and
|
|
11
|
+
* memoize `values`; each new object replaces the previous values. `null` clears them.
|
|
12
|
+
*/
|
|
13
|
+
declare function useBoundaryFeatureState(map: Map$1 | null | undefined, values: TileflowBoundaryValues | null | undefined, options?: CreateBoundaryFeatureStateOptions): void;
|
|
14
|
+
|
|
8
15
|
type TileflowMapStyleSourceProps = {
|
|
9
16
|
source: TileflowRuntimeSource;
|
|
10
17
|
theme?: TileflowThemeSelection;
|
|
@@ -87,4 +94,4 @@ type TileflowMapLibreConfiguration = Readonly<{
|
|
|
87
94
|
/** Configure the application-owned worker before mounting an interactive Tileflow map. */
|
|
88
95
|
declare function configureTileflowMapLibre(configuration: TileflowMapLibreConfiguration): void;
|
|
89
96
|
|
|
90
|
-
export { Map, type MapProps, type TileflowAnnotationRenderer, type TileflowInteractionRenderer, type TileflowMapLibreConfiguration, type TileflowMapOptions, type TileflowMapSource, configureTileflowMapLibre };
|
|
97
|
+
export { Map, type MapProps, type TileflowAnnotationRenderer, type TileflowInteractionRenderer, type TileflowMapLibreConfiguration, type TileflowMapOptions, type TileflowMapSource, configureTileflowMapLibre, useBoundaryFeatureState };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,41 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
// src/boundary-feature-state.ts
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import {
|
|
6
|
+
createBoundaryFeatureState
|
|
7
|
+
} from "@tileflow/core/browser";
|
|
8
|
+
function useBoundaryFeatureState(map, values, options = {}) {
|
|
9
|
+
const { family, labels, state } = options;
|
|
10
|
+
const [controller, setController] = useState(null);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!map) return void 0;
|
|
13
|
+
const created = createBoundaryFeatureState(map, {
|
|
14
|
+
...family === void 0 ? {} : { family },
|
|
15
|
+
...labels === void 0 ? {} : { labels },
|
|
16
|
+
...state === void 0 ? {} : { state }
|
|
17
|
+
});
|
|
18
|
+
setController(created);
|
|
19
|
+
return () => {
|
|
20
|
+
setController(null);
|
|
21
|
+
created.dispose();
|
|
22
|
+
};
|
|
23
|
+
}, [map, family, labels, state]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!controller) return;
|
|
26
|
+
if (values) controller.set(values);
|
|
27
|
+
else controller.clear();
|
|
28
|
+
}, [controller, values]);
|
|
29
|
+
}
|
|
30
|
+
|
|
3
31
|
// src/map.tsx
|
|
4
32
|
import {
|
|
5
33
|
useCallback,
|
|
6
|
-
useEffect,
|
|
34
|
+
useEffect as useEffect2,
|
|
7
35
|
useLayoutEffect,
|
|
8
36
|
useMemo,
|
|
9
37
|
useRef,
|
|
10
|
-
useState
|
|
38
|
+
useState as useState2
|
|
11
39
|
} from "react";
|
|
12
40
|
import { createPortal } from "react-dom";
|
|
13
41
|
import {
|
|
@@ -313,7 +341,7 @@ function Map2(props) {
|
|
|
313
341
|
const vegetationRef = useRef(void 0);
|
|
314
342
|
const vegetationInputsRef = useRef({ vegetation, viewMode });
|
|
315
343
|
vegetationInputsRef.current = { vegetation, viewMode };
|
|
316
|
-
|
|
344
|
+
useEffect2(() => {
|
|
317
345
|
vegetationRef.current?.setVisible(vegetation);
|
|
318
346
|
vegetationRef.current?.setViewMode(viewMode);
|
|
319
347
|
}, [vegetation, viewMode]);
|
|
@@ -338,26 +366,26 @@ function Map2(props) {
|
|
|
338
366
|
const renderPopupRef = useRef(renderPopup);
|
|
339
367
|
const renderTooltipRef = useRef(renderTooltip);
|
|
340
368
|
const reportedDeclarativeDiagnosticKeysRef = useRef(/* @__PURE__ */ new Set());
|
|
341
|
-
const [manifestResolution, setManifestResolution] =
|
|
369
|
+
const [manifestResolution, setManifestResolution] = useState2({
|
|
342
370
|
key: "",
|
|
343
371
|
map: null,
|
|
344
372
|
state: "loading"
|
|
345
373
|
});
|
|
346
|
-
const [systemColorScheme, setSystemColorScheme] =
|
|
347
|
-
const [imageSize, setImageSize] =
|
|
348
|
-
const [captureState, setCaptureState] =
|
|
349
|
-
const [themeTransitionState, setThemeTransitionState] =
|
|
374
|
+
const [systemColorScheme, setSystemColorScheme] = useState2("light");
|
|
375
|
+
const [imageSize, setImageSize] = useState2(null);
|
|
376
|
+
const [captureState, setCaptureState] = useState2("loading");
|
|
377
|
+
const [themeTransitionState, setThemeTransitionState] = useState2(
|
|
350
378
|
"idle"
|
|
351
379
|
);
|
|
352
|
-
const [activeRuntimeResource, setActiveRuntimeResource] =
|
|
353
|
-
const [annotationRenderTargets, setAnnotationRenderTargets] =
|
|
354
|
-
const [semanticRenderTargets, setSemanticRenderTargets] =
|
|
355
|
-
const [annotationBridgeDiagnostic, setAnnotationBridgeDiagnostic] =
|
|
356
|
-
const [semanticBridgeDiagnostic, setSemanticBridgeDiagnostic] =
|
|
357
|
-
const [stateBridgeDiagnostic, setStateBridgeDiagnostic] =
|
|
358
|
-
const [rendererBridgeDiagnostic, setRendererBridgeDiagnostic] =
|
|
359
|
-
const [annotationDiagnostics, setAnnotationDiagnostics] =
|
|
360
|
-
const [semanticDiagnostics, setSemanticDiagnostics] =
|
|
380
|
+
const [activeRuntimeResource, setActiveRuntimeResource] = useState2(void 0);
|
|
381
|
+
const [annotationRenderTargets, setAnnotationRenderTargets] = useState2([]);
|
|
382
|
+
const [semanticRenderTargets, setSemanticRenderTargets] = useState2([]);
|
|
383
|
+
const [annotationBridgeDiagnostic, setAnnotationBridgeDiagnostic] = useState2();
|
|
384
|
+
const [semanticBridgeDiagnostic, setSemanticBridgeDiagnostic] = useState2();
|
|
385
|
+
const [stateBridgeDiagnostic, setStateBridgeDiagnostic] = useState2();
|
|
386
|
+
const [rendererBridgeDiagnostic, setRendererBridgeDiagnostic] = useState2();
|
|
387
|
+
const [annotationDiagnostics, setAnnotationDiagnostics] = useState2([]);
|
|
388
|
+
const [semanticDiagnostics, setSemanticDiagnostics] = useState2([]);
|
|
361
389
|
const stableMapOptions = useStableMapOptions(mapOptions);
|
|
362
390
|
const resolvedCaptureId = normalizeTileflowCaptureId(captureId);
|
|
363
391
|
const resolvedMode = resolveTileflowMapMode({ mode });
|
|
@@ -484,12 +512,12 @@ function Map2(props) {
|
|
|
484
512
|
renderPopupRef.current = renderPopup;
|
|
485
513
|
renderTooltipRef.current = renderTooltip;
|
|
486
514
|
onThemeChangeRef.current = props.onThemeChange;
|
|
487
|
-
|
|
515
|
+
useEffect2(() => {
|
|
488
516
|
if (theme !== "system") return;
|
|
489
517
|
setSystemColorScheme(getTileflowSystemColorScheme());
|
|
490
518
|
return subscribeTileflowSystemColorScheme(setSystemColorScheme);
|
|
491
519
|
}, [theme]);
|
|
492
|
-
|
|
520
|
+
useEffect2(() => {
|
|
493
521
|
const previousKeys = reportedDeclarativeDiagnosticKeysRef.current;
|
|
494
522
|
const nextKeys = /* @__PURE__ */ new Set();
|
|
495
523
|
for (const diagnostic of declarativeInteractionDiagnostics) {
|
|
@@ -499,7 +527,7 @@ function Map2(props) {
|
|
|
499
527
|
}
|
|
500
528
|
reportedDeclarativeDiagnosticKeysRef.current = nextKeys;
|
|
501
529
|
}, [declarativeInteractionDiagnostics]);
|
|
502
|
-
|
|
530
|
+
useEffect2(() => {
|
|
503
531
|
let cancelled = false;
|
|
504
532
|
setManifestResolution({ key: manifestRequestKey, map: null, state: "loading" });
|
|
505
533
|
loadTileflowManifest(manifestUrl).then((manifest) => {
|
|
@@ -556,7 +584,7 @@ function Map2(props) {
|
|
|
556
584
|
() => mergeTileflowAnalytics(analytics, runtimeStyle?.analytics),
|
|
557
585
|
[analytics, runtimeStyle]
|
|
558
586
|
);
|
|
559
|
-
|
|
587
|
+
useEffect2(() => {
|
|
560
588
|
centerRef.current = resolvedCenter;
|
|
561
589
|
zoomRef.current = resolvedZoom;
|
|
562
590
|
bearingRef.current = resolvedBearing;
|
|
@@ -568,13 +596,13 @@ function Map2(props) {
|
|
|
568
596
|
zoom: resolvedZoom
|
|
569
597
|
});
|
|
570
598
|
}, [resolvedBearing, resolvedCenter, resolvedPitch, resolvedZoom]);
|
|
571
|
-
|
|
599
|
+
useEffect2(() => {
|
|
572
600
|
onLoadRef.current = onLoad;
|
|
573
601
|
}, [onLoad]);
|
|
574
|
-
|
|
602
|
+
useEffect2(() => {
|
|
575
603
|
mapNameRef.current = mapName;
|
|
576
604
|
}, [mapName]);
|
|
577
|
-
|
|
605
|
+
useEffect2(() => {
|
|
578
606
|
resolvedAnalyticsRef.current = resolvedAnalytics;
|
|
579
607
|
}, [resolvedAnalytics]);
|
|
580
608
|
const runtimeImageUrl = useMemo(
|
|
@@ -610,7 +638,7 @@ function Map2(props) {
|
|
|
610
638
|
})();
|
|
611
639
|
const currentCaptureState = activeRuntimeResource === (isImageMode ? runtimeImageUrl : runtimeStyle) ? captureState : "loading";
|
|
612
640
|
const effectiveCaptureState = currentCaptureState === "error" || interactionReadiness === "error" || runtimeResolutionState === "error" || themeTransitionState === "error" ? "error" : currentCaptureState === "idle" && interactionReadiness === "idle" && runtimeResolutionState === "idle" && themeTransitionState === "idle" ? "idle" : "loading";
|
|
613
|
-
|
|
641
|
+
useEffect2(() => {
|
|
614
642
|
if (!isImageMode || imageUrl || !containerRef.current) {
|
|
615
643
|
setImageSize(null);
|
|
616
644
|
return;
|
|
@@ -632,7 +660,7 @@ function Map2(props) {
|
|
|
632
660
|
resizeObserver.disconnect();
|
|
633
661
|
};
|
|
634
662
|
}, [imageUrl, isImageMode]);
|
|
635
|
-
|
|
663
|
+
useEffect2(() => {
|
|
636
664
|
const runtime = interactionRuntimeRef.current;
|
|
637
665
|
if (!runtime || !preparedAnnotations.ok) return;
|
|
638
666
|
try {
|
|
@@ -647,7 +675,7 @@ function Map2(props) {
|
|
|
647
675
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
648
676
|
}
|
|
649
677
|
}, [preparedAnnotations]);
|
|
650
|
-
|
|
678
|
+
useEffect2(() => {
|
|
651
679
|
const runtime = semanticRuntimeRef.current;
|
|
652
680
|
if (!runtime || !preparedInteractions.ok) return;
|
|
653
681
|
try {
|
|
@@ -662,7 +690,7 @@ function Map2(props) {
|
|
|
662
690
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
663
691
|
}
|
|
664
692
|
}, [preparedInteractions]);
|
|
665
|
-
|
|
693
|
+
useEffect2(() => {
|
|
666
694
|
if (!preparedInteractionState.controlled || !preparedInteractionState.ok) return;
|
|
667
695
|
try {
|
|
668
696
|
setStateBridgeDiagnostic(void 0);
|
|
@@ -677,7 +705,7 @@ function Map2(props) {
|
|
|
677
705
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
678
706
|
}
|
|
679
707
|
}, [preparedInteractionState]);
|
|
680
|
-
|
|
708
|
+
useEffect2(() => {
|
|
681
709
|
const annotationRuntime = interactionRuntimeRef.current;
|
|
682
710
|
const semanticRuntime = semanticRuntimeRef.current;
|
|
683
711
|
if (!annotationRuntime && !semanticRuntime) return;
|
|
@@ -701,7 +729,7 @@ function Map2(props) {
|
|
|
701
729
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
702
730
|
}
|
|
703
731
|
}, [renderMarker, renderPopup, renderTooltip]);
|
|
704
|
-
|
|
732
|
+
useEffect2(() => {
|
|
705
733
|
setThemeTransitionState("idle");
|
|
706
734
|
const initialRuntimeStyle = runtimeStyleRef.current;
|
|
707
735
|
if (!containerRef.current || !initialRuntimeStyle) {
|
|
@@ -1038,7 +1066,7 @@ function Map2(props) {
|
|
|
1038
1066
|
disposeMap?.();
|
|
1039
1067
|
};
|
|
1040
1068
|
}, [resolvedInteractive, runtimeMapIdentity, runtimeStyleReady, stableMapOptions]);
|
|
1041
|
-
|
|
1069
|
+
useEffect2(() => {
|
|
1042
1070
|
const controller = themeControllerRef.current;
|
|
1043
1071
|
if (!controller || themeBlendKey) return;
|
|
1044
1072
|
if (themeResolution.error) {
|
|
@@ -1054,7 +1082,7 @@ function Map2(props) {
|
|
|
1054
1082
|
}
|
|
1055
1083
|
});
|
|
1056
1084
|
}, [runtimeStyle, themeBlendKey, themeResolution.error]);
|
|
1057
|
-
|
|
1085
|
+
useEffect2(() => {
|
|
1058
1086
|
const controller = themeControllerRef.current;
|
|
1059
1087
|
if (!controller || !blendStyles || themeBlendPosition === void 0) return;
|
|
1060
1088
|
if (runtimeStyle) setActiveRuntimeResource(runtimeStyle);
|
|
@@ -1278,11 +1306,11 @@ function useTileflowPortalReadiness(portalKeys) {
|
|
|
1278
1306
|
expectedKeysRef.current = { keys: [...portalKeys].sort(), token };
|
|
1279
1307
|
}
|
|
1280
1308
|
const expectedKeys = expectedKeysRef.current.keys;
|
|
1281
|
-
const [committed, setCommitted] =
|
|
1309
|
+
const [committed, setCommitted] = useState2(() => ({
|
|
1282
1310
|
keys: /* @__PURE__ */ new Set(),
|
|
1283
1311
|
token
|
|
1284
1312
|
}));
|
|
1285
|
-
const [readyToken, setReadyToken] =
|
|
1313
|
+
const [readyToken, setReadyToken] = useState2(token);
|
|
1286
1314
|
const readinessRunRef = useRef(0);
|
|
1287
1315
|
const markCommitted = useCallback((committedToken, portalKey) => {
|
|
1288
1316
|
setCommitted((current) => {
|
|
@@ -1412,5 +1440,6 @@ function isPlainObject(value) {
|
|
|
1412
1440
|
}
|
|
1413
1441
|
export {
|
|
1414
1442
|
Map2 as Map,
|
|
1415
|
-
configureTileflowMapLibre
|
|
1443
|
+
configureTileflowMapLibre,
|
|
1444
|
+
useBoundaryFeatureState
|
|
1416
1445
|
};
|
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.31",
|
|
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.36 <0.1.0-beta.0",
|
|
47
47
|
"@tileflow/interactions": ">=0.1.0-alpha.5 <0.1.0-beta.0",
|
|
48
48
|
"@tileflow/static": ">=0.1.0-alpha.28 <0.1.0-beta.0"
|
|
49
49
|
},
|