@orioro/react-maplibre-util 0.8.1 → 0.9.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @orioro/react-maplibre-util
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 793bce0: add support for non-reactive <Source /> and <Layer /> props through force-remount via `key` prop
8
+ - d8d6b68: implement feature-state handling on <Source /> component and expose memoizee options on makeMemoFetch
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies [d8d6b68]
13
+ - @orioro/vector-tile-util@0.4.0
14
+
3
15
  ## 0.8.1
4
16
 
5
17
  ### Patch Changes
@@ -1,3 +1,3 @@
1
1
  export * from './LayeredMap';
2
2
  export * from './parseMapViews';
3
- export * from './layeredMapOnClickHandler';
3
+ export * from './layeredMapMouseEventHandler';
@@ -0,0 +1,24 @@
1
+ import type { MapGeoJSONFeature, MapMouseEvent } from 'maplibre-gl';
2
+ import { Merge } from 'type-fest';
3
+ type MouseEventHandlerFn = (feature: MapGeoJSONFeature, event: AugmentedMouseEvent, context: Record<string, any>) => any;
4
+ type LayeredMouseInteractiveFeature = Merge<MapGeoJSONFeature, {
5
+ layer: {
6
+ id: string;
7
+ onClick: MouseEventHandlerFn;
8
+ onMouseMove: MouseEventHandlerFn;
9
+ };
10
+ }>;
11
+ type AugmentedMouseEvent = Merge<MapMouseEvent, {
12
+ features: LayeredMouseInteractiveFeature[];
13
+ }>;
14
+ type LayeredMapMouseEventHandlerName = 'onClick' | 'onMouseMove';
15
+ type LayeredMapMouseEventHandlerProps = {
16
+ resolveTargetFeature?: (features: AugmentedMouseEvent['features'], event: AugmentedMouseEvent) => LayeredMouseInteractiveFeature | Promise<LayeredMouseInteractiveFeature>;
17
+ context?: Record<string, any>;
18
+ };
19
+ type LayeredMapEventHandler = (e: AugmentedMouseEvent) => any;
20
+ type LayeredMapEventHandlerList = Record<LayeredMapMouseEventHandlerName, LayeredMapEventHandler>;
21
+ export declare function layeredMapMouseEventHandler(handlerName: LayeredMapMouseEventHandlerName, props?: LayeredMapMouseEventHandlerProps): LayeredMapEventHandler;
22
+ export declare function layeredMapMouseEventHandler(handlerName: LayeredMapMouseEventHandlerName[], props?: LayeredMapMouseEventHandlerProps): LayeredMapEventHandlerList;
23
+ export declare function layeredMapOnClickHandler(props?: LayeredMapMouseEventHandlerProps): LayeredMapEventHandler;
24
+ export {};
@@ -9,6 +9,7 @@ type ParsedLayer = MapViewLayer & {
9
9
  id: string;
10
10
  viewId: string;
11
11
  onClick?: (feature: GeoJSON.Feature, event: MapMouseEvent) => any;
12
+ onMouseMove?: (feature: GeoJSON.Feature, event: MapMouseEvent) => any;
12
13
  };
13
14
  export type MapViewsParseResult = {
14
15
  srcMapViews: MapView[];
@@ -0,0 +1,14 @@
1
+ import React, { type ComponentProps } from 'react';
2
+ import { Source as MapGlSource } from 'react-map-gl/maplibre';
3
+ import { FeatureState } from '../useFeatureState';
4
+ type MapGlSourceProps = ComponentProps<typeof MapGlSource>;
5
+ export type SourceProps = MapGlSourceProps & {
6
+ id: string;
7
+ featureState: FeatureState;
8
+ };
9
+ /**
10
+ * Drop-in replacement for react-map-gl's <Source /> that also declaratively
11
+ * syncs MapLibre feature-state from props.
12
+ */
13
+ export declare function Source({ id, featureState, ...sourceProps }: SourceProps): React.JSX.Element;
14
+ export {};
@@ -0,0 +1 @@
1
+ export * from './Source';
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { MapProps, SourceProps } from 'react-map-gl/maplibre';
3
+ export declare const DEBUG_MAP_PROPS: MapProps;
4
+ export declare function DebugMap({ children, panel, ...props }: MapProps): React.JSX.Element;
5
+ export declare function sourceGeoJsonBr({ id, intrarregiao, ...props }?: {
6
+ id?: string;
7
+ intrarregiao?: 'municipio' | 'uf' | 'regiao' | null;
8
+ [key: string]: any;
9
+ }): SourceProps;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ type PanelPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
3
+ export declare const Panel: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
4
+ $position: PanelPosition;
5
+ }>> & string;
6
+ export declare function DebugPanel({ data, children, position, ...props }: {
7
+ data: any;
8
+ children?: React.ReactNode;
9
+ position?: PanelPosition;
10
+ }): React.JSX.Element;
11
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from './DebugPanel';
2
+ export * from './DebugMap';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Global interning table for H3 cell ids.
3
+ *
4
+ * Problem: every row of every API response carries full 15-char hex H3 ids.
5
+ * Parsing each into a BigInt (`h3ToBigInt`) allocates a heap object per row,
6
+ * and using bigint as a Map key is slow + heavy. But the *set* of distinct
7
+ * hexes in play (a city, at a fixed resolution) is small and highly
8
+ * recurring across mousemoves/tiles — so we intern each hex string to a
9
+ * small uint32 index exactly once, and every downstream structure
10
+ * (TileData, feature-state ids, caches) works with that index instead.
11
+ *
12
+ * This module is a singleton by design: it must persist for the lifetime
13
+ * of the app so the same hex always maps to the same index, and so the
14
+ * dedup benefit compounds across requests instead of resetting per fetch.
15
+ */
16
+ export declare function hexRegistry(): {
17
+ registerHex: (hex: string) => number;
18
+ hexFromIdx: (idx: number) => string;
19
+ idxFromHex: (hex: string) => number | undefined;
20
+ registrySize: () => number;
21
+ };
@@ -0,0 +1 @@
1
+ export * from './hexRegistry';
package/dist/index.d.ts CHANGED
@@ -8,3 +8,6 @@ export * from './scales';
8
8
  export * from './util';
9
9
  export * from './types';
10
10
  export * from './Controls';
11
+ export * from './Source';
12
+ export * from './useFeatureState';
13
+ export * from './h3';
package/dist/index.mjs CHANGED
@@ -1,18 +1,17 @@
1
- import { __assign, __spreadArray, __rest, __awaiter, __generator, __makeTemplateObject } from 'tslib';
2
- import React, { forwardRef, useContext, createContext, useRef, useMemo, useImperativeHandle, useEffect, useState, useCallback, useLayoutEffect, createRef } from 'react';
3
- import { Map, Source, Layer, useMap, useControl } from 'react-map-gl/maplibre';
1
+ import { __assign, __rest, __spreadArray, __awaiter, __generator, __makeTemplateObject } from 'tslib';
2
+ import React, { useRef, useEffect, forwardRef, useContext, createContext, useMemo, useImperativeHandle, useState, useCallback, useLayoutEffect, createRef } from 'react';
3
+ import { useMap, Source as Source$1, Map as Map$1, Layer, useControl } from 'react-map-gl/maplibre';
4
+ import { bbox } from '@turf/turf';
4
5
  import { isPlainObject, uniq, uniqBy, isEqual, pick, omit } from 'lodash-es';
5
6
  import { Flex, useRefByKey, useLocalState, DropdownMenu } from '@orioro/react-ui-core';
6
7
  import styled from 'styled-components';
7
8
  import { usePrevious } from 'react-use';
8
9
  import { mergeRefs } from 'react-merge-refs';
9
- import { jsxs } from 'react/jsx-runtime';
10
10
  import { mdiCloseCircleOutline, mdiCheck, mdiTerrain, mdiVideo3d } from '@mdi/js';
11
11
  import { interpolate, strExpr } from '@orioro/util';
12
12
  import { ckmeans } from 'simple-statistics';
13
13
  import { schemeYlOrRd } from 'd3-scale-chromatic';
14
14
  import { maxIndex, range, variance, sum } from 'd3';
15
- import { bbox } from '@turf/turf';
16
15
  import { createPortal } from 'react-dom';
17
16
  import { Icon } from '@mdi/react';
18
17
  import { Tooltip } from '@radix-ui/themes';
@@ -20,6 +19,244 @@ import maplibregl from 'maplibre-gl';
20
19
  import mlcontour from 'maplibre-contour';
21
20
  import MaplibreInspect from '@maplibre/maplibre-gl-inspect';
22
21
 
22
+ function _resolveState(map, sourceId, _a) {
23
+ var sourceLayer = _a.sourceLayer,
24
+ stateById = _a.stateById,
25
+ stateByQuery = _a.stateByQuery;
26
+ var result = {};
27
+ for (var _i = 0, _b = stateByQuery !== null && stateByQuery !== void 0 ? stateByQuery : []; _i < _b.length; _i++) {
28
+ var spec = _b[_i];
29
+ var features = map.querySourceFeatures(sourceId, {
30
+ sourceLayer: sourceLayer,
31
+ filter: spec.filter
32
+ });
33
+ for (var _c = 0, features_1 = features; _c < features_1.length; _c++) {
34
+ var f = features_1[_c];
35
+ if (f.id === undefined) continue;
36
+ // result[f.id] may be undefined here — spreading undefined is a no-op
37
+ // ({ ...undefined, ...x } === { ...x }), so this is safe as-is.
38
+ result[f.id] = __assign(__assign({}, result[f.id]), spec.state);
39
+ }
40
+ }
41
+ for (var id in stateById) {
42
+ result[id] = __assign(__assign({}, result[id]), stateById[id]);
43
+ }
44
+ return result;
45
+ }
46
+ function useFeatureState(featureStateBySourceId) {
47
+ var _a;
48
+ if (featureStateBySourceId === void 0) {
49
+ featureStateBySourceId = {};
50
+ }
51
+ var mapRef = useMap();
52
+ var map = (_a = mapRef.current) === null || _a === void 0 ? void 0 : _a.getMap();
53
+ var prevAppliedStateBySourceId = useRef({});
54
+ useEffect(function () {
55
+ if (!map) {
56
+ return;
57
+ }
58
+ var appliedOnceBySourceId = {};
59
+ function _applySourceFeatureState(sourceId) {
60
+ var _a;
61
+ var featureState = featureStateBySourceId[sourceId];
62
+ var _map = map;
63
+ if (!_map.getSource(sourceId)) {
64
+ return false;
65
+ }
66
+ var _nextSourceAppliedState = _resolveState(_map, sourceId, featureState);
67
+ var _prevSourceAppliedState = prevAppliedStateBySourceId.current[sourceId] || {};
68
+ //
69
+ // TODO: maybe add batching + requestAnimationFrame
70
+ //
71
+ for (var id in _nextSourceAppliedState) {
72
+ _map.setFeatureState({
73
+ source: sourceId,
74
+ sourceLayer: featureState.sourceLayer,
75
+ id: id
76
+ }, _nextSourceAppliedState[id]);
77
+ }
78
+ for (var id in _prevSourceAppliedState) {
79
+ if (!(id in _nextSourceAppliedState)) {
80
+ _map.removeFeatureState({
81
+ source: sourceId,
82
+ sourceLayer: featureState.sourceLayer,
83
+ id: id
84
+ });
85
+ }
86
+ }
87
+ prevAppliedStateBySourceId.current = __assign(__assign({}, prevAppliedStateBySourceId.current), (_a = {}, _a[sourceId] = _nextSourceAppliedState, _a));
88
+ return true;
89
+ }
90
+ appliedOnceBySourceId = Object.fromEntries(Object.keys(featureStateBySourceId).map(function (sourceId) {
91
+ return [sourceId, _applySourceFeatureState(sourceId)];
92
+ }));
93
+ // Re-apply on sourcedata for two distinct reasons:
94
+ // 1. Initial retry: if the source wasn't registered yet on mount (async
95
+ // relative to this effect), keep trying until it succeeds — this
96
+ // matters even for stateById-only usage, not just stateByQuery.
97
+ // 2. Ongoing re-resolution: stateByQuery results only reflect currently
98
+ // loaded tiles, so once applied at least once, keep re-resolving as
99
+ // more tiles load — this part only matters when queries are in use.
100
+ function onSourceData(e) {
101
+ var _a;
102
+ var _b;
103
+ if (!(e.sourceId in featureStateBySourceId)) {
104
+ //
105
+ // No feature state specified for the given source
106
+ //
107
+ return;
108
+ }
109
+ if (!appliedOnceBySourceId[e.sourceId]) {
110
+ appliedOnceBySourceId = __assign(__assign({}, appliedOnceBySourceId), (_a = {}, _a[e.sourceId] = _applySourceFeatureState(e.sourceId), _a));
111
+ } else if ((_b = featureStateBySourceId[e.sourceId].stateByQuery) === null || _b === void 0 ? void 0 : _b.length) {
112
+ _applySourceFeatureState(e.sourceId);
113
+ }
114
+ }
115
+ map.on('sourcedata', onSourceData);
116
+ return function () {
117
+ map.off('sourcedata', onSourceData);
118
+ };
119
+ }, [map, featureStateBySourceId]);
120
+ }
121
+
122
+ var DEFAULT_OPTIONS = {
123
+ padding: {
124
+ top: 60,
125
+ bottom: 60,
126
+ left: 60,
127
+ right: 60
128
+ }
129
+ };
130
+ function fitGeometry(map, geo, options) {
131
+ if (options === void 0) {
132
+ options = DEFAULT_OPTIONS;
133
+ }
134
+ var bounds = bbox(geo);
135
+ return map.fitBounds([[bounds[0], bounds[1]], [bounds[2], bounds[3]]], __assign(__assign({}, DEFAULT_OPTIONS), options));
136
+ }
137
+
138
+ //
139
+ // Taken from react-map-gl/maplibre
140
+ // https://github.com/visgl/react-map-gl/blob/c7112cf50d6985e8427d6b187d23a4d957791bb7/modules/react-maplibre/src/utils/apply-react-style.ts
141
+ //
142
+ // This is a simplified version of
143
+ // https://github.com/facebook/react/blob/4131af3e4bf52f3a003537ec95a1655147c81270/src/renderers/dom/shared/CSSPropertyOperations.js#L62
144
+ var unitlessNumber = /box|flex|grid|column|lineHeight|fontWeight|opacity|order|tabSize|zIndex/;
145
+ function applyReactStyle(element, styles) {
146
+ if (!element || !styles) {
147
+ return;
148
+ }
149
+ var style = element.style;
150
+ for (var key in styles) {
151
+ var value = styles[key];
152
+ if (Number.isFinite(value) && !unitlessNumber.test(key)) {
153
+ style[key] = "".concat(value, "px");
154
+ } else {
155
+ style[key] = value;
156
+ }
157
+ }
158
+ }
159
+
160
+ function ensureAddLayer(map, layerId, layer) {
161
+ if (!map.getLayer(layerId)) {
162
+ map.addLayer(__assign(__assign({}, layer), {
163
+ id: layerId
164
+ }));
165
+ }
166
+ }
167
+ function ensureRemoveLayer(map, layerId) {
168
+ if (map.getLayer(layerId)) {
169
+ map.removeLayer(layerId);
170
+ }
171
+ }
172
+ function ensureAddSource(map, sourceId, sourceSpec) {
173
+ if (!map.getSource(sourceId)) {
174
+ map.addSource(sourceId, sourceSpec);
175
+ }
176
+ }
177
+ function ensureRemoveSource(map, sourceId) {
178
+ if (map.getSource(sourceId)) {
179
+ map.removeSource(sourceId);
180
+ }
181
+ }
182
+
183
+ // Props react-map-gl's updateSource() cannot push to the underlying
184
+ // MapLibre/Mapbox source after creation, per source type.
185
+ // Keep this in sync with react-map-gl's source.ts — as it gains
186
+ // reactive support for more props (e.g. tiles/url did in 7.1.8),
187
+ // trim the corresponding entries here.
188
+ //
189
+ // See location of <Source /> component src code:
190
+ // https://github.com/visgl/react-map-gl/blob/4b649aaf926adacb3ffba4b7c5d8edebaca90f8a/modules/react-maplibre/src/components/source.ts#L79
191
+ //
192
+ var NON_REACTIVE_PROPS_BY_TYPE = {
193
+ vector: ['bounds', 'scheme', 'minzoom', 'maxzoom', 'attribution', 'promoteId', 'volatile'],
194
+ raster: ['bounds', 'minzoom', 'maxzoom', 'tileSize', 'scheme', 'attribution', 'volatile'],
195
+ 'raster-dem': ['bounds', 'tileSize', 'minzoom', 'maxzoom', 'encoding', 'attribution'],
196
+ geojson: ['cluster', 'clusterRadius', 'clusterMaxZoom', 'clusterMinPoints', 'clusterProperties', 'maxzoom', 'attribution', 'buffer', 'tolerance', 'lineMetrics', 'generateId', 'promoteId', 'filter'],
197
+ video: ['urls'],
198
+ canvas: ['canvas', 'animate']
199
+ // image has no non-reactive props — omitted
200
+ };
201
+ function getSourceRemountKey(id, source) {
202
+ var _a, _b;
203
+ var nonReactiveKeys = (_b = NON_REACTIVE_PROPS_BY_TYPE[(_a = source.type) !== null && _a !== void 0 ? _a : '']) !== null && _b !== void 0 ? _b : [];
204
+ if (nonReactiveKeys.length === 0) return id;
205
+ var fingerprint = nonReactiveKeys.map(function (key) {
206
+ return typeof source[key] !== 'undefined' ? "".concat(key, ":").concat(JSON.stringify(source[key])) : null;
207
+ }).filter(Boolean).join('|');
208
+ return "".concat(id, ":").concat(fingerprint);
209
+ }
210
+
211
+ /**
212
+ * Derives a React `key` for a <Layer> that changes whenever a
213
+ * non-reactive prop changes, forcing a remount instead of a silently
214
+ * dropped update.
215
+ *
216
+ * react-map-gl only pushes `paint`, `layout`, `filter`,
217
+ * `minzoom`/`maxzoom`, and `beforeId` to the map after mount (via
218
+ * real setters like `setPaintProperty`). `type`, `source`, and
219
+ * `source-layer` have no such setters — changing them is a no-op
220
+ * unless the layer is removed and re-added, i.e. remounted.
221
+ *
222
+ * <Layer key={getLayerRemountKey(id, layer)} id={id} {...layer} />
223
+ *
224
+ * https://github.com/visgl/react-map-gl/blob/4b649aaf926adacb3ffba4b7c5d8edebaca90f8a/modules/react-maplibre/src/components/layer.ts#L20
225
+ */
226
+ function getLayerRemountKey(id, layer) {
227
+ var _a;
228
+ return "".concat(id, ":").concat(layer.type, ":").concat(layer.source, ":").concat((_a = layer['source-layer']) !== null && _a !== void 0 ? _a : '');
229
+ }
230
+
231
+ /**
232
+ * Drop-in replacement for react-map-gl's <Source /> that also declaratively
233
+ * syncs MapLibre feature-state from props.
234
+ */
235
+ function Source(_a) {
236
+ var _b;
237
+ var id = _a.id,
238
+ featureState = _a.featureState,
239
+ sourceProps = __rest(_a, ["id", "featureState"]);
240
+ useFeatureState(featureState ? (_b = {}, _b[id] = featureState, _b) : {});
241
+ return /*#__PURE__*/React.createElement(Source$1
242
+ //
243
+ // Use `getSourceRemountKey` to ensure that
244
+ // non-reactive props (props that react-map-gl as no
245
+ // way of updating on maplibre) force re-mount
246
+ // of component
247
+ //
248
+ , __assign({
249
+ //
250
+ // Use `getSourceRemountKey` to ensure that
251
+ // non-reactive props (props that react-map-gl as no
252
+ // way of updating on maplibre) force re-mount
253
+ // of component
254
+ //
255
+ key: getSourceRemountKey(id, sourceProps),
256
+ id: id
257
+ }, sourceProps));
258
+ }
259
+
23
260
  function _validZIndex(zIndex) {
24
261
  return typeof zIndex === 'number' && !Number.isNaN(zIndex);
25
262
  }
@@ -279,7 +516,6 @@ function syncLayerOrder(_a) {
279
516
  });
280
517
  }
281
518
 
282
- // import { mergeRefs } from 'react-merge-refs'
283
519
  //
284
520
  // Augment mouse events with info from original view
285
521
  //
@@ -367,7 +603,7 @@ var LayeredMap = /*#__PURE__*/forwardRef(function LayeredMapInner(_a, layeredMap
367
603
  clearTimeout(timeoutId);
368
604
  };
369
605
  }, [parsed === null || parsed === void 0 ? void 0 : parsed.layers]);
370
- return /*#__PURE__*/React.createElement(Map, __assign({
606
+ return /*#__PURE__*/React.createElement(Map$1, __assign({
371
607
  ref: mapRef,
372
608
  interactiveLayerIds: __spreadArray(__spreadArray([], interactiveLayerIdsInput, true), parsed.interactiveLayerIds, true)
373
609
  }, mapProps, evtHandlers), /*#__PURE__*/React.createElement(LayeredMapContext.Provider, {
@@ -377,58 +613,85 @@ var LayeredMap = /*#__PURE__*/forwardRef(function LayeredMapInner(_a, layeredMap
377
613
  _a.viewId;
378
614
  var source = __rest(_a, ["id", "viewId"]);
379
615
  return /*#__PURE__*/React.createElement(Source, __assign({
380
- key: id,
381
616
  id: id
382
617
  }, source));
383
618
  }), parsed.layers.map(function (_a) {
384
619
  var id = _a.id,
385
620
  layer = __rest(_a, ["id"]);
386
- return /*#__PURE__*/React.createElement(Layer, __assign({
387
- key: id,
621
+ return /*#__PURE__*/React.createElement(Layer
622
+ //
623
+ // Use `getLayerRemountKey` to ensure that
624
+ // non-reactive props (props that react-map-gl as no
625
+ // way of updating on maplibre) force re-mount
626
+ // of component
627
+ //
628
+ , __assign({
629
+ //
630
+ // Use `getLayerRemountKey` to ensure that
631
+ // non-reactive props (props that react-map-gl as no
632
+ // way of updating on maplibre) force re-mount
633
+ // of component
634
+ //
635
+ key: getLayerRemountKey(id, layer),
388
636
  id: id
389
637
  }, layer));
390
638
  })));
391
639
  });
392
640
 
393
- function selectFirstClickableFeature(features) {
641
+ function selectFirstInteractiveFeature(features) {
394
642
  return features[0];
395
643
  }
396
- function layeredMapOnClickHandler(_a) {
397
- var _b = _a === void 0 ? {} : _a,
398
- _c = _b.resolveTargetFeature,
399
- resolveTargetFeature = _c === void 0 ? selectFirstClickableFeature : _c,
400
- _d = _b.context,
401
- context = _d === void 0 ? {} : _d;
402
- return function onClick(e) {
403
- return __awaiter(this, void 0, void 0, function () {
404
- var features, clickableFeatures, targetFeature, _a;
405
- return __generator(this, function (_b) {
406
- switch (_b.label) {
407
- case 0:
408
- features = e.features || [];
409
- clickableFeatures = features.filter(function (feature) {
410
- var _a;
411
- return typeof ((_a = feature.layer) === null || _a === void 0 ? void 0 : _a.onClick) === 'function';
412
- });
413
- if (!(clickableFeatures.length > 0)) return [3 /*break*/, 4];
414
- if (!(clickableFeatures.length === 1)) return [3 /*break*/, 1];
415
- _a = clickableFeatures[0];
416
- return [3 /*break*/, 3];
417
- case 1:
418
- return [4 /*yield*/, resolveTargetFeature(clickableFeatures, e)];
419
- case 2:
420
- _a = _b.sent();
421
- _b.label = 3;
422
- case 3:
423
- targetFeature = _a;
424
- targetFeature.layer.onClick(targetFeature, e, context);
425
- _b.label = 4;
426
- case 4:
427
- return [2 /*return*/];
428
- }
644
+ function layeredMapMouseEventHandler(handlerName, props) {
645
+ if (props === void 0) {
646
+ props = {};
647
+ }
648
+ if (Array.isArray(handlerName)) {
649
+ return Object.fromEntries(handlerName.map(function (_handlerName) {
650
+ return [_handlerName, layeredMapMouseEventHandler(_handlerName, props)];
651
+ }));
652
+ } else {
653
+ var _a = props.resolveTargetFeature,
654
+ resolveTargetFeature_1 = _a === void 0 ? selectFirstInteractiveFeature : _a,
655
+ _b = props.context,
656
+ context_1 = _b === void 0 ? {} : _b;
657
+ return function layeredMapOnHandleMouseEvent(e) {
658
+ return __awaiter(this, void 0, void 0, function () {
659
+ var features, taregetableFeatures, targetFeature, _a;
660
+ return __generator(this, function (_b) {
661
+ switch (_b.label) {
662
+ case 0:
663
+ features = e.features || [];
664
+ taregetableFeatures = features.filter(function (feature) {
665
+ var _a;
666
+ return typeof ((_a = feature.layer) === null || _a === void 0 ? void 0 : _a[handlerName]) === 'function';
667
+ });
668
+ if (!(taregetableFeatures.length > 0)) return [3 /*break*/, 4];
669
+ if (!(taregetableFeatures.length === 1)) return [3 /*break*/, 1];
670
+ _a = taregetableFeatures[0];
671
+ return [3 /*break*/, 3];
672
+ case 1:
673
+ return [4 /*yield*/, resolveTargetFeature_1(taregetableFeatures, e)];
674
+ case 2:
675
+ _a = _b.sent();
676
+ _b.label = 3;
677
+ case 3:
678
+ targetFeature = _a;
679
+ targetFeature.layer[handlerName](targetFeature, e, context_1);
680
+ _b.label = 4;
681
+ case 4:
682
+ return [2 /*return*/];
683
+ }
684
+ });
429
685
  });
430
- });
431
- };
686
+ };
687
+ }
688
+ }
689
+ function layeredMapOnClickHandler(props) {
690
+ if (props === void 0) {
691
+ props = {};
692
+ }
693
+ console.warn('layeredMapOnClickHandler deprecated, prefer layeredMapMouseEventHandler(`onClick`, props)');
694
+ return layeredMapMouseEventHandler('onClick', props);
432
695
  }
433
696
 
434
697
  var Container = styled.div(templateObject_1$2 || (templateObject_1$2 = __makeTemplateObject(["\n pointer-events: none;\n position: absolute;\n z-index: 2;\n\n background: rgba(0, 0, 0, 0.5);\n border-radius: 16px;\n box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);\n backdrop-filter: blur(5px);\n -webkit-backdrop-filter: blur(5px);\n border: 1px solid rgba(0, 0, 0, 0.3);\n\n // background-color: black;\n color: white;\n border-radius: 5px;\n font-size: 0.9rem;\n\n max-width: 300px;\n\n hyphens: auto;\n word-break: break-word; /* Avoids overflow */\n overflow-wrap: break-word; /* Ensures long words break */\n white-space: normal;\n"], ["\n pointer-events: none;\n position: absolute;\n z-index: 2;\n\n background: rgba(0, 0, 0, 0.5);\n border-radius: 16px;\n box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);\n backdrop-filter: blur(5px);\n -webkit-backdrop-filter: blur(5px);\n border: 1px solid rgba(0, 0, 0, 0.3);\n\n // background-color: black;\n color: white;\n border-radius: 5px;\n font-size: 0.9rem;\n\n max-width: 300px;\n\n hyphens: auto;\n word-break: break-word; /* Avoids overflow */\n overflow-wrap: break-word; /* Ensures long words break */\n white-space: normal;\n"])));
@@ -661,7 +924,7 @@ var MapWindow = /*#__PURE__*/forwardRef(function MapWindowInner(_a, externalRef)
661
924
  externalOnLoad(e);
662
925
  }
663
926
  }, [setCenterOffsetPixels, setMapReady]);
664
- return /*#__PURE__*/React.createElement(Map, __assign({
927
+ return /*#__PURE__*/React.createElement(Map$1, __assign({
665
928
  attributionControl: false
666
929
  }, mapProps, {
667
930
  onLoad: _onLoad,
@@ -767,160 +1030,6 @@ function useHover(props, deps) {
767
1030
  }, hoverInfo, isDragging];
768
1031
  }
769
1032
 
770
- function _arrayLikeToArray(r, a) {
771
- (null == a || a > r.length) && (a = r.length);
772
- for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
773
- return n;
774
- }
775
- function _arrayWithHoles(r) {
776
- if (Array.isArray(r)) return r;
777
- }
778
- function _defineProperty(e, r, t) {
779
- return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
780
- value: t,
781
- enumerable: true,
782
- configurable: true,
783
- writable: true
784
- }) : e[r] = t, e;
785
- }
786
- function _iterableToArrayLimit(r, l) {
787
- var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
788
- if (null != t) {
789
- var e,
790
- n,
791
- i,
792
- u,
793
- a = [],
794
- f = true,
795
- o = false;
796
- try {
797
- if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
798
- } catch (r) {
799
- o = true, n = r;
800
- } finally {
801
- try {
802
- if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
803
- } finally {
804
- if (o) throw n;
805
- }
806
- }
807
- return a;
808
- }
809
- }
810
- function _nonIterableRest() {
811
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
812
- }
813
- function ownKeys(e, r) {
814
- var t = Object.keys(e);
815
- if (Object.getOwnPropertySymbols) {
816
- var o = Object.getOwnPropertySymbols(e);
817
- r && (o = o.filter(function (r) {
818
- return Object.getOwnPropertyDescriptor(e, r).enumerable;
819
- })), t.push.apply(t, o);
820
- }
821
- return t;
822
- }
823
- function _objectSpread2(e) {
824
- for (var r = 1; r < arguments.length; r++) {
825
- var t = null != arguments[r] ? arguments[r] : {};
826
- r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
827
- _defineProperty(e, r, t[r]);
828
- }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
829
- Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
830
- });
831
- }
832
- return e;
833
- }
834
- function _objectWithoutProperties(e, t) {
835
- if (null == e) return {};
836
- var o,
837
- r,
838
- i = _objectWithoutPropertiesLoose(e, t);
839
- if (Object.getOwnPropertySymbols) {
840
- var n = Object.getOwnPropertySymbols(e);
841
- for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
842
- }
843
- return i;
844
- }
845
- function _objectWithoutPropertiesLoose(r, e) {
846
- if (null == r) return {};
847
- var t = {};
848
- for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
849
- if (-1 !== e.indexOf(n)) continue;
850
- t[n] = r[n];
851
- }
852
- return t;
853
- }
854
- function _slicedToArray(r, e) {
855
- return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
856
- }
857
- function _toPrimitive(t, r) {
858
- if ("object" != typeof t || !t) return t;
859
- var e = t[Symbol.toPrimitive];
860
- if (void 0 !== e) {
861
- var i = e.call(t, r);
862
- if ("object" != typeof i) return i;
863
- throw new TypeError("@@toPrimitive must return a primitive value.");
864
- }
865
- return ("string" === r ? String : Number)(t);
866
- }
867
- function _toPropertyKey(t) {
868
- var i = _toPrimitive(t, "string");
869
- return "symbol" == typeof i ? i : i + "";
870
- }
871
- function _typeof(o) {
872
- "@babel/helpers - typeof";
873
-
874
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
875
- return typeof o;
876
- } : function (o) {
877
- return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
878
- }, _typeof(o);
879
- }
880
- function _unsupportedIterableToArray(r, a) {
881
- if (r) {
882
- if ("string" == typeof r) return _arrayLikeToArray(r, a);
883
- var t = {}.toString.call(r).slice(8, -1);
884
- return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
885
- }
886
- }
887
-
888
- var _excluded = ["cursor", "onMouseMove", "onDragStart", "onDragEnd", "children"];
889
- function useMergedCallback(cbA, cbB) {
890
- return useCallback(function () {
891
- if (typeof cbA === 'function') {
892
- cbA.apply(void 0, arguments);
893
- }
894
- if (typeof cbB === 'function') {
895
- cbB.apply(void 0, arguments);
896
- }
897
- }, [cbA, cbB]);
898
- }
899
- function withHover(Component, withHoverProps) {
900
- return /*#__PURE__*/forwardRef(function WithHover(_ref, ref) {
901
- var cursor = _ref.cursor,
902
- onMouseMove = _ref.onMouseMove,
903
- onDragStart = _ref.onDragStart,
904
- onDragEnd = _ref.onDragEnd,
905
- children = _ref.children,
906
- restProps = _objectWithoutProperties(_ref, _excluded);
907
- //
908
- // Hover stuff
909
- //
910
- var _useHover = useHover(withHoverProps, []),
911
- _useHover2 = _slicedToArray(_useHover, 1),
912
- hoverProps = _useHover2[0];
913
- return /*#__PURE__*/jsxs(Component, _objectSpread2(_objectSpread2({}, restProps), {}, {
914
- ref: ref,
915
- cursor: cursor || hoverProps.cursor,
916
- onMouseMove: useMergedCallback(onMouseMove, hoverProps.onMouseMove),
917
- onDragStart: useMergedCallback(onDragStart, hoverProps.onDragStart),
918
- onDragEnd: useMergedCallback(onDragEnd, hoverProps.onDragEnd),
919
- children: [hoverProps.children, children]
920
- }));
921
- });
922
- }
923
-
924
1033
  function parseHoverInfo(index, event) {
925
1034
  return {
926
1035
  index: index,
@@ -1097,7 +1206,7 @@ function makeSyncedMaps(_a) {
1097
1206
  }
1098
1207
  var SyncedMaps = makeSyncedMaps({
1099
1208
  components: {
1100
- Map: Map
1209
+ Map: Map$1
1101
1210
  }
1102
1211
  });
1103
1212
  var templateObject_1;
@@ -1549,67 +1658,6 @@ var $naturalBreaks = function $naturalBreaks(_a) {
1549
1658
  return breaks;
1550
1659
  };
1551
1660
 
1552
- var DEFAULT_OPTIONS = {
1553
- padding: {
1554
- top: 60,
1555
- bottom: 60,
1556
- left: 60,
1557
- right: 60
1558
- }
1559
- };
1560
- function fitGeometry(map, geo, options) {
1561
- if (options === void 0) {
1562
- options = DEFAULT_OPTIONS;
1563
- }
1564
- var bounds = bbox(geo);
1565
- return map.fitBounds([[bounds[0], bounds[1]], [bounds[2], bounds[3]]], __assign(__assign({}, DEFAULT_OPTIONS), options));
1566
- }
1567
-
1568
- //
1569
- // Taken from react-map-gl/maplibre
1570
- // https://github.com/visgl/react-map-gl/blob/c7112cf50d6985e8427d6b187d23a4d957791bb7/modules/react-maplibre/src/utils/apply-react-style.ts
1571
- //
1572
- // This is a simplified version of
1573
- // https://github.com/facebook/react/blob/4131af3e4bf52f3a003537ec95a1655147c81270/src/renderers/dom/shared/CSSPropertyOperations.js#L62
1574
- var unitlessNumber = /box|flex|grid|column|lineHeight|fontWeight|opacity|order|tabSize|zIndex/;
1575
- function applyReactStyle(element, styles) {
1576
- if (!element || !styles) {
1577
- return;
1578
- }
1579
- var style = element.style;
1580
- for (var key in styles) {
1581
- var value = styles[key];
1582
- if (Number.isFinite(value) && !unitlessNumber.test(key)) {
1583
- style[key] = "".concat(value, "px");
1584
- } else {
1585
- style[key] = value;
1586
- }
1587
- }
1588
- }
1589
-
1590
- function ensureAddLayer(map, layerId, layer) {
1591
- if (!map.getLayer(layerId)) {
1592
- map.addLayer(__assign(__assign({}, layer), {
1593
- id: layerId
1594
- }));
1595
- }
1596
- }
1597
- function ensureRemoveLayer(map, layerId) {
1598
- if (map.getLayer(layerId)) {
1599
- map.removeLayer(layerId);
1600
- }
1601
- }
1602
- function ensureAddSource(map, sourceId, sourceSpec) {
1603
- if (!map.getSource(sourceId)) {
1604
- map.addSource(sourceId, sourceSpec);
1605
- }
1606
- }
1607
- function ensureRemoveSource(map, sourceId) {
1608
- if (map.getSource(sourceId)) {
1609
- map.removeSource(sourceId);
1610
- }
1611
- }
1612
-
1613
1661
  function ControlContainer(_a) {
1614
1662
  var style = _a.style,
1615
1663
  _b = _a.position,
@@ -1660,6 +1708,16 @@ function ControlContainerWithStyleReset(_a) {
1660
1708
  }
1661
1709
  ControlContainer.Unstyled = ControlContainerWithStyleReset;
1662
1710
 
1711
+ function _typeof(o) {
1712
+ "@babel/helpers - typeof";
1713
+
1714
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
1715
+ return typeof o;
1716
+ } : function (o) {
1717
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
1718
+ }, _typeof(o);
1719
+ }
1720
+
1663
1721
  var DEFAULT_DEM_SOURCE_ID = 'dem';
1664
1722
  var DEFAULT_DEM_SOURCE_SPEC = {
1665
1723
  url: 'https://elevation-tiles-prod.s3.amazonaws.com/terrarium/{z}/{x}/{y}.png',
@@ -1978,4 +2036,54 @@ function InspectControl(props) {
1978
2036
  return null;
1979
2037
  }
1980
2038
 
1981
- export { $naturalBreaks, ControlContainer, DynamicImages, HoverTooltip, InspectControl, LayeredMap, MapWindow, index as SVG_PATTERNS, SyncedMaps, TerrainControl, applyReactStyle, augmentFeature, circles_1, cross_1, diamonds_1, ensureAddLayer, ensureAddSource, ensureRemoveLayer, ensureRemoveSource, fitGeometry, fmtLayerAbsoluteId, getSrcLayer, getSrcViewByLayerId, hoverParseEvent, layeredMapOnClickHandler, lines_1, makeSyncedMaps, mapSetFeaturesState, mosaic_1, mosaic_2, naturalBreakBounds, parseMapViews, scaleNaturalBreaks, sortLayers, squares_1, svgIconGenerator, svgImageGenerator, svgImageId, triangles_1, useClientRect, useHover, useLayeredMap, useMapRegistry, useTilesLoading, waves_1, withHover };
2039
+ /**
2040
+ * Global interning table for H3 cell ids.
2041
+ *
2042
+ * Problem: every row of every API response carries full 15-char hex H3 ids.
2043
+ * Parsing each into a BigInt (`h3ToBigInt`) allocates a heap object per row,
2044
+ * and using bigint as a Map key is slow + heavy. But the *set* of distinct
2045
+ * hexes in play (a city, at a fixed resolution) is small and highly
2046
+ * recurring across mousemoves/tiles — so we intern each hex string to a
2047
+ * small uint32 index exactly once, and every downstream structure
2048
+ * (TileData, feature-state ids, caches) works with that index instead.
2049
+ *
2050
+ * This module is a singleton by design: it must persist for the lifetime
2051
+ * of the app so the same hex always maps to the same index, and so the
2052
+ * dedup benefit compounds across requests instead of resetting per fetch.
2053
+ */
2054
+ function hexRegistry() {
2055
+ var hexToIdx = new Map();
2056
+ var idxToHex = [];
2057
+ /** Intern a 15-char H3 hex string, returning a stable uint32 index. */
2058
+ var registerHex = function registerHex(hex) {
2059
+ var idx = hexToIdx.get(hex);
2060
+ if (idx === undefined) {
2061
+ idx = idxToHex.length;
2062
+ hexToIdx.set(hex, idx);
2063
+ idxToHex.push(hex);
2064
+ }
2065
+ return idx;
2066
+ };
2067
+ /** Reverse lookup: index -> original hex string (e.g. for feature ids). */
2068
+ var hexFromIdx = function hexFromIdx(idx) {
2069
+ var hex = idxToHex[idx];
2070
+ if (hex === undefined) {
2071
+ throw new Error("hexRegistry: no hex interned for index ".concat(idx));
2072
+ }
2073
+ return hex;
2074
+ };
2075
+ var idxFromHex = function idxFromHex(hex) {
2076
+ return hexToIdx.get(hex);
2077
+ };
2078
+ var registrySize = function registrySize() {
2079
+ return idxToHex.length;
2080
+ };
2081
+ return {
2082
+ registerHex: registerHex,
2083
+ hexFromIdx: hexFromIdx,
2084
+ idxFromHex: idxFromHex,
2085
+ registrySize: registrySize
2086
+ };
2087
+ }
2088
+
2089
+ export { $naturalBreaks, ControlContainer, DynamicImages, HoverTooltip, InspectControl, LayeredMap, MapWindow, index as SVG_PATTERNS, Source, SyncedMaps, TerrainControl, applyReactStyle, augmentFeature, circles_1, cross_1, diamonds_1, ensureAddLayer, ensureAddSource, ensureRemoveLayer, ensureRemoveSource, fitGeometry, fmtLayerAbsoluteId, getLayerRemountKey, getSourceRemountKey, getSrcLayer, getSrcViewByLayerId, hexRegistry, hoverParseEvent, layeredMapMouseEventHandler, layeredMapOnClickHandler, lines_1, makeSyncedMaps, mapSetFeaturesState, mosaic_1, mosaic_2, naturalBreakBounds, parseMapViews, scaleNaturalBreaks, sortLayers, squares_1, svgIconGenerator, svgImageGenerator, svgImageId, triangles_1, useClientRect, useFeatureState, useHover, useLayeredMap, useMapRegistry, useTilesLoading, waves_1 };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Converts an array of H3 cell indexes into a GeoJSON FeatureCollection
3
+ * of polygons.
4
+ */
5
+ export declare function h3CellsToGeoJSON(cells: any, getProperties?: () => {}): {
6
+ type: string;
7
+ features: ({
8
+ type: string;
9
+ id: any;
10
+ properties: {
11
+ h3: any;
12
+ };
13
+ geometry: {
14
+ type: string;
15
+ coordinates: import("h3-js").CoordPair[][];
16
+ };
17
+ } | {
18
+ type: string;
19
+ id: any;
20
+ properties: {
21
+ h3: any;
22
+ };
23
+ geometry: {
24
+ type: string;
25
+ coordinates: number[][][][];
26
+ };
27
+ })[];
28
+ };
29
+ export declare function resolutionForZoom(zoom: any): number;
30
+ /**
31
+ * Bounds -> H3 cell indexes covering that area at the given resolution.
32
+ * `paddingRatio` expands the box so cells don't pop in right at the
33
+ * viewport edge while panning.
34
+ */
35
+ export declare function boundsToH3Cells(bounds: any, resolution: any, paddingRatio?: number): string[];
36
+ /** Memoized GeoJSON conversion; see usage note on stable `cells` refs. */
37
+ export declare function useH3GeoJSON(cells: any, getProperties: any): {
38
+ type: string;
39
+ features: ({
40
+ type: string;
41
+ id: any;
42
+ properties: {
43
+ h3: any;
44
+ };
45
+ geometry: {
46
+ type: string;
47
+ coordinates: import("h3-js").CoordPair[][];
48
+ };
49
+ } | {
50
+ type: string;
51
+ id: any;
52
+ properties: {
53
+ h3: any;
54
+ };
55
+ geometry: {
56
+ type: string;
57
+ coordinates: number[][][][];
58
+ };
59
+ })[];
60
+ };
61
+ /**
62
+ * Tracks the map's viewport and returns the H3 cells currently covering
63
+ * it. Recomputes on `moveend` (debounced) rather than every pan frame —
64
+ * `polygonToCells` does real geometric coverage work and isn't cheap
65
+ * enough to run at 60fps during drag.
66
+ */
67
+ export declare function useViewportH3Cells({ mapId, getResolution, paddingRatio, debounceMs, }?: {
68
+ getResolution?: typeof resolutionForZoom | undefined;
69
+ paddingRatio?: number | undefined;
70
+ debounceMs?: number | undefined;
71
+ }): never[];
72
+ /**
73
+ * Drop-in hex layer. Two modes:
74
+ * - pass `cells` explicitly -> renders exactly that set (old behavior)
75
+ * - omit `cells` -> auto-tracks the viewport and renders whatever's
76
+ * visible, at a resolution derived from zoom
77
+ */
78
+ export declare function H3HexSource({ id, mapId, cells: cellsProp, getResolution, paddingRatio, getProperties, fillColor, fillOpacity, lineColor, lineWidth, beforeId, }: {
79
+ id?: string | undefined;
80
+ mapId: any;
81
+ cells: any;
82
+ getResolution: any;
83
+ paddingRatio: any;
84
+ getProperties: any;
85
+ fillColor?: string | undefined;
86
+ fillOpacity?: number | undefined;
87
+ lineColor?: string | undefined;
88
+ lineWidth?: number | undefined;
89
+ beforeId: any;
90
+ }): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import 'maplibre-gl/dist/maplibre-gl.css';
3
+ import '@radix-ui/themes/styles.css';
4
+ declare const _default: {
5
+ title: string;
6
+ parameters: {
7
+ layout: string;
8
+ };
9
+ };
10
+ export default _default;
11
+ export declare const Basic: () => React.JSX.Element;
package/dist/types.d.ts CHANGED
@@ -11,6 +11,7 @@ export type MapViewLayer = Omit<AnyLayer, 'id'> & {
11
11
  absoluteSourceId?: string;
12
12
  zIndex?: number;
13
13
  onClick?: (feature: MapGeoJSONFeature, event: MapMouseEvent) => any;
14
+ onMouseMove?: (feature: MapGeoJSONFeature, event: MapMouseEvent) => any;
14
15
  };
15
16
  type MapViewLegend = {
16
17
  type: string;
@@ -0,0 +1 @@
1
+ export * from './useFeatureState';
@@ -0,0 +1,17 @@
1
+ import type { FilterSpecification } from 'maplibre-gl';
2
+ type SourceId = string;
3
+ type FeatureId = string | number;
4
+ type FeatureStateValue = Record<string, unknown>;
5
+ export type QuerySpec = {
6
+ filter: FilterSpecification;
7
+ state: FeatureStateValue;
8
+ };
9
+ export type FeatureState = {
10
+ sourceLayer?: string;
11
+ featureIdType?: 'string' | 'number';
12
+ stateById?: Record<FeatureId, FeatureStateValue>;
13
+ stateByQuery?: QuerySpec[];
14
+ };
15
+ export type FeatureStateBySourceId = Record<SourceId, FeatureState>;
16
+ export declare function useFeatureState(featureStateBySourceId?: Record<SourceId, FeatureState>): void;
17
+ export {};
@@ -1,2 +1 @@
1
1
  export * from './useHover';
2
- export * from './withHover';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Derives a React `key` for a <Layer> that changes whenever a
3
+ * non-reactive prop changes, forcing a remount instead of a silently
4
+ * dropped update.
5
+ *
6
+ * react-map-gl only pushes `paint`, `layout`, `filter`,
7
+ * `minzoom`/`maxzoom`, and `beforeId` to the map after mount (via
8
+ * real setters like `setPaintProperty`). `type`, `source`, and
9
+ * `source-layer` have no such setters — changing them is a no-op
10
+ * unless the layer is removed and re-added, i.e. remounted.
11
+ *
12
+ * <Layer key={getLayerRemountKey(id, layer)} id={id} {...layer} />
13
+ *
14
+ * https://github.com/visgl/react-map-gl/blob/4b649aaf926adacb3ffba4b7c5d8edebaca90f8a/modules/react-maplibre/src/components/layer.ts#L20
15
+ */
16
+ export declare function getLayerRemountKey(id: string, layer: Record<string, unknown>): string;
@@ -0,0 +1,5 @@
1
+ type SourceLike = Record<string, unknown> & {
2
+ type?: string;
3
+ };
4
+ export declare function getSourceRemountKey(id: string, source: SourceLike): string;
5
+ export {};
@@ -1,3 +1,5 @@
1
1
  export * from './fitGeometry';
2
2
  export * from './applyReactStyle';
3
3
  export * from './misc';
4
+ export * from './getSourceRemountKey';
5
+ export * from './getLayerRemountKey';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orioro/react-maplibre-util",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "packageManager": "yarn@4.0.2",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -56,10 +56,12 @@
56
56
  "@orioro/resolve": "^0.1.12",
57
57
  "@orioro/scale-util": "^0.0.12",
58
58
  "@orioro/util": "^0.16.0",
59
+ "@orioro/vector-tile-util": "^0.4.0",
59
60
  "@turf/turf": "^7.2.0",
60
61
  "d3": "^7.9.0",
61
62
  "d3-scale-chromatic": "^3.1.0",
62
63
  "greenlet": "^1.1.0",
64
+ "h3-js": "^4.5.0",
63
65
  "lodash-es": "^4.17.21",
64
66
  "maplibre-contour": "^0.1.0",
65
67
  "query-string": "^9.1.1",
@@ -1,17 +0,0 @@
1
- import type { MapGeoJSONFeature, MapMouseEvent } from 'maplibre-gl';
2
- import { Merge } from 'type-fest';
3
- type ClickableFeature = Merge<MapGeoJSONFeature, {
4
- layer: {
5
- id: string;
6
- onClick: (feature: MapGeoJSONFeature, event: AugmentedMouseEvent, context: Record<string, any>) => any;
7
- };
8
- }>;
9
- type AugmentedMouseEvent = Merge<MapMouseEvent, {
10
- features: ClickableFeature[];
11
- }>;
12
- type LayeredMapOnClickHandlerProps = {
13
- resolveTargetFeature?: (features: AugmentedMouseEvent['features'], event: AugmentedMouseEvent) => ClickableFeature | Promise<ClickableFeature>;
14
- context?: Record<string, any>;
15
- };
16
- export declare function layeredMapOnClickHandler({ resolveTargetFeature, context, }?: LayeredMapOnClickHandlerProps): (e: AugmentedMouseEvent) => Promise<void>;
17
- export {};
@@ -1,2 +0,0 @@
1
- export function withHover(Component: any, withHoverProps: any): React.ForwardRefExoticComponent<React.RefAttributes<any>>;
2
- import React from 'react';