@tileflow/react 0.1.0-alpha.30 → 0.1.0-alpha.32
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 +66 -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,19 +1,48 @@
|
|
|
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 {
|
|
14
42
|
attachTileflowAtmosphere,
|
|
15
43
|
attachTileflowFairUseNotice,
|
|
16
44
|
attachTileflowMapLifecycle,
|
|
45
|
+
attachTileflowNotices,
|
|
17
46
|
createTileflowSessionStarter,
|
|
18
47
|
createTileflowThemeController,
|
|
19
48
|
createTileflowVegetation,
|
|
@@ -313,7 +342,7 @@ function Map2(props) {
|
|
|
313
342
|
const vegetationRef = useRef(void 0);
|
|
314
343
|
const vegetationInputsRef = useRef({ vegetation, viewMode });
|
|
315
344
|
vegetationInputsRef.current = { vegetation, viewMode };
|
|
316
|
-
|
|
345
|
+
useEffect2(() => {
|
|
317
346
|
vegetationRef.current?.setVisible(vegetation);
|
|
318
347
|
vegetationRef.current?.setViewMode(viewMode);
|
|
319
348
|
}, [vegetation, viewMode]);
|
|
@@ -338,26 +367,26 @@ function Map2(props) {
|
|
|
338
367
|
const renderPopupRef = useRef(renderPopup);
|
|
339
368
|
const renderTooltipRef = useRef(renderTooltip);
|
|
340
369
|
const reportedDeclarativeDiagnosticKeysRef = useRef(/* @__PURE__ */ new Set());
|
|
341
|
-
const [manifestResolution, setManifestResolution] =
|
|
370
|
+
const [manifestResolution, setManifestResolution] = useState2({
|
|
342
371
|
key: "",
|
|
343
372
|
map: null,
|
|
344
373
|
state: "loading"
|
|
345
374
|
});
|
|
346
|
-
const [systemColorScheme, setSystemColorScheme] =
|
|
347
|
-
const [imageSize, setImageSize] =
|
|
348
|
-
const [captureState, setCaptureState] =
|
|
349
|
-
const [themeTransitionState, setThemeTransitionState] =
|
|
375
|
+
const [systemColorScheme, setSystemColorScheme] = useState2("light");
|
|
376
|
+
const [imageSize, setImageSize] = useState2(null);
|
|
377
|
+
const [captureState, setCaptureState] = useState2("loading");
|
|
378
|
+
const [themeTransitionState, setThemeTransitionState] = useState2(
|
|
350
379
|
"idle"
|
|
351
380
|
);
|
|
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] =
|
|
381
|
+
const [activeRuntimeResource, setActiveRuntimeResource] = useState2(void 0);
|
|
382
|
+
const [annotationRenderTargets, setAnnotationRenderTargets] = useState2([]);
|
|
383
|
+
const [semanticRenderTargets, setSemanticRenderTargets] = useState2([]);
|
|
384
|
+
const [annotationBridgeDiagnostic, setAnnotationBridgeDiagnostic] = useState2();
|
|
385
|
+
const [semanticBridgeDiagnostic, setSemanticBridgeDiagnostic] = useState2();
|
|
386
|
+
const [stateBridgeDiagnostic, setStateBridgeDiagnostic] = useState2();
|
|
387
|
+
const [rendererBridgeDiagnostic, setRendererBridgeDiagnostic] = useState2();
|
|
388
|
+
const [annotationDiagnostics, setAnnotationDiagnostics] = useState2([]);
|
|
389
|
+
const [semanticDiagnostics, setSemanticDiagnostics] = useState2([]);
|
|
361
390
|
const stableMapOptions = useStableMapOptions(mapOptions);
|
|
362
391
|
const resolvedCaptureId = normalizeTileflowCaptureId(captureId);
|
|
363
392
|
const resolvedMode = resolveTileflowMapMode({ mode });
|
|
@@ -484,12 +513,12 @@ function Map2(props) {
|
|
|
484
513
|
renderPopupRef.current = renderPopup;
|
|
485
514
|
renderTooltipRef.current = renderTooltip;
|
|
486
515
|
onThemeChangeRef.current = props.onThemeChange;
|
|
487
|
-
|
|
516
|
+
useEffect2(() => {
|
|
488
517
|
if (theme !== "system") return;
|
|
489
518
|
setSystemColorScheme(getTileflowSystemColorScheme());
|
|
490
519
|
return subscribeTileflowSystemColorScheme(setSystemColorScheme);
|
|
491
520
|
}, [theme]);
|
|
492
|
-
|
|
521
|
+
useEffect2(() => {
|
|
493
522
|
const previousKeys = reportedDeclarativeDiagnosticKeysRef.current;
|
|
494
523
|
const nextKeys = /* @__PURE__ */ new Set();
|
|
495
524
|
for (const diagnostic of declarativeInteractionDiagnostics) {
|
|
@@ -499,7 +528,7 @@ function Map2(props) {
|
|
|
499
528
|
}
|
|
500
529
|
reportedDeclarativeDiagnosticKeysRef.current = nextKeys;
|
|
501
530
|
}, [declarativeInteractionDiagnostics]);
|
|
502
|
-
|
|
531
|
+
useEffect2(() => {
|
|
503
532
|
let cancelled = false;
|
|
504
533
|
setManifestResolution({ key: manifestRequestKey, map: null, state: "loading" });
|
|
505
534
|
loadTileflowManifest(manifestUrl).then((manifest) => {
|
|
@@ -556,7 +585,7 @@ function Map2(props) {
|
|
|
556
585
|
() => mergeTileflowAnalytics(analytics, runtimeStyle?.analytics),
|
|
557
586
|
[analytics, runtimeStyle]
|
|
558
587
|
);
|
|
559
|
-
|
|
588
|
+
useEffect2(() => {
|
|
560
589
|
centerRef.current = resolvedCenter;
|
|
561
590
|
zoomRef.current = resolvedZoom;
|
|
562
591
|
bearingRef.current = resolvedBearing;
|
|
@@ -568,13 +597,13 @@ function Map2(props) {
|
|
|
568
597
|
zoom: resolvedZoom
|
|
569
598
|
});
|
|
570
599
|
}, [resolvedBearing, resolvedCenter, resolvedPitch, resolvedZoom]);
|
|
571
|
-
|
|
600
|
+
useEffect2(() => {
|
|
572
601
|
onLoadRef.current = onLoad;
|
|
573
602
|
}, [onLoad]);
|
|
574
|
-
|
|
603
|
+
useEffect2(() => {
|
|
575
604
|
mapNameRef.current = mapName;
|
|
576
605
|
}, [mapName]);
|
|
577
|
-
|
|
606
|
+
useEffect2(() => {
|
|
578
607
|
resolvedAnalyticsRef.current = resolvedAnalytics;
|
|
579
608
|
}, [resolvedAnalytics]);
|
|
580
609
|
const runtimeImageUrl = useMemo(
|
|
@@ -610,7 +639,7 @@ function Map2(props) {
|
|
|
610
639
|
})();
|
|
611
640
|
const currentCaptureState = activeRuntimeResource === (isImageMode ? runtimeImageUrl : runtimeStyle) ? captureState : "loading";
|
|
612
641
|
const effectiveCaptureState = currentCaptureState === "error" || interactionReadiness === "error" || runtimeResolutionState === "error" || themeTransitionState === "error" ? "error" : currentCaptureState === "idle" && interactionReadiness === "idle" && runtimeResolutionState === "idle" && themeTransitionState === "idle" ? "idle" : "loading";
|
|
613
|
-
|
|
642
|
+
useEffect2(() => {
|
|
614
643
|
if (!isImageMode || imageUrl || !containerRef.current) {
|
|
615
644
|
setImageSize(null);
|
|
616
645
|
return;
|
|
@@ -632,7 +661,7 @@ function Map2(props) {
|
|
|
632
661
|
resizeObserver.disconnect();
|
|
633
662
|
};
|
|
634
663
|
}, [imageUrl, isImageMode]);
|
|
635
|
-
|
|
664
|
+
useEffect2(() => {
|
|
636
665
|
const runtime = interactionRuntimeRef.current;
|
|
637
666
|
if (!runtime || !preparedAnnotations.ok) return;
|
|
638
667
|
try {
|
|
@@ -647,7 +676,7 @@ function Map2(props) {
|
|
|
647
676
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
648
677
|
}
|
|
649
678
|
}, [preparedAnnotations]);
|
|
650
|
-
|
|
679
|
+
useEffect2(() => {
|
|
651
680
|
const runtime = semanticRuntimeRef.current;
|
|
652
681
|
if (!runtime || !preparedInteractions.ok) return;
|
|
653
682
|
try {
|
|
@@ -662,7 +691,7 @@ function Map2(props) {
|
|
|
662
691
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
663
692
|
}
|
|
664
693
|
}, [preparedInteractions]);
|
|
665
|
-
|
|
694
|
+
useEffect2(() => {
|
|
666
695
|
if (!preparedInteractionState.controlled || !preparedInteractionState.ok) return;
|
|
667
696
|
try {
|
|
668
697
|
setStateBridgeDiagnostic(void 0);
|
|
@@ -677,7 +706,7 @@ function Map2(props) {
|
|
|
677
706
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
678
707
|
}
|
|
679
708
|
}, [preparedInteractionState]);
|
|
680
|
-
|
|
709
|
+
useEffect2(() => {
|
|
681
710
|
const annotationRuntime = interactionRuntimeRef.current;
|
|
682
711
|
const semanticRuntime = semanticRuntimeRef.current;
|
|
683
712
|
if (!annotationRuntime && !semanticRuntime) return;
|
|
@@ -701,7 +730,7 @@ function Map2(props) {
|
|
|
701
730
|
onInteractionDiagnosticRef.current?.(diagnostic);
|
|
702
731
|
}
|
|
703
732
|
}, [renderMarker, renderPopup, renderTooltip]);
|
|
704
|
-
|
|
733
|
+
useEffect2(() => {
|
|
705
734
|
setThemeTransitionState("idle");
|
|
706
735
|
const initialRuntimeStyle = runtimeStyleRef.current;
|
|
707
736
|
if (!containerRef.current || !initialRuntimeStyle) {
|
|
@@ -792,6 +821,7 @@ function Map2(props) {
|
|
|
792
821
|
setupResources.map = map;
|
|
793
822
|
registerCleanup(() => map.remove());
|
|
794
823
|
registerCleanup(attachTileflowAtmosphere(map));
|
|
824
|
+
registerCleanup(attachTileflowNotices(map));
|
|
795
825
|
const themeController = initialRuntimeStyle.theme === void 0 ? void 0 : createTileflowThemeController({
|
|
796
826
|
initial: initialRuntimeStyle,
|
|
797
827
|
map,
|
|
@@ -1038,7 +1068,7 @@ function Map2(props) {
|
|
|
1038
1068
|
disposeMap?.();
|
|
1039
1069
|
};
|
|
1040
1070
|
}, [resolvedInteractive, runtimeMapIdentity, runtimeStyleReady, stableMapOptions]);
|
|
1041
|
-
|
|
1071
|
+
useEffect2(() => {
|
|
1042
1072
|
const controller = themeControllerRef.current;
|
|
1043
1073
|
if (!controller || themeBlendKey) return;
|
|
1044
1074
|
if (themeResolution.error) {
|
|
@@ -1054,7 +1084,7 @@ function Map2(props) {
|
|
|
1054
1084
|
}
|
|
1055
1085
|
});
|
|
1056
1086
|
}, [runtimeStyle, themeBlendKey, themeResolution.error]);
|
|
1057
|
-
|
|
1087
|
+
useEffect2(() => {
|
|
1058
1088
|
const controller = themeControllerRef.current;
|
|
1059
1089
|
if (!controller || !blendStyles || themeBlendPosition === void 0) return;
|
|
1060
1090
|
if (runtimeStyle) setActiveRuntimeResource(runtimeStyle);
|
|
@@ -1278,11 +1308,11 @@ function useTileflowPortalReadiness(portalKeys) {
|
|
|
1278
1308
|
expectedKeysRef.current = { keys: [...portalKeys].sort(), token };
|
|
1279
1309
|
}
|
|
1280
1310
|
const expectedKeys = expectedKeysRef.current.keys;
|
|
1281
|
-
const [committed, setCommitted] =
|
|
1311
|
+
const [committed, setCommitted] = useState2(() => ({
|
|
1282
1312
|
keys: /* @__PURE__ */ new Set(),
|
|
1283
1313
|
token
|
|
1284
1314
|
}));
|
|
1285
|
-
const [readyToken, setReadyToken] =
|
|
1315
|
+
const [readyToken, setReadyToken] = useState2(token);
|
|
1286
1316
|
const readinessRunRef = useRef(0);
|
|
1287
1317
|
const markCommitted = useCallback((committedToken, portalKey) => {
|
|
1288
1318
|
setCommitted((current) => {
|
|
@@ -1412,5 +1442,6 @@ function isPlainObject(value) {
|
|
|
1412
1442
|
}
|
|
1413
1443
|
export {
|
|
1414
1444
|
Map2 as Map,
|
|
1415
|
-
configureTileflowMapLibre
|
|
1445
|
+
configureTileflowMapLibre,
|
|
1446
|
+
useBoundaryFeatureState
|
|
1416
1447
|
};
|
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.32",
|
|
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.37 <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
|
},
|