@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 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
- useEffect(() => {
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] = useState({
370
+ const [manifestResolution, setManifestResolution] = useState2({
342
371
  key: "",
343
372
  map: null,
344
373
  state: "loading"
345
374
  });
346
- const [systemColorScheme, setSystemColorScheme] = useState("light");
347
- const [imageSize, setImageSize] = useState(null);
348
- const [captureState, setCaptureState] = useState("loading");
349
- const [themeTransitionState, setThemeTransitionState] = useState(
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] = useState(void 0);
353
- const [annotationRenderTargets, setAnnotationRenderTargets] = useState([]);
354
- const [semanticRenderTargets, setSemanticRenderTargets] = useState([]);
355
- const [annotationBridgeDiagnostic, setAnnotationBridgeDiagnostic] = useState();
356
- const [semanticBridgeDiagnostic, setSemanticBridgeDiagnostic] = useState();
357
- const [stateBridgeDiagnostic, setStateBridgeDiagnostic] = useState();
358
- const [rendererBridgeDiagnostic, setRendererBridgeDiagnostic] = useState();
359
- const [annotationDiagnostics, setAnnotationDiagnostics] = useState([]);
360
- const [semanticDiagnostics, setSemanticDiagnostics] = useState([]);
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
- useEffect(() => {
516
+ useEffect2(() => {
488
517
  if (theme !== "system") return;
489
518
  setSystemColorScheme(getTileflowSystemColorScheme());
490
519
  return subscribeTileflowSystemColorScheme(setSystemColorScheme);
491
520
  }, [theme]);
492
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
600
+ useEffect2(() => {
572
601
  onLoadRef.current = onLoad;
573
602
  }, [onLoad]);
574
- useEffect(() => {
603
+ useEffect2(() => {
575
604
  mapNameRef.current = mapName;
576
605
  }, [mapName]);
577
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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] = useState(() => ({
1311
+ const [committed, setCommitted] = useState2(() => ({
1282
1312
  keys: /* @__PURE__ */ new Set(),
1283
1313
  token
1284
1314
  }));
1285
- const [readyToken, setReadyToken] = useState(token);
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.30",
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.35 <0.1.0-beta.0",
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
  },