@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 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
- useEffect(() => {
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] = useState({
369
+ const [manifestResolution, setManifestResolution] = useState2({
342
370
  key: "",
343
371
  map: null,
344
372
  state: "loading"
345
373
  });
346
- const [systemColorScheme, setSystemColorScheme] = useState("light");
347
- const [imageSize, setImageSize] = useState(null);
348
- const [captureState, setCaptureState] = useState("loading");
349
- const [themeTransitionState, setThemeTransitionState] = useState(
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] = 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([]);
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
- useEffect(() => {
515
+ useEffect2(() => {
488
516
  if (theme !== "system") return;
489
517
  setSystemColorScheme(getTileflowSystemColorScheme());
490
518
  return subscribeTileflowSystemColorScheme(setSystemColorScheme);
491
519
  }, [theme]);
492
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
599
+ useEffect2(() => {
572
600
  onLoadRef.current = onLoad;
573
601
  }, [onLoad]);
574
- useEffect(() => {
602
+ useEffect2(() => {
575
603
  mapNameRef.current = mapName;
576
604
  }, [mapName]);
577
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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
- useEffect(() => {
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] = useState(() => ({
1309
+ const [committed, setCommitted] = useState2(() => ({
1282
1310
  keys: /* @__PURE__ */ new Set(),
1283
1311
  token
1284
1312
  }));
1285
- const [readyToken, setReadyToken] = useState(token);
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.30",
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.35 <0.1.0-beta.0",
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
  },