@tileflow/react-native 0.1.0-alpha.8 → 0.1.0-alpha.9
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 +8 -1
- package/dist/{contract-BWT1jxqg.d.ts → contract-BWCbfMOx.d.ts} +9 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +387 -51
- package/dist/internal/camera.d.ts +1 -1
- package/dist/internal/initial-view.d.ts +1 -1
- package/dist/internal/interactions.js +1 -0
- package/dist/internal/native-appearance.d.ts +1 -1
- package/dist/internal/source-state.d.ts +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ Add `timeOfDay="dusk"` beside `theme` to select a map's optional `day`, `dawn`,
|
|
|
91
91
|
`night` preset. For Streets, `theme="light"` with this preset selects the existing `dusk` palette.
|
|
92
92
|
Maps without this capability ignore the option; omitting it keeps the selected theme unchanged.
|
|
93
93
|
Changing it uses the existing theme transition. `themeBlend` takes precedence. See the
|
|
94
|
-
[authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/
|
|
94
|
+
[authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/REFERENCE.md#style-with-themes-and-semantic-modules).
|
|
95
95
|
|
|
96
96
|
Theme and `timeOfDay` changes cross-fade over 450 ms by default: a snapshot of the drawn map covers it, follows the camera and fades out once the new theme is drawn. Use `themeTransition={{duration: 700}}` to customize the duration, or `duration: 0` for an immediate change. `themeBlend={{themes: ['night', 'dusk', 'day'], position}}` shows a continuous blend of the map's themes instead of `theme`; position 0 is the first theme and 1.5 is halfway from the second to the third:
|
|
97
97
|
|
|
@@ -275,6 +275,13 @@ wrapper. A bounded accessible marker is rendered when `renderMarker` is omitted.
|
|
|
275
275
|
remain in native vector or GeoJSON layers and are selected through semantic rendered-feature
|
|
276
276
|
queries rather than thousands of React marker views.
|
|
277
277
|
|
|
278
|
+
`Map` also takes the web adapters' `dataSources`, `dataOverlays` and `annotationClusters`, with
|
|
279
|
+
`geojsonSource()`, `clusterOverlay()` and `heatmapOverlay()` from `@tileflow/core/native`. Tapping a
|
|
280
|
+
cluster zooms in unless the application controls `view`, and reports a `cluster` target through
|
|
281
|
+
`onInteractionEvent`. Compiled clusters and heatmaps are checked by
|
|
282
|
+
`tileflow validate --renderer native`. Polygon drawing with Terra Draw is web-only. See the
|
|
283
|
+
[interactions reference](https://github.com/tileflow/tileflow-sdk/blob/main/packages/interactions/REFERENCE.md).
|
|
284
|
+
|
|
278
285
|
The application owns persistent selection and its presentation. The SDK emits normalized
|
|
279
286
|
annotation or POI targets but no popup, callout, tooltip, sheet, panel or modal renderer.
|
|
280
287
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { MapProps as MapProps$1 } from '@maplibre/maplibre-react-native';
|
|
2
2
|
import { ReactElement, ReactNode, Ref } from 'react';
|
|
3
3
|
import { ViewProps } from 'react-native';
|
|
4
|
-
import { TileflowNativeInitialView, TileflowNativeInitialViewOptions, TileflowNativeSource, TileflowNativeSourceOptions, TileflowBoundaryValues, TileflowBoundaryFamily, TileflowNativeSourceState, TileflowNativeSourceError } from '@tileflow/core/native';
|
|
5
|
-
import { TileflowAnnotation, TileflowInteractionBinding, TileflowInteractionEvent, TileflowResolvedAnnotationTarget, TileflowResolvedPoiFeatureTarget, TileflowInteractionDiagnostic, TileflowAnnotationViewContext } from '@tileflow/interactions';
|
|
4
|
+
import { TileflowMapLibreOverlay, TileflowGeoJsonSource, TileflowNativeInitialView, TileflowNativeInitialViewOptions, TileflowNativeSource, TileflowNativeSourceOptions, TileflowBoundaryValues, TileflowBoundaryFamily, TileflowNativeSourceState, TileflowNativeSourceError } from '@tileflow/core/native';
|
|
5
|
+
import { TileflowAnnotation, TileflowAnnotationClusterOptions, TileflowInteractionBinding, TileflowInteractionEvent, TileflowResolvedAnnotationTarget, TileflowResolvedClusterTarget, TileflowResolvedPoiFeatureTarget, TileflowResolvedStyleFeatureTarget, TileflowInteractionDiagnostic, TileflowAnnotationViewContext } from '@tileflow/interactions';
|
|
6
6
|
|
|
7
7
|
/** Marker composition only. Selection presentation and native handles are not part of this context. */
|
|
8
8
|
type MapMarkerRenderContext<TAnnotation extends TileflowAnnotation = TileflowAnnotation> = Readonly<Pick<TileflowAnnotationViewContext<TAnnotation>, 'annotation' | 'target' | 'content' | 'viewName'>>;
|
|
@@ -10,11 +10,17 @@ type MapMarkerRenderContext<TAnnotation extends TileflowAnnotation = TileflowAnn
|
|
|
10
10
|
type MapMarkerRenderer<TAnnotation extends TileflowAnnotation = TileflowAnnotation> = (context: MapMarkerRenderContext<TAnnotation>) => ReactElement;
|
|
11
11
|
/** Mobile reports activation; the application owns any persistent selection. */
|
|
12
12
|
type MapInteractionEvent<TAnnotation extends TileflowAnnotation = TileflowAnnotation> = Readonly<Omit<TileflowInteractionEvent<TAnnotation>, 'target' | 'type'> & {
|
|
13
|
-
target: TileflowResolvedAnnotationTarget<TAnnotation> | TileflowResolvedPoiFeatureTarget;
|
|
13
|
+
target: TileflowResolvedAnnotationTarget<TAnnotation> | TileflowResolvedClusterTarget | TileflowResolvedPoiFeatureTarget | TileflowResolvedStyleFeatureTarget;
|
|
14
14
|
type: 'target:activate';
|
|
15
15
|
}>;
|
|
16
16
|
type MapInteractionProps<TAnnotation extends TileflowAnnotation = TileflowAnnotation> = Readonly<{
|
|
17
|
+
/** Group `annotations` into accessible native cluster buttons. Up to 1,000 annotations. */
|
|
18
|
+
annotationClusters?: TileflowAnnotationClusterOptions;
|
|
17
19
|
annotations?: readonly TAnnotation[];
|
|
20
|
+
/** Overlays drawn over `dataSources` or compiled sources, keyed as in `defineMap`. */
|
|
21
|
+
dataOverlays?: Readonly<Record<string, TileflowMapLibreOverlay>>;
|
|
22
|
+
/** Application GeoJSON, keyed as in `defineMap`. A compiled source of the same key is filled. */
|
|
23
|
+
dataSources?: Readonly<Record<string, TileflowGeoJsonSource>>;
|
|
18
24
|
interactions?: readonly TileflowInteractionBinding[];
|
|
19
25
|
onInteractionEvent?: (event: MapInteractionEvent<TAnnotation>) => void;
|
|
20
26
|
onInteractionDiagnostic?: (diagnostic: TileflowInteractionDiagnostic) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
2
|
import { TileflowAnnotation } from '@tileflow/interactions';
|
|
3
3
|
export { TileflowAnnotation, TileflowInteractionBinding, TileflowInteractionCoordinate, TileflowInteractionDiagnostic, TileflowPoiCategory, TileflowResolvedAnnotationTarget, TileflowResolvedPoiFeatureTarget } from '@tileflow/interactions';
|
|
4
|
-
import { d as MapProps } from './contract-
|
|
5
|
-
export { e as MapBaseProps, f as MapBoundaryFeatureState, c as MapCameraProps, g as MapColorScheme, h as MapErrorEvent, i as MapEventHandlers, b as MapInitialViewInputs, j as MapInteractionEvent, k as MapInteractionProps, l as MapLoadEvent, m as MapMarkerRenderContext, n as MapMarkerRenderer, o as MapOptions, p as MapPresentationProps, q as MapReadinessChangeEvent, r as MapRef, s as MapSelection, t as MapSessionError, u as MapSource, v as MapSourceError, w as MapSourceProps, x as MapSourceState, y as MapTheme, z as MapThemeBlend, A as MapThemeChangeEvent, B as MapThemeSelection, C as MapThemeTransition, M as MapView, a as MapViewChangeEvent } from './contract-
|
|
4
|
+
import { d as MapProps } from './contract-BWCbfMOx.js';
|
|
5
|
+
export { e as MapBaseProps, f as MapBoundaryFeatureState, c as MapCameraProps, g as MapColorScheme, h as MapErrorEvent, i as MapEventHandlers, b as MapInitialViewInputs, j as MapInteractionEvent, k as MapInteractionProps, l as MapLoadEvent, m as MapMarkerRenderContext, n as MapMarkerRenderer, o as MapOptions, p as MapPresentationProps, q as MapReadinessChangeEvent, r as MapRef, s as MapSelection, t as MapSessionError, u as MapSource, v as MapSourceError, w as MapSourceProps, x as MapSourceState, y as MapTheme, z as MapThemeBlend, A as MapThemeChangeEvent, B as MapThemeSelection, C as MapThemeTransition, M as MapView, a as MapViewChangeEvent } from './contract-BWCbfMOx.js';
|
|
6
6
|
import '@maplibre/maplibre-react-native';
|
|
7
7
|
import 'react-native';
|
|
8
8
|
import '@tileflow/core/native';
|
package/dist/index.js
CHANGED
|
@@ -15,16 +15,25 @@ import {
|
|
|
15
15
|
Map as NativeMap
|
|
16
16
|
} from "@maplibre/maplibre-react-native";
|
|
17
17
|
import {
|
|
18
|
-
Fragment,
|
|
18
|
+
Fragment as Fragment2,
|
|
19
19
|
useImperativeHandle,
|
|
20
20
|
useLayoutEffect as useLayoutEffect3,
|
|
21
21
|
useMemo as useMemo3,
|
|
22
|
-
useRef,
|
|
22
|
+
useRef as useRef2,
|
|
23
23
|
useState as useState2,
|
|
24
24
|
useSyncExternalStore
|
|
25
25
|
} from "react";
|
|
26
26
|
import { AppState, findNodeHandle, View as View2 } from "react-native";
|
|
27
27
|
|
|
28
|
+
// src/native-annotation-clusters.ts
|
|
29
|
+
import {
|
|
30
|
+
createTileflowAnnotationClusterIndex
|
|
31
|
+
} from "@tileflow/interactions";
|
|
32
|
+
import { withTileflowClusterLabelFont } from "@tileflow/interactions";
|
|
33
|
+
function createNativeAnnotationClusters(annotations, options) {
|
|
34
|
+
return createTileflowAnnotationClusterIndex(annotations, options);
|
|
35
|
+
}
|
|
36
|
+
|
|
28
37
|
// src/native-interaction-input.ts
|
|
29
38
|
import {
|
|
30
39
|
tileflowInteractionJsonValueSchema,
|
|
@@ -36,6 +45,7 @@ var messages = {
|
|
|
36
45
|
DUPLICATE_ANNOTATION_ID: "Annotation IDs must be unique.",
|
|
37
46
|
INVALID_FIELD: "An interaction field is invalid.",
|
|
38
47
|
INVALID_ANNOTATION: "An annotation is invalid.",
|
|
48
|
+
INVALID_DATA_LAYER: "A data source or overlay is invalid.",
|
|
39
49
|
INVALID_DOCUMENT: "The interaction document is invalid.",
|
|
40
50
|
LIMIT_EXCEEDED: "The portable interaction limit was exceeded.",
|
|
41
51
|
MISSING_VIEW: "The interaction view is unavailable.",
|
|
@@ -828,8 +838,8 @@ function item(value, index) {
|
|
|
828
838
|
return descriptor.value;
|
|
829
839
|
}
|
|
830
840
|
function array2(value, maximum2) {
|
|
831
|
-
const
|
|
832
|
-
return Array.from({ length:
|
|
841
|
+
const count2 = length(value, maximum2);
|
|
842
|
+
return Array.from({ length: count2 }, (_, index) => item(value, index));
|
|
833
843
|
}
|
|
834
844
|
function name2(value) {
|
|
835
845
|
return typeof value === "string" && value.length > 0 && value.length <= 256;
|
|
@@ -914,8 +924,8 @@ function createNativeInteractionQuery(style, current, query) {
|
|
|
914
924
|
cancel();
|
|
915
925
|
return;
|
|
916
926
|
}
|
|
917
|
-
const
|
|
918
|
-
const read = Math.min(
|
|
927
|
+
const count2 = length(hits);
|
|
928
|
+
const read = Math.min(count2, nativePoiQueryLimit + 1 - features.length);
|
|
919
929
|
for (let index = 0; index < read; index++) {
|
|
920
930
|
const feature = item(hits, index);
|
|
921
931
|
if (nativeInteractionField(feature, "type") !== "Feature") throw new Error();
|
|
@@ -1501,10 +1511,245 @@ function NativeBoundaryFeatureState({
|
|
|
1501
1511
|
);
|
|
1502
1512
|
}
|
|
1503
1513
|
|
|
1514
|
+
// src/native-data-layers.tsx
|
|
1515
|
+
import {
|
|
1516
|
+
GeoJSONSource,
|
|
1517
|
+
Layer,
|
|
1518
|
+
Marker
|
|
1519
|
+
} from "@maplibre/maplibre-react-native";
|
|
1520
|
+
import { useRef } from "react";
|
|
1521
|
+
import { Pressable, Text } from "react-native";
|
|
1522
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1523
|
+
var bubble = Object.freeze({
|
|
1524
|
+
alignItems: "center",
|
|
1525
|
+
backgroundColor: "#2563eb",
|
|
1526
|
+
borderColor: "#ffffff",
|
|
1527
|
+
borderRadius: 22,
|
|
1528
|
+
borderWidth: 2,
|
|
1529
|
+
justifyContent: "center",
|
|
1530
|
+
minHeight: 44,
|
|
1531
|
+
minWidth: 44,
|
|
1532
|
+
paddingHorizontal: 6
|
|
1533
|
+
});
|
|
1534
|
+
var count = Object.freeze({ color: "#ffffff", fontSize: 14, fontWeight: "600" });
|
|
1535
|
+
function NativeDataLayers({
|
|
1536
|
+
camera,
|
|
1537
|
+
expand,
|
|
1538
|
+
onActivate,
|
|
1539
|
+
plan
|
|
1540
|
+
}) {
|
|
1541
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1542
|
+
plan.sources.map((source) => /* @__PURE__ */ jsx2(
|
|
1543
|
+
NativeDataSource,
|
|
1544
|
+
{
|
|
1545
|
+
camera,
|
|
1546
|
+
expand,
|
|
1547
|
+
onActivate,
|
|
1548
|
+
source
|
|
1549
|
+
},
|
|
1550
|
+
source.id
|
|
1551
|
+
)),
|
|
1552
|
+
plan.layers.map((layer) => /* @__PURE__ */ jsx2(Layer, { ...layer }, layer.id))
|
|
1553
|
+
] });
|
|
1554
|
+
}
|
|
1555
|
+
function NativeDataSource({
|
|
1556
|
+
camera,
|
|
1557
|
+
expand,
|
|
1558
|
+
onActivate,
|
|
1559
|
+
source
|
|
1560
|
+
}) {
|
|
1561
|
+
const ref = useRef(null);
|
|
1562
|
+
const pressable = source.clusterLayers.length > 0 || source.pointLayers.length > 0;
|
|
1563
|
+
return /* @__PURE__ */ jsx2(
|
|
1564
|
+
GeoJSONSource,
|
|
1565
|
+
{
|
|
1566
|
+
...source.definition,
|
|
1567
|
+
id: source.id,
|
|
1568
|
+
ref,
|
|
1569
|
+
data: source.data,
|
|
1570
|
+
onPress: pressable ? (event) => {
|
|
1571
|
+
const feature = event.nativeEvent.features[0];
|
|
1572
|
+
const coordinate2 = pointCoordinate(feature);
|
|
1573
|
+
if (!feature || !coordinate2) return;
|
|
1574
|
+
const properties2 = jsonProperties(feature.properties);
|
|
1575
|
+
const clusterId = properties2.cluster_id;
|
|
1576
|
+
if (typeof clusterId !== "number") {
|
|
1577
|
+
const layerId = source.pointLayers[0];
|
|
1578
|
+
if (!layerId) return;
|
|
1579
|
+
onActivate({
|
|
1580
|
+
coordinate: coordinate2,
|
|
1581
|
+
feature: { ...feature.id === void 0 ? {} : { id: feature.id }, properties: properties2 },
|
|
1582
|
+
kind: "style-feature",
|
|
1583
|
+
layerId
|
|
1584
|
+
});
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
onActivate({
|
|
1588
|
+
clusterId,
|
|
1589
|
+
coordinate: coordinate2,
|
|
1590
|
+
kind: "cluster",
|
|
1591
|
+
pointCount: typeof properties2.point_count === "number" ? properties2.point_count : 0,
|
|
1592
|
+
properties: properties2,
|
|
1593
|
+
source: source.id
|
|
1594
|
+
});
|
|
1595
|
+
if (!expand || !source.expandOnClick) return;
|
|
1596
|
+
ref.current?.getClusterExpansionZoom(clusterId).then(
|
|
1597
|
+
(zoom) => camera.current?.easeTo({
|
|
1598
|
+
center: [coordinate2[0], coordinate2[1]],
|
|
1599
|
+
duration: 400,
|
|
1600
|
+
zoom
|
|
1601
|
+
}),
|
|
1602
|
+
() => void 0
|
|
1603
|
+
);
|
|
1604
|
+
} : void 0,
|
|
1605
|
+
children: source.layers.map((layer) => /* @__PURE__ */ jsx2(Layer, { ...layer }, layer.id))
|
|
1606
|
+
}
|
|
1607
|
+
);
|
|
1608
|
+
}
|
|
1609
|
+
function NativeClusterMarker({
|
|
1610
|
+
cluster,
|
|
1611
|
+
label,
|
|
1612
|
+
onPress
|
|
1613
|
+
}) {
|
|
1614
|
+
const press = (event) => {
|
|
1615
|
+
event?.stopPropagation();
|
|
1616
|
+
onPress();
|
|
1617
|
+
};
|
|
1618
|
+
return /* @__PURE__ */ jsx2(
|
|
1619
|
+
Marker,
|
|
1620
|
+
{
|
|
1621
|
+
id: `tileflow-cluster-${cluster.clusterId}`,
|
|
1622
|
+
lngLat: [cluster.coordinate[0], cluster.coordinate[1]],
|
|
1623
|
+
accessible: true,
|
|
1624
|
+
accessibilityLabel: label,
|
|
1625
|
+
accessibilityRole: "button",
|
|
1626
|
+
onAccessibilityTap: onPress,
|
|
1627
|
+
onPress: press,
|
|
1628
|
+
children: /* @__PURE__ */ jsx2(Pressable, { accessible: false, onPress: press, style: bubble, children: /* @__PURE__ */ jsx2(Text, { style: count, children: cluster.label }) })
|
|
1629
|
+
}
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
1632
|
+
function pointCoordinate(feature) {
|
|
1633
|
+
const coordinates = feature?.geometry?.coordinates;
|
|
1634
|
+
return feature?.geometry?.type === "Point" && Array.isArray(coordinates) && typeof coordinates[0] === "number" && typeof coordinates[1] === "number" ? [coordinates[0], coordinates[1]] : void 0;
|
|
1635
|
+
}
|
|
1636
|
+
function jsonProperties(properties2) {
|
|
1637
|
+
return Object.fromEntries(
|
|
1638
|
+
Object.entries(properties2 ?? {}).filter(([, value]) => value !== void 0)
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
// src/native-data-layers-plan.ts
|
|
1643
|
+
var clusterMetadataKey = "tileflow:cluster";
|
|
1644
|
+
var placementMetadataKey = "tileflow:overlay-placement-manifest";
|
|
1645
|
+
function planNativeDataLayers(input) {
|
|
1646
|
+
const styleSources = input.style.sources ?? {};
|
|
1647
|
+
const styleLayers = input.style.layers ?? [];
|
|
1648
|
+
const styleLayerIds = new Set(styleLayers.map(({ id }) => String(id)));
|
|
1649
|
+
const anchors = input.style.metadata?.[placementMetadataKey]?.anchors;
|
|
1650
|
+
const diagnostics = [];
|
|
1651
|
+
const entries = /* @__PURE__ */ new Map();
|
|
1652
|
+
const freeLayers = [];
|
|
1653
|
+
const entry = (id) => entries.get(id);
|
|
1654
|
+
const addRoles = (sourceId, layer) => {
|
|
1655
|
+
const target = entry(sourceId);
|
|
1656
|
+
const cluster = layer.metadata?.[clusterMetadataKey];
|
|
1657
|
+
if (!target || !cluster) return;
|
|
1658
|
+
if (cluster.role === "cluster") target.clusterLayers.push(String(layer.id));
|
|
1659
|
+
if (cluster.role === "point") target.pointLayers.push(String(layer.id));
|
|
1660
|
+
if (cluster.expandOnClick === false) target.expandOnClick = false;
|
|
1661
|
+
};
|
|
1662
|
+
for (const [id, source] of Object.entries(input.dataSources ?? {}).sort(compare)) {
|
|
1663
|
+
const compiled2 = styleSources[id];
|
|
1664
|
+
if (source.kind !== "geojson" || compiled2 && compiled2.type !== "geojson") {
|
|
1665
|
+
diagnostics.push({ message: `Source "${id}" is not GeoJSON.`, path: `/dataSources/${id}` });
|
|
1666
|
+
continue;
|
|
1667
|
+
}
|
|
1668
|
+
const { data, kind: _kind, ...definition } = source;
|
|
1669
|
+
entries.set(id, {
|
|
1670
|
+
adopted: compiled2 !== void 0,
|
|
1671
|
+
clusterLayers: [],
|
|
1672
|
+
data,
|
|
1673
|
+
// A reused source keeps its compiled cluster options.
|
|
1674
|
+
definition: compiled2 ? {} : definition,
|
|
1675
|
+
expandOnClick: true,
|
|
1676
|
+
layers: [],
|
|
1677
|
+
pointLayers: []
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
for (const layer of styleLayers) {
|
|
1681
|
+
const sourceId = typeof layer.source === "string" ? layer.source : void 0;
|
|
1682
|
+
const cluster = layer.metadata?.[clusterMetadataKey];
|
|
1683
|
+
if (!sourceId || !cluster || entries.has(sourceId)) continue;
|
|
1684
|
+
const compiled2 = styleSources[sourceId];
|
|
1685
|
+
if (compiled2?.type !== "geojson") continue;
|
|
1686
|
+
entries.set(sourceId, {
|
|
1687
|
+
adopted: true,
|
|
1688
|
+
clusterLayers: [],
|
|
1689
|
+
data: compiled2.data,
|
|
1690
|
+
definition: {},
|
|
1691
|
+
expandOnClick: true,
|
|
1692
|
+
layers: [],
|
|
1693
|
+
pointLayers: []
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
for (const layer of styleLayers) {
|
|
1697
|
+
if (typeof layer.source === "string") addRoles(layer.source, layer);
|
|
1698
|
+
}
|
|
1699
|
+
for (const [id, overlay] of Object.entries(input.dataOverlays ?? {}).sort(compare)) {
|
|
1700
|
+
const path = `/dataOverlays/${id}`;
|
|
1701
|
+
if (overlay.kind !== "maplibre-overlay") {
|
|
1702
|
+
diagnostics.push({ message: `Overlay "${id}" is not a Tileflow overlay.`, path });
|
|
1703
|
+
continue;
|
|
1704
|
+
}
|
|
1705
|
+
const target = entry(overlay.source);
|
|
1706
|
+
if (!target && !styleSources[overlay.source]) {
|
|
1707
|
+
diagnostics.push({
|
|
1708
|
+
message: `Overlay "${id}" references unknown source "${overlay.source}".`,
|
|
1709
|
+
path: `${path}/source`
|
|
1710
|
+
});
|
|
1711
|
+
continue;
|
|
1712
|
+
}
|
|
1713
|
+
const anchor = anchors?.[overlay.placement];
|
|
1714
|
+
const beforeId = typeof anchor === "string" && styleLayerIds.has(anchor) ? anchor : void 0;
|
|
1715
|
+
for (const layer of overlay.layers) {
|
|
1716
|
+
if (styleLayerIds.has(layer.id)) continue;
|
|
1717
|
+
const prepared = withTileflowClusterLabelFont(layer, styleLayers);
|
|
1718
|
+
if (!prepared) {
|
|
1719
|
+
diagnostics.push({
|
|
1720
|
+
message: `Layer "${layer.id}" needs font: the style has no label font.`,
|
|
1721
|
+
path
|
|
1722
|
+
});
|
|
1723
|
+
continue;
|
|
1724
|
+
}
|
|
1725
|
+
const native = {
|
|
1726
|
+
...prepared,
|
|
1727
|
+
...beforeId ? { beforeId } : {},
|
|
1728
|
+
id: layer.id,
|
|
1729
|
+
source: overlay.source
|
|
1730
|
+
};
|
|
1731
|
+
if (target) {
|
|
1732
|
+
target.layers.push(native);
|
|
1733
|
+
addRoles(overlay.source, layer);
|
|
1734
|
+
} else {
|
|
1735
|
+
freeLayers.push(native);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
return Object.freeze({
|
|
1740
|
+
diagnostics: Object.freeze(diagnostics),
|
|
1741
|
+
layers: Object.freeze(freeLayers),
|
|
1742
|
+
sources: Object.freeze([...entries].map(([id, value]) => Object.freeze({ id, ...value })))
|
|
1743
|
+
});
|
|
1744
|
+
}
|
|
1745
|
+
function compare([left], [right]) {
|
|
1746
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1504
1749
|
// src/native-marker.tsx
|
|
1505
1750
|
import { Marker as NativeMarker } from "@maplibre/maplibre-react-native";
|
|
1506
1751
|
import { useLayoutEffect as useLayoutEffect2, useMemo as useMemo2 } from "react";
|
|
1507
|
-
import { Pressable, processColor, View } from "react-native";
|
|
1752
|
+
import { Pressable as Pressable2, processColor, View } from "react-native";
|
|
1508
1753
|
|
|
1509
1754
|
// src/native-marker-model.ts
|
|
1510
1755
|
import { isValidElement } from "react";
|
|
@@ -1550,7 +1795,7 @@ function resolveNativeMarkerContent(context, render) {
|
|
|
1550
1795
|
}
|
|
1551
1796
|
|
|
1552
1797
|
// src/native-marker.tsx
|
|
1553
|
-
import { jsx as
|
|
1798
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1554
1799
|
var button = Object.freeze({
|
|
1555
1800
|
minWidth: 44,
|
|
1556
1801
|
minHeight: 44,
|
|
@@ -1593,7 +1838,7 @@ function NativeAnnotationMarker({
|
|
|
1593
1838
|
event?.stopPropagation();
|
|
1594
1839
|
lifecycle.markerPress(sceneKey, annotation);
|
|
1595
1840
|
};
|
|
1596
|
-
return /* @__PURE__ */
|
|
1841
|
+
return /* @__PURE__ */ jsx3(
|
|
1597
1842
|
NativeMarker,
|
|
1598
1843
|
{
|
|
1599
1844
|
id: `tileflow-annotation-${model.key}`,
|
|
@@ -1604,8 +1849,8 @@ function NativeAnnotationMarker({
|
|
|
1604
1849
|
lifecycle.beginTouch(sceneKey);
|
|
1605
1850
|
press();
|
|
1606
1851
|
},
|
|
1607
|
-
children: /* @__PURE__ */
|
|
1608
|
-
|
|
1852
|
+
children: /* @__PURE__ */ jsx3(
|
|
1853
|
+
Pressable2,
|
|
1609
1854
|
{
|
|
1610
1855
|
accessible: false,
|
|
1611
1856
|
disabled: !enabled,
|
|
@@ -1615,14 +1860,14 @@ function NativeAnnotationMarker({
|
|
|
1615
1860
|
event.stopPropagation();
|
|
1616
1861
|
},
|
|
1617
1862
|
onPress: press,
|
|
1618
|
-
children: /* @__PURE__ */
|
|
1863
|
+
children: /* @__PURE__ */ jsx3(
|
|
1619
1864
|
View,
|
|
1620
1865
|
{
|
|
1621
1866
|
pointerEvents: "none",
|
|
1622
1867
|
accessible: false,
|
|
1623
1868
|
accessibilityElementsHidden: true,
|
|
1624
1869
|
importantForAccessibility: "no-hide-descendants",
|
|
1625
|
-
children: rendered.content ?? /* @__PURE__ */
|
|
1870
|
+
children: rendered.content ?? /* @__PURE__ */ jsx3(View, { style: [dot, { backgroundColor: color }] })
|
|
1626
1871
|
}
|
|
1627
1872
|
)
|
|
1628
1873
|
}
|
|
@@ -2823,7 +3068,7 @@ function decodeUtf8(bytes) {
|
|
|
2823
3068
|
if (index + remaining > bytes.length) {
|
|
2824
3069
|
throw new HostedNativeSessionError("NATIVE_SESSION_RESPONSE_UTF8_INVALID");
|
|
2825
3070
|
}
|
|
2826
|
-
for (let
|
|
3071
|
+
for (let count2 = 0; count2 < remaining; count2 += 1) {
|
|
2827
3072
|
const byte = bytes[index++];
|
|
2828
3073
|
if ((byte & 192) !== 128) {
|
|
2829
3074
|
throw new HostedNativeSessionError("NATIVE_SESSION_RESPONSE_UTF8_INVALID");
|
|
@@ -3890,27 +4135,27 @@ function decode(bytes) {
|
|
|
3890
4135
|
for (let index = 0; index < bytes.length; ) {
|
|
3891
4136
|
const first = bytes[index++];
|
|
3892
4137
|
let code;
|
|
3893
|
-
let
|
|
4138
|
+
let count2;
|
|
3894
4139
|
let minimum;
|
|
3895
4140
|
if (first <= 127) {
|
|
3896
4141
|
code = first;
|
|
3897
|
-
|
|
4142
|
+
count2 = 0;
|
|
3898
4143
|
minimum = 0;
|
|
3899
4144
|
} else if (first >= 194 && first <= 223) {
|
|
3900
4145
|
code = first & 31;
|
|
3901
|
-
|
|
4146
|
+
count2 = 1;
|
|
3902
4147
|
minimum = 128;
|
|
3903
4148
|
} else if (first >= 224 && first <= 239) {
|
|
3904
4149
|
code = first & 15;
|
|
3905
|
-
|
|
4150
|
+
count2 = 2;
|
|
3906
4151
|
minimum = 2048;
|
|
3907
4152
|
} else if (first >= 240 && first <= 244) {
|
|
3908
4153
|
code = first & 7;
|
|
3909
|
-
|
|
4154
|
+
count2 = 3;
|
|
3910
4155
|
minimum = 65536;
|
|
3911
4156
|
} else throw new NativePreparationError();
|
|
3912
|
-
if (index +
|
|
3913
|
-
for (let part = 0; part <
|
|
4157
|
+
if (index + count2 > bytes.length) throw new NativePreparationError();
|
|
4158
|
+
for (let part = 0; part < count2; part++) {
|
|
3914
4159
|
const next = bytes[index++];
|
|
3915
4160
|
if ((next & 192) !== 128) throw new NativePreparationError();
|
|
3916
4161
|
code = code * 64 + (next & 63);
|
|
@@ -5594,7 +5839,7 @@ function fontMetadata(input, developmentOrigin) {
|
|
|
5594
5839
|
}
|
|
5595
5840
|
function validateFontFaces(faces, documentUrl, metadata, add, rewrites, policy, discriminate, developmentOrigin) {
|
|
5596
5841
|
if (Object.keys(faces).length > nativePreparationLimits.fontFaces) throw invalid3();
|
|
5597
|
-
let
|
|
5842
|
+
let count2 = 0;
|
|
5598
5843
|
const matched = /* @__PURE__ */ new Set();
|
|
5599
5844
|
for (const name3 of Object.keys(faces).sort()) {
|
|
5600
5845
|
if (name3.length === 0 || name3.length > 256 || name3 !== name3.trim()) throw invalid3();
|
|
@@ -5602,7 +5847,7 @@ function validateFontFaces(faces, documentUrl, metadata, add, rewrites, policy,
|
|
|
5602
5847
|
if (!Array.isArray(definitions) || definitions.length === 0) throw invalid3();
|
|
5603
5848
|
const rewritten = [];
|
|
5604
5849
|
for (const definition of definitions) {
|
|
5605
|
-
if (++
|
|
5850
|
+
if (++count2 > nativePreparationLimits.fontFaces || !record2(definition)) throw invalid3();
|
|
5606
5851
|
const keys = Object.keys(definition).sort();
|
|
5607
5852
|
if (keys.length !== 4 || !["font-family", "font-style", "font-weight", "url"].every((key2) => keys.includes(key2)))
|
|
5608
5853
|
throw invalid3();
|
|
@@ -7703,7 +7948,7 @@ function createNativeMapOwner() {
|
|
|
7703
7948
|
}
|
|
7704
7949
|
|
|
7705
7950
|
// src/map.tsx
|
|
7706
|
-
import { jsx as
|
|
7951
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7707
7952
|
var fill = Object.freeze({ flex: 1 });
|
|
7708
7953
|
function NativeScene({
|
|
7709
7954
|
scene,
|
|
@@ -7713,17 +7958,18 @@ function NativeScene({
|
|
|
7713
7958
|
interactions,
|
|
7714
7959
|
interactionEnabled,
|
|
7715
7960
|
renderMarker,
|
|
7716
|
-
boundaryFeatureState
|
|
7961
|
+
boundaryFeatureState,
|
|
7962
|
+
data
|
|
7717
7963
|
}) {
|
|
7718
|
-
const view =
|
|
7719
|
-
const camera =
|
|
7720
|
-
const nativeMap =
|
|
7721
|
-
const committedHost =
|
|
7722
|
-
const tag =
|
|
7723
|
-
const laidOut =
|
|
7724
|
-
const styleLoaded =
|
|
7725
|
-
const attachRequested =
|
|
7726
|
-
const latestStyle =
|
|
7964
|
+
const view = useRef2(null);
|
|
7965
|
+
const camera = useRef2(null);
|
|
7966
|
+
const nativeMap = useRef2(null);
|
|
7967
|
+
const committedHost = useRef2(void 0);
|
|
7968
|
+
const tag = useRef2(null);
|
|
7969
|
+
const laidOut = useRef2(false);
|
|
7970
|
+
const styleLoaded = useRef2(false);
|
|
7971
|
+
const attachRequested = useRef2(false);
|
|
7972
|
+
const latestStyle = useRef2(scene.style);
|
|
7727
7973
|
latestStyle.current = scene.style;
|
|
7728
7974
|
const [loadedStyle, setLoadedStyle] = useState2(void 0);
|
|
7729
7975
|
const host = useMemo3(() => {
|
|
@@ -7778,7 +8024,42 @@ function NativeScene({
|
|
|
7778
8024
|
};
|
|
7779
8025
|
const annotations = interactions?.annotations;
|
|
7780
8026
|
const byId = new globalThis.Map(annotations?.map((annotation) => [annotation.id, annotation]));
|
|
7781
|
-
|
|
8027
|
+
const [zoomLevel, setZoomLevel] = useState2(() => Math.floor(scene.initialView.zoom));
|
|
8028
|
+
const followZoom = (event) => {
|
|
8029
|
+
if (Number.isFinite(event.nativeEvent.zoom)) setZoomLevel(Math.floor(event.nativeEvent.zoom));
|
|
8030
|
+
};
|
|
8031
|
+
const clusterSettings = data.annotationClusters;
|
|
8032
|
+
const clusterKey = JSON.stringify(clusterSettings ?? null);
|
|
8033
|
+
const clusterIndex = useMemo3(
|
|
8034
|
+
() => clusterSettings && annotations ? createNativeAnnotationClusters(annotations, clusterSettings) : void 0,
|
|
8035
|
+
// Options written inline are new objects each render; only a real change rebuilds groups.
|
|
8036
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
8037
|
+
[annotations, clusterKey]
|
|
8038
|
+
);
|
|
8039
|
+
const clusterView = clusterIndex?.at(zoomLevel);
|
|
8040
|
+
const styleReady = loadedStyle === scene.style;
|
|
8041
|
+
const dataPlan = useMemo3(
|
|
8042
|
+
() => planNativeDataLayers({
|
|
8043
|
+
dataOverlays: data.dataOverlays,
|
|
8044
|
+
dataSources: data.dataSources,
|
|
8045
|
+
style: scene.style
|
|
8046
|
+
}),
|
|
8047
|
+
[data.dataOverlays, data.dataSources, scene.style]
|
|
8048
|
+
);
|
|
8049
|
+
const reportedPlan = useRef2(void 0);
|
|
8050
|
+
useLayoutEffect3(() => {
|
|
8051
|
+
if (reportedPlan.current === dataPlan) return;
|
|
8052
|
+
reportedPlan.current = dataPlan;
|
|
8053
|
+
for (const { message, path } of dataPlan.diagnostics)
|
|
8054
|
+
data.onDiagnostic({ code: "INVALID_DATA_LAYER", level: "error", message, path });
|
|
8055
|
+
}, [data, dataPlan]);
|
|
8056
|
+
const activate = (target) => data.onActivate({
|
|
8057
|
+
coordinate: target.coordinate,
|
|
8058
|
+
inputModality: "touch",
|
|
8059
|
+
target,
|
|
8060
|
+
type: "target:activate"
|
|
8061
|
+
});
|
|
8062
|
+
return /* @__PURE__ */ jsx4(
|
|
7782
8063
|
View2,
|
|
7783
8064
|
{
|
|
7784
8065
|
ref: bind2,
|
|
@@ -7795,13 +8076,15 @@ function NativeScene({
|
|
|
7795
8076
|
lifecycle.layoutChanged(scene.key);
|
|
7796
8077
|
attach();
|
|
7797
8078
|
},
|
|
7798
|
-
children: /* @__PURE__ */
|
|
8079
|
+
children: /* @__PURE__ */ jsxs2(
|
|
7799
8080
|
NativeMap,
|
|
7800
8081
|
{
|
|
7801
8082
|
...mapOptions,
|
|
7802
8083
|
ref: nativeMap,
|
|
7803
8084
|
style: fill,
|
|
7804
8085
|
mapStyle,
|
|
8086
|
+
onRegionIsChanging: followZoom,
|
|
8087
|
+
onRegionDidChange: followZoom,
|
|
7805
8088
|
onPress: (event) => {
|
|
7806
8089
|
if (!host.current()) return;
|
|
7807
8090
|
try {
|
|
@@ -7820,7 +8103,7 @@ function NativeScene({
|
|
|
7820
8103
|
attach();
|
|
7821
8104
|
},
|
|
7822
8105
|
children: [
|
|
7823
|
-
/* @__PURE__ */
|
|
8106
|
+
/* @__PURE__ */ jsx4(
|
|
7824
8107
|
NativeCamera,
|
|
7825
8108
|
{
|
|
7826
8109
|
ref: camera,
|
|
@@ -7830,21 +8113,57 @@ function NativeScene({
|
|
|
7830
8113
|
},
|
|
7831
8114
|
"tileflow-camera"
|
|
7832
8115
|
),
|
|
7833
|
-
/* @__PURE__ */
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
8116
|
+
/* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
8117
|
+
interactions?.plan.order.map((id) => {
|
|
8118
|
+
const annotation = byId.get(id);
|
|
8119
|
+
return annotation && !clusterView?.hiddenAnnotationIds.has(id) ? /* @__PURE__ */ jsx4(
|
|
8120
|
+
NativeAnnotationMarker,
|
|
8121
|
+
{
|
|
8122
|
+
annotation,
|
|
8123
|
+
enabled: interactionEnabled,
|
|
8124
|
+
sceneKey: scene.key,
|
|
8125
|
+
lifecycle,
|
|
8126
|
+
renderMarker
|
|
8127
|
+
},
|
|
8128
|
+
annotation.id
|
|
8129
|
+
) : null;
|
|
8130
|
+
}),
|
|
8131
|
+
clusterView?.clusters.map((cluster) => /* @__PURE__ */ jsx4(
|
|
8132
|
+
NativeClusterMarker,
|
|
7837
8133
|
{
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
8134
|
+
cluster,
|
|
8135
|
+
label: clusterSettings?.formatAriaLabel?.(cluster.pointCount) ?? `${cluster.pointCount} places`,
|
|
8136
|
+
onPress: () => {
|
|
8137
|
+
activate({
|
|
8138
|
+
annotationIds: cluster.annotationIds,
|
|
8139
|
+
clusterId: cluster.clusterId,
|
|
8140
|
+
coordinate: cluster.coordinate,
|
|
8141
|
+
kind: "cluster",
|
|
8142
|
+
pointCount: cluster.pointCount,
|
|
8143
|
+
properties: { point_count: cluster.pointCount }
|
|
8144
|
+
});
|
|
8145
|
+
if (data.expand && clusterSettings?.expandOnClick !== false)
|
|
8146
|
+
camera.current?.easeTo({
|
|
8147
|
+
center: [cluster.coordinate[0], cluster.coordinate[1]],
|
|
8148
|
+
duration: 400,
|
|
8149
|
+
zoom: cluster.expansionZoom
|
|
8150
|
+
});
|
|
8151
|
+
}
|
|
7843
8152
|
},
|
|
7844
|
-
|
|
7845
|
-
)
|
|
7846
|
-
|
|
7847
|
-
|
|
8153
|
+
`cluster-${cluster.clusterId}`
|
|
8154
|
+
))
|
|
8155
|
+
] }, "tileflow-annotations"),
|
|
8156
|
+
styleReady ? /* @__PURE__ */ jsx4(
|
|
8157
|
+
NativeDataLayers,
|
|
8158
|
+
{
|
|
8159
|
+
camera,
|
|
8160
|
+
expand: data.expand,
|
|
8161
|
+
onActivate: activate,
|
|
8162
|
+
plan: dataPlan
|
|
8163
|
+
},
|
|
8164
|
+
"tileflow-data-layers"
|
|
8165
|
+
) : null,
|
|
8166
|
+
boundaryFeatureState ? /* @__PURE__ */ jsx4(
|
|
7848
8167
|
NativeBoundaryFeatureState,
|
|
7849
8168
|
{
|
|
7850
8169
|
input: boundaryFeatureState,
|
|
@@ -7869,6 +8188,22 @@ function Map2(props) {
|
|
|
7869
8188
|
lifecycle.getSnapshot,
|
|
7870
8189
|
lifecycle.getSnapshot
|
|
7871
8190
|
);
|
|
8191
|
+
const latestProps = useRef2(props);
|
|
8192
|
+
latestProps.current = props;
|
|
8193
|
+
const [callbacks] = useState2(() => ({
|
|
8194
|
+
onActivate: (event) => latestProps.current.onInteractionEvent?.({ ...event, type: "target:activate" }),
|
|
8195
|
+
onDiagnostic: (diagnostic) => latestProps.current.onInteractionDiagnostic?.(diagnostic)
|
|
8196
|
+
}));
|
|
8197
|
+
const data = useMemo3(
|
|
8198
|
+
() => ({
|
|
8199
|
+
annotationClusters: props.annotationClusters,
|
|
8200
|
+
dataOverlays: props.dataOverlays,
|
|
8201
|
+
dataSources: props.dataSources,
|
|
8202
|
+
expand: props.view === void 0,
|
|
8203
|
+
...callbacks
|
|
8204
|
+
}),
|
|
8205
|
+
[callbacks, props.annotationClusters, props.dataOverlays, props.dataSources, props.view]
|
|
8206
|
+
);
|
|
7872
8207
|
useImperativeHandle(
|
|
7873
8208
|
props.ref,
|
|
7874
8209
|
() => Object.freeze({ getSourceState: lifecycle.getSourceState, retry: lifecycle.retry }),
|
|
@@ -7889,7 +8224,7 @@ function Map2(props) {
|
|
|
7889
8224
|
useLayoutEffect3(() => {
|
|
7890
8225
|
lifecycle.update(props);
|
|
7891
8226
|
});
|
|
7892
|
-
return /* @__PURE__ */
|
|
8227
|
+
return /* @__PURE__ */ jsx4(View2, { style: props.style, testID: props.testID, children: snapshot.renderer ? /* @__PURE__ */ jsx4(
|
|
7893
8228
|
NativeScene,
|
|
7894
8229
|
{
|
|
7895
8230
|
scene: snapshot.renderer,
|
|
@@ -7899,6 +8234,7 @@ function Map2(props) {
|
|
|
7899
8234
|
interactionEnabled: snapshot.interactionEnabled === true,
|
|
7900
8235
|
renderMarker: props.renderMarker,
|
|
7901
8236
|
boundaryFeatureState: props.boundaryFeatureState,
|
|
8237
|
+
data,
|
|
7902
8238
|
children: props.children
|
|
7903
8239
|
},
|
|
7904
8240
|
snapshot.renderer.key
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as MapView, a as MapViewChangeEvent, b as MapInitialViewInputs, c as MapCameraProps } from '../contract-
|
|
1
|
+
import { M as MapView, a as MapViewChangeEvent, b as MapInitialViewInputs, c as MapCameraProps } from '../contract-BWCbfMOx.js';
|
|
2
2
|
import '@maplibre/maplibre-react-native';
|
|
3
3
|
import 'react';
|
|
4
4
|
import 'react-native';
|
|
@@ -9,6 +9,7 @@ var messages = {
|
|
|
9
9
|
DUPLICATE_ANNOTATION_ID: "Annotation IDs must be unique.",
|
|
10
10
|
INVALID_FIELD: "An interaction field is invalid.",
|
|
11
11
|
INVALID_ANNOTATION: "An annotation is invalid.",
|
|
12
|
+
INVALID_DATA_LAYER: "A data source or overlay is invalid.",
|
|
12
13
|
INVALID_DOCUMENT: "The interaction document is invalid.",
|
|
13
14
|
LIMIT_EXCEEDED: "The portable interaction limit was exceeded.",
|
|
14
15
|
MISSING_VIEW: "The interaction view is unavailable.",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TileflowNativeSourceState } from '@tileflow/core/native';
|
|
2
|
-
import { x as MapSourceState } from '../contract-
|
|
2
|
+
import { x as MapSourceState } from '../contract-BWCbfMOx.js';
|
|
3
3
|
import '@maplibre/maplibre-react-native';
|
|
4
4
|
import 'react';
|
|
5
5
|
import 'react-native';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/react-native",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.9",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Tileflow maps, camera control and semantic interactions for React Native.",
|
|
6
6
|
"type": "module",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@tileflow/core": ">=0.1.0-alpha.
|
|
39
|
-
"@tileflow/interactions": ">=0.1.0-alpha.
|
|
38
|
+
"@tileflow/core": ">=0.1.0-alpha.48 <0.1.0-beta.0",
|
|
39
|
+
"@tileflow/interactions": ">=0.1.0-alpha.8 <0.1.0-beta.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@maplibre/maplibre-react-native": "11.5.0",
|