@goway.to/maps 0.0.0-stage → 0.1.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.
Files changed (125) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +676 -0
  3. package/NOTICE +25 -0
  4. package/README.md +59 -2
  5. package/app.plugin.js +19 -0
  6. package/dist/CameraBinding.web.d.ts +24 -0
  7. package/dist/CameraBinding.web.js +46 -0
  8. package/dist/DefaultMapMarker.d.ts +8 -0
  9. package/dist/DefaultMapMarker.js +30 -0
  10. package/dist/GeoJSONSource.d.ts +2 -0
  11. package/dist/GeoJSONSource.js +17 -0
  12. package/dist/GeoJSONSource.web.d.ts +2 -0
  13. package/dist/GeoJSONSource.web.js +68 -0
  14. package/dist/InteractionBinding.web.d.ts +7 -0
  15. package/dist/InteractionBinding.web.js +35 -0
  16. package/dist/Layer.d.ts +2 -0
  17. package/dist/Layer.js +21 -0
  18. package/dist/Layer.web.d.ts +2 -0
  19. package/dist/Layer.web.js +46 -0
  20. package/dist/LayerStyle.web.d.ts +10 -0
  21. package/dist/LayerStyle.web.js +45 -0
  22. package/dist/LightBinding.web.d.ts +5 -0
  23. package/dist/LightBinding.web.js +19 -0
  24. package/dist/MapAttribution.d.ts +27 -0
  25. package/dist/MapAttribution.js +149 -0
  26. package/dist/MapBrand.d.ts +22 -0
  27. package/dist/MapBrand.js +91 -0
  28. package/dist/MapErrorState.d.ts +9 -0
  29. package/dist/MapErrorState.js +58 -0
  30. package/dist/MapPlugin.d.ts +6 -0
  31. package/dist/MapPlugin.js +3 -0
  32. package/dist/MapPlugin.web.d.ts +2 -0
  33. package/dist/MapPlugin.web.js +43 -0
  34. package/dist/MapView.d.ts +2 -0
  35. package/dist/MapView.js +420 -0
  36. package/dist/MapView.web.d.ts +5 -0
  37. package/dist/MapView.web.js +413 -0
  38. package/dist/Marker.d.ts +13 -0
  39. package/dist/Marker.js +11 -0
  40. package/dist/Marker.web.d.ts +15 -0
  41. package/dist/Marker.web.js +51 -0
  42. package/dist/Markers.d.ts +2 -0
  43. package/dist/Markers.js +13 -0
  44. package/dist/Markers.web.d.ts +2 -0
  45. package/dist/Markers.web.js +13 -0
  46. package/dist/PluginProps.web.d.ts +7 -0
  47. package/dist/PluginProps.web.js +19 -0
  48. package/dist/RasterDEMSource.d.ts +2 -0
  49. package/dist/RasterDEMSource.js +7 -0
  50. package/dist/RasterDEMSource.web.d.ts +2 -0
  51. package/dist/RasterDEMSource.web.js +44 -0
  52. package/dist/SourceData.web.d.ts +6 -0
  53. package/dist/SourceData.web.js +14 -0
  54. package/dist/UserLocation.d.ts +2 -0
  55. package/dist/UserLocation.js +19 -0
  56. package/dist/UserLocation.web.d.ts +2 -0
  57. package/dist/UserLocation.web.js +21 -0
  58. package/dist/brand/BrandedChrome.d.ts +47 -0
  59. package/dist/brand/BrandedChrome.js +51 -0
  60. package/dist/brand/GowayLogo.d.ts +28 -0
  61. package/dist/brand/GowayLogo.js +50 -0
  62. package/dist/brand/artwork.d.ts +130 -0
  63. package/dist/brand/artwork.js +176 -0
  64. package/dist/cameraReports.web.d.ts +50 -0
  65. package/dist/cameraReports.web.js +117 -0
  66. package/dist/cameraTarget.d.ts +14 -0
  67. package/dist/cameraTarget.js +8 -0
  68. package/dist/chromeLift.d.ts +15 -0
  69. package/dist/chromeLift.js +15 -0
  70. package/dist/clusters.d.ts +57 -0
  71. package/dist/clusters.js +102 -0
  72. package/dist/commands.web.d.ts +16 -0
  73. package/dist/commands.web.js +158 -0
  74. package/dist/compactCredit.d.ts +52 -0
  75. package/dist/compactCredit.js +40 -0
  76. package/dist/creditHeight.d.ts +3 -0
  77. package/dist/creditHeight.js +12 -0
  78. package/dist/creditLabels.d.ts +30 -0
  79. package/dist/creditLabels.js +30 -0
  80. package/dist/creditRise.d.ts +28 -0
  81. package/dist/creditRise.js +33 -0
  82. package/dist/dragAxes.web.d.ts +65 -0
  83. package/dist/dragAxes.web.js +70 -0
  84. package/dist/engineContext.web.d.ts +13 -0
  85. package/dist/engineContext.web.js +12 -0
  86. package/dist/index.d.ts +12 -0
  87. package/dist/index.js +12 -0
  88. package/dist/index.web.d.ts +13 -0
  89. package/dist/index.web.js +13 -0
  90. package/dist/labels.d.ts +202 -0
  91. package/dist/labels.js +451 -0
  92. package/dist/loadedCamera.d.ts +64 -0
  93. package/dist/loadedCamera.js +56 -0
  94. package/dist/locationStore.d.ts +16 -0
  95. package/dist/locationStore.js +132 -0
  96. package/dist/mapRef.d.ts +4 -0
  97. package/dist/mapRef.js +13 -0
  98. package/dist/moveMarkers.d.ts +27 -0
  99. package/dist/moveMarkers.js +22 -0
  100. package/dist/nativeCameraReports.d.ts +44 -0
  101. package/dist/nativeCameraReports.js +73 -0
  102. package/dist/plugin.d.ts +63 -0
  103. package/dist/plugin.js +2 -0
  104. package/dist/queryLayers.d.ts +19 -0
  105. package/dist/queryLayers.js +54 -0
  106. package/dist/shared-exports.d.ts +17 -0
  107. package/dist/shared-exports.js +13 -0
  108. package/dist/shared.d.ts +170 -0
  109. package/dist/shared.js +337 -0
  110. package/dist/sourceContext.d.ts +2 -0
  111. package/dist/sourceContext.js +9 -0
  112. package/dist/styleUrl.d.ts +19 -0
  113. package/dist/styleUrl.js +22 -0
  114. package/dist/testing.d.ts +41 -0
  115. package/dist/testing.js +80 -0
  116. package/dist/types.d.ts +348 -0
  117. package/dist/types.js +1 -0
  118. package/dist/useMarkerSlots.d.ts +6 -0
  119. package/dist/useMarkerSlots.js +56 -0
  120. package/dist/useUserLocation.d.ts +44 -0
  121. package/dist/useUserLocation.js +247 -0
  122. package/dist/zoom.d.ts +2 -0
  123. package/dist/zoom.js +10 -0
  124. package/metro.cjs +70 -0
  125. package/package.json +115 -4
package/NOTICE ADDED
@@ -0,0 +1,25 @@
1
+ ============================================================================
2
+ NOTICE
3
+ OxyHQ/GoWay — the @goway.to/sdk package
4
+ Copyright (c) 2026-present The Oxy Collective, Inc.
5
+ ============================================================================
6
+
7
+ This product is licensed under the Breathe License 1.0, license identifier
8
+ LicenseRef-Breathe-1.0. See the LICENSE file. Commercial terms are available
9
+ at https://github.com/OxyHQ/.github/blob/main/LICENSE-COMMERCIAL.md
10
+
11
+ Attribution under Section 3.1 of the Breathe License is required of
12
+ everyone, including paying commercial licensees, and cannot be waived.
13
+
14
+ ----------------------------------------------------------------------------
15
+ THIRD PARTY COMPONENTS
16
+ ----------------------------------------------------------------------------
17
+
18
+ This repository contains no vendored third party source. Its dependencies
19
+ are resolved and installed by the package manager and remain governed by
20
+ their own licenses.
21
+
22
+ This work must not take on a GPL or AGPL dependency that is linked in
23
+ process: the Breathe License is not compatible with either, in either
24
+ direction. If you are adding one, stop and read
25
+ https://github.com/OxyHQ/.github/blob/main/licensing/README.md
package/README.md CHANGED
@@ -1,3 +1,60 @@
1
- # Temporary Holding Version
1
+ # `@goway.to/maps`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ GoWay's maps SDK for React and React Native: one declarative map for the web
4
+ (MapLibre GL JS) and iOS/Android (MapLibre Native), drawn in GoWay's styles.
5
+
6
+ ```bash
7
+ bun add @goway.to/maps @goway.to/sdk maplibre-gl @maplibre/maplibre-react-native
8
+ ```
9
+
10
+ ```tsx
11
+ import { MapView, Markers, UserLocation, type MapViewRef } from '@goway.to/maps';
12
+
13
+ <MapView
14
+ ref={mapRef}
15
+ mapStyle="goway://styles/standard" // the default; day in light mode, night in dark
16
+ camera={{ bounds: searchedArea }} // framed whenever its VALUE changes
17
+ onCameraChanged={({ bounds, source, isFinal }) => …}
18
+ onPress={({ coordinate, label }) => …}
19
+ >
20
+ <Markers data={homes} selectedId={selected} cluster onPress={(home) => …} />
21
+ <UserLocation />
22
+ </MapView>
23
+ ```
24
+
25
+ - **Markers** are Bloom's (`@oxy.so/bloom/map-marker`) on both platforms, and
26
+ cluster with Mapbox's source options (`cluster`, `clusterRadius`,
27
+ `clusterMaxZoom`).
28
+ - **The camera** opens on `initialViewState` and follows `camera` by value, so
29
+ a re-render never moves it under a reader who has panned; the ref
30
+ (`fitBounds`, `moveTo`, …) is for event handlers.
31
+ - **User location** is drawn only against a permission already granted;
32
+ `useUserLocation()` asks, from a tap.
33
+ - Every map carries the GoWay mark and the data credit, with no prop to turn
34
+ either off.
35
+
36
+ ## Setup
37
+
38
+ - **Native**: add the config plugin (`plugins: ['@goway.to/maps']` in
39
+ `app.config.js`) and make a development or store build — MapLibre Native is
40
+ not in Expo Go.
41
+ - **Web with Metro (Expo)**: wrap the Metro config, which serves MapLibre's
42
+ worker from your origin (`maplibre-gl` 6 starts it by URL), and gitignore
43
+ `public/vendor/`:
44
+
45
+ ```js
46
+ const { withGoWayMaps } = require('@goway.to/maps/metro');
47
+ module.exports = withGoWayMaps(getDefaultConfig(__dirname));
48
+ ```
49
+
50
+ - Render inside Bloom's `BloomProvider`: the map follows its theme and locale.
51
+
52
+ ## Entry points
53
+
54
+ | import | what |
55
+ | --- | --- |
56
+ | `@goway.to/maps` | `MapView`, `Markers`, `GeoJSONSource`, `RasterDEMSource`, `Layer`, `UserLocation`, `MapPlugin`, `useMap`, `useUserLocation` |
57
+ | `@goway.to/maps/plugin` | the engine-plugin protocol and the guards every coordinate crossing into an engine goes through (no React) |
58
+ | `@goway.to/maps/testing` | a fake engine that drives the map's real camera reports, for testing a screen's camera logic |
59
+ | `@goway.to/maps/brand` | the GoWay mark as geometry |
60
+ | `@goway.to/maps/metro`, `@goway.to/maps/app.plugin.js` | the Metro helper and the Expo config plugin |
package/app.plugin.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `@goway.to/maps` Expo config plugin: adds MapLibre Native to a development
3
+ * or store build (it is absent from Expo Go).
4
+ *
5
+ * // app.config.js
6
+ * plugins: ['@goway.to/maps']
7
+ *
8
+ * It applies `@maplibre/maplibre-react-native`'s own plugin, so an app names
9
+ * one map package in its config, as with `@rnmapbox/maps`. The location usage
10
+ * strings stay the app's (`expo-location`'s plugin): only the app knows why it
11
+ * asks.
12
+ */
13
+ // The package exports its plugin as an ES module's default.
14
+ const loaded = require('@maplibre/maplibre-react-native/app.plugin.js');
15
+ const maplibre = typeof loaded === 'function' ? loaded : loaded.default;
16
+
17
+ module.exports = function withGoWayMaps(config, options = {}) {
18
+ return maplibre(config, options);
19
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `MapView`'s `camera` target, web — framed whenever its VALUE changes.
3
+ *
4
+ * The camera is an imperative object behind maplibre-gl's `fitBounds`, and a
5
+ * target that changes because the app's state changed — a place picked, a URL
6
+ * arriving, back and forward — comes with no event this map handles. So the
7
+ * one binding below applies it, keyed on the four numbers of the box (or the
8
+ * point and zoom), never on the object: a re-render with an equal target does
9
+ * nothing, and the map never moves under a reader who has panned.
10
+ *
11
+ * The first target is JUMPED to — the map opens on it. A later change
12
+ * animates over `duration`. An engine rebuilt for a new style keeps the
13
+ * camera it had, so an unchanged target is not applied to it again: changing
14
+ * the map's look never moves the map.
15
+ */
16
+ import { type RefObject } from 'react';
17
+ import type { Map as MapLibreMap } from 'maplibre-gl';
18
+ import type { MapCameraTarget } from './types';
19
+ export declare function CameraBinding({ map, target, applied, }: {
20
+ map: MapLibreMap;
21
+ target: MapCameraTarget;
22
+ /** The target last applied, on ANY engine of this map — owned by `MapView`, which outlives them. */
23
+ applied: RefObject<string | null>;
24
+ }): null;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `MapView`'s `camera` target, web — framed whenever its VALUE changes.
3
+ *
4
+ * The camera is an imperative object behind maplibre-gl's `fitBounds`, and a
5
+ * target that changes because the app's state changed — a place picked, a URL
6
+ * arriving, back and forward — comes with no event this map handles. So the
7
+ * one binding below applies it, keyed on the four numbers of the box (or the
8
+ * point and zoom), never on the object: a re-render with an equal target does
9
+ * nothing, and the map never moves under a reader who has panned.
10
+ *
11
+ * The first target is JUMPED to — the map opens on it. A later change
12
+ * animates over `duration`. An engine rebuilt for a new style keeps the
13
+ * camera it had, so an unchanged target is not applied to it again: changing
14
+ * the map's look never moves the map.
15
+ */
16
+ import { useLayoutEffect } from 'react';
17
+ import { cameraTargetKey } from './cameraTarget';
18
+ import { createWebMapApi } from './commands.web';
19
+ export function CameraBinding({ map, target, applied, }) {
20
+ const { bounds, center, zoom, padding, maxZoom, duration } = target;
21
+ const key = cameraTargetKey({ bounds, center, zoom });
22
+ // Binding: the engine's camera follows the VALUE of `camera` (`key`).
23
+ // biome-ignore lint/correctness/useExhaustiveDependencies: `key` is the value the target stands for; a new object with the same numbers must not move the camera
24
+ useLayoutEffect(() => {
25
+ if (key === null || key === applied.current)
26
+ return;
27
+ const first = applied.current === null;
28
+ applied.current = key;
29
+ const api = createWebMapApi(() => map);
30
+ const time = first ? 0 : duration;
31
+ if (bounds) {
32
+ api.fitBounds(bounds, {
33
+ ...(padding !== undefined ? { padding } : {}),
34
+ ...(maxZoom !== undefined ? { maxZoom } : {}),
35
+ ...(time !== undefined ? { duration: time } : {}),
36
+ });
37
+ }
38
+ else if (center) {
39
+ api.moveTo(center, {
40
+ ...(zoom !== undefined ? { zoom } : {}),
41
+ ...(time !== undefined ? { duration: time } : {}),
42
+ });
43
+ }
44
+ }, [map, key]);
45
+ return null;
46
+ }
@@ -0,0 +1,8 @@
1
+ import type { MapMarkerItem } from './types';
2
+ export interface DefaultMapMarkerProps {
3
+ item: MapMarkerItem;
4
+ onPress?: () => void;
5
+ }
6
+ declare function DefaultMapMarkerComponent({ item, onPress }: DefaultMapMarkerProps): import("react").JSX.Element;
7
+ export declare const DefaultMapMarker: import("react").MemoExoticComponent<typeof DefaultMapMarkerComponent>;
8
+ export {};
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * The marker body both forks draw when a caller does not pass `renderMarker`.
4
+ *
5
+ * These are Bloom components, not GoWay ones: `@oxy.so/bloom/map-marker`
6
+ * already ships the pill and the cluster bubble with their `default` /
7
+ * `active` / `visited` states, their press affordance and their web focus
8
+ * ring, so a local marker would be a second, worse copy that drifts from the
9
+ * design system at the first token change.
10
+ *
11
+ * Because it is ordinary Bloom UI on both platforms (native positions it with
12
+ * MapLibre's `<Marker>`, web with a DOM `maplibregl.Marker` it is portalled
13
+ * into), a marker looks identical on iOS, Android and the web.
14
+ */
15
+ import { memo } from 'react';
16
+ import { MapClusterMarker, MapPriceMarker } from '@oxy.so/bloom/map-marker';
17
+ function DefaultMapMarkerComponent({ item, onPress }) {
18
+ if (item.kind === 'cluster') {
19
+ return (_jsx(MapClusterMarker, { count: item.cluster.count, state: item.state, onPress: onPress, testID: `map-cluster-${item.cluster.id}` }));
20
+ }
21
+ const { marker } = item;
22
+ return (_jsx(MapPriceMarker
23
+ // Bloom's pill is a labelled marker; the label happens to be a price in
24
+ // its first consumer. GoWay passes a place name, a category or nothing.
25
+ , {
26
+ // Bloom's pill is a labelled marker; the label happens to be a price in
27
+ // its first consumer. GoWay passes a place name, a category or nothing.
28
+ price: marker.label ?? '•', state: item.state, size: marker.label ? 'default' : 'compact', onPress: onPress, accessibilityLabel: marker.accessibilityLabel ?? marker.label ?? 'Map marker', testID: `map-marker-${marker.id}` }));
29
+ }
30
+ export const DefaultMapMarker = memo(DefaultMapMarkerComponent);
@@ -0,0 +1,2 @@
1
+ import type { GeoJSONSourceProps } from './types';
2
+ export declare function GeoJSONSource({ id, data, cluster, clusterRadius, clusterMaxZoom, clusterMinPoints, clusterProperties, children, }: GeoJSONSourceProps): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { GeoJSONSource as NativeGeoJSONSource } from '@maplibre/maplibre-react-native';
3
+ import { isDrawableGeoJSON, reportMapDefect } from './shared';
4
+ import { SourceIdContext } from './sourceContext';
5
+ const EMPTY = { type: 'FeatureCollection', features: [] };
6
+ export function GeoJSONSource({ id, data, cluster, clusterRadius, clusterMaxZoom, clusterMinPoints, clusterProperties, children, }) {
7
+ // Raw GeoJSON fails SILENTLY rather than loudly: refused here, and said so.
8
+ const drawable = isDrawableGeoJSON(data);
9
+ if (!drawable) {
10
+ reportMapDefect(`source:${id}`, `Dropped the data of source "${id}": it is not drawable GeoJSON.`);
11
+ }
12
+ return (_jsx(NativeGeoJSONSource, { id: id, data: drawable ? data : EMPTY, ...(cluster ? { cluster: true } : {}), ...(clusterRadius !== undefined ? { clusterRadius } : {}), ...(clusterMaxZoom !== undefined ? { clusterMaxZoom } : {}), ...(clusterMinPoints !== undefined ? { clusterMinPoints } : {}), ...(clusterProperties
13
+ ? {
14
+ clusterProperties: clusterProperties,
15
+ }
16
+ : {}), children: _jsx(SourceIdContext.Provider, { value: id, children: children }) }));
17
+ }
@@ -0,0 +1,2 @@
1
+ import type { GeoJSONSourceProps } from './types';
2
+ export declare function GeoJSONSource(props: GeoJSONSourceProps): import("react").JSX.Element | null;
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `<GeoJSONSource>`, web — a GeoJSON source on the engine, with `<Layer>`
4
+ * children drawing it.
5
+ *
6
+ * Two bindings, in two files, because a source has two lives: it is ADDED
7
+ * once per engine (here, with its clustering options, which maplibre-gl takes
8
+ * only at creation) and its DATA changes many times (`SourceData.web.tsx`,
9
+ * `setData`). Removing and re-adding it for every new data set would take its
10
+ * layers with it.
11
+ *
12
+ * The layers render only once the source exists: React binds children before
13
+ * their parent, and a layer added before its source throws.
14
+ */
15
+ import { useContext, useLayoutEffect, useState } from 'react';
16
+ import { WebEngineContext } from './engineContext.web';
17
+ import { isDrawableGeoJSON, reportMapDefect, runEngineCommand } from './shared';
18
+ import { SourceIdContext } from './sourceContext';
19
+ import { SourceData } from './SourceData.web';
20
+ const EMPTY = { type: 'FeatureCollection', features: [] };
21
+ export function GeoJSONSource(props) {
22
+ const engine = useContext(WebEngineContext);
23
+ return engine ? _jsx(GeoJSONSourceBinding, { map: engine.map, ...props }) : null;
24
+ }
25
+ function GeoJSONSourceBinding({ map, id, data, cluster, clusterRadius, clusterMaxZoom, clusterMinPoints, clusterProperties, children, }) {
26
+ const [added, setAdded] = useState(null);
27
+ // Binding: the source `id` exists on the engine, with its creation options.
28
+ useLayoutEffect(() => {
29
+ const spec = {
30
+ type: 'geojson',
31
+ data: EMPTY,
32
+ ...(cluster ? { cluster: true } : {}),
33
+ ...(clusterRadius !== undefined ? { clusterRadius } : {}),
34
+ ...(clusterMaxZoom !== undefined ? { clusterMaxZoom } : {}),
35
+ ...(clusterMinPoints !== undefined ? { clusterMinPoints } : {}),
36
+ ...(clusterProperties
37
+ ? {
38
+ clusterProperties: clusterProperties,
39
+ }
40
+ : {}),
41
+ };
42
+ if (!runEngineCommand('addSource', () => map.addSource(id, spec)))
43
+ return;
44
+ setAdded(map);
45
+ return () => {
46
+ setAdded(null);
47
+ runEngineCommand('removeSource', () => {
48
+ // A layer still drawing the source would make the removal throw.
49
+ for (const layer of map.getStyle()?.layers ?? []) {
50
+ if ('source' in layer && layer.source === id)
51
+ map.removeLayer(layer.id);
52
+ }
53
+ if (map.getSource(id))
54
+ map.removeSource(id);
55
+ });
56
+ };
57
+ }, [map, id, cluster, clusterRadius, clusterMaxZoom, clusterMinPoints, clusterProperties]);
58
+ if (added !== map)
59
+ return null;
60
+ // Raw GeoJSON fails SILENTLY rather than loudly: a NaN vertex tiles to
61
+ // nothing and the line the reader asked for is simply absent. Refused here,
62
+ // and said so.
63
+ const drawable = isDrawableGeoJSON(data);
64
+ if (!drawable) {
65
+ reportMapDefect(`source:${id}`, `Dropped the data of source "${id}": it is not drawable GeoJSON.`);
66
+ }
67
+ return (_jsxs(SourceIdContext.Provider, { value: id, children: [_jsx(SourceData, { map: map, id: id, data: drawable ? data : EMPTY }), children] }));
68
+ }
@@ -0,0 +1,7 @@
1
+ import type { Map as MapLibreMap } from 'maplibre-gl';
2
+ import type { MapInteractionOptions } from './types';
3
+ export declare function InteractionBinding({ map, interaction, maxPitch, }: {
4
+ map: MapLibreMap;
5
+ interaction: MapInteractionOptions | undefined;
6
+ maxPitch: number;
7
+ }): null;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The engine's gesture handlers and pitch ceiling, bound to `interaction` and
3
+ * `maxPitch`.
4
+ *
5
+ * maplibre-gl toggles gestures with imperative `enable()`/`disable()` calls on
6
+ * its handlers, so this component holds the one binding that keeps them equal
7
+ * to the props. Every flag is independent, on web as on native: `rotate` and
8
+ * `pitch` are separate axes even though MapLibre drives both from one desktop
9
+ * handler (`dragAxes.web.ts`).
10
+ */
11
+ import { useLayoutEffect } from 'react';
12
+ import { applyDragAxes } from './dragAxes.web';
13
+ import { resolveInteraction, runEngineCommand } from './shared';
14
+ export function InteractionBinding({ map, interaction, maxPitch, }) {
15
+ const { pan, zoom, rotate, pitch } = resolveInteraction(interaction);
16
+ // Binding: the engine's gesture handlers follow `interaction` and `maxPitch`.
17
+ useLayoutEffect(() => {
18
+ const toggle = (handler, on) => on ? handler.enable() : handler.disable();
19
+ runEngineCommand('interaction', () => {
20
+ toggle(map.dragPan, pan);
21
+ toggle(map.scrollZoom, zoom);
22
+ toggle(map.touchZoomRotate, zoom);
23
+ toggle(map.doubleClickZoom, zoom);
24
+ if (rotate)
25
+ map.touchZoomRotate.enableRotation();
26
+ else
27
+ map.touchZoomRotate.disableRotation();
28
+ applyDragAxes(map.dragRotate, { rotate, pitch });
29
+ toggle(map.touchPitch, pitch);
30
+ // The backstop: with pitch off no path can tilt the camera.
31
+ map.setMaxPitch(pitch ? maxPitch : 0);
32
+ });
33
+ }, [map, pan, zoom, rotate, pitch, maxPitch]);
34
+ return null;
35
+ }
@@ -0,0 +1,2 @@
1
+ import type { LayerProps } from './types';
2
+ export declare function Layer({ id, type, paint, layout, filter, beforeId, minzoom, maxzoom }: LayerProps): import("react").JSX.Element;
package/dist/Layer.js ADDED
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Layer as NativeLayer } from '@maplibre/maplibre-react-native';
3
+ import { useSourceId } from './sourceContext';
4
+ export function Layer({ id, type, paint, layout, filter, beforeId, minzoom, maxzoom }) {
5
+ const source = useSourceId();
6
+ const props = {
7
+ id,
8
+ type,
9
+ source,
10
+ ...(paint ? { paint } : {}),
11
+ ...(layout ? { layout } : {}),
12
+ ...(filter ? { filter } : {}),
13
+ ...(beforeId ? { beforeId } : {}),
14
+ ...(minzoom !== undefined ? { minzoom } : {}),
15
+ ...(maxzoom !== undefined ? { maxzoom } : {}),
16
+ };
17
+ // MapLibre Native types a layer as a union keyed by `type`; a `<Layer>`
18
+ // takes its type as a value and its properties as plain style-spec names,
19
+ // which the engine validates itself.
20
+ return _jsx(NativeLayer, { ...props });
21
+ }
@@ -0,0 +1,2 @@
1
+ import type { LayerProps } from './types';
2
+ export declare function Layer(props: LayerProps): import("react").JSX.Element | null;
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * `<Layer>`, web — a style layer drawing the source around it.
4
+ *
5
+ * Added once per engine with what maplibre-gl fixes at creation (its id,
6
+ * type, source and place in the stack); its paint, layout and filter are
7
+ * their own binding (`LayerStyle.web.tsx`), so a new colour restyles the layer
8
+ * instead of removing and re-adding it.
9
+ */
10
+ import { useContext, useLayoutEffect, useState } from 'react';
11
+ import { WebEngineContext } from './engineContext.web';
12
+ import { LayerStyle } from './LayerStyle.web';
13
+ import { runEngineCommand } from './shared';
14
+ import { useSourceId } from './sourceContext';
15
+ export function Layer(props) {
16
+ const engine = useContext(WebEngineContext);
17
+ const source = useSourceId();
18
+ return engine ? _jsx(LayerBinding, { map: engine.map, source: source, ...props }) : null;
19
+ }
20
+ function LayerBinding({ map, source, id, type, beforeId, minzoom, maxzoom, paint, layout, filter, }) {
21
+ const [added, setAdded] = useState(null);
22
+ // Binding: the layer `id` exists on the engine, over `source`, under `beforeId`.
23
+ useLayoutEffect(() => {
24
+ const spec = {
25
+ id,
26
+ type,
27
+ source,
28
+ ...(minzoom !== undefined ? { minzoom } : {}),
29
+ ...(maxzoom !== undefined ? { maxzoom } : {}),
30
+ };
31
+ // Under `beforeId` only when the style has it: a style that makes no such
32
+ // promise gets the layer on top rather than an exception.
33
+ const before = beforeId && map.getLayer(beforeId) ? beforeId : undefined;
34
+ if (!runEngineCommand('addLayer', () => map.addLayer(spec, before)))
35
+ return;
36
+ setAdded(map);
37
+ return () => {
38
+ setAdded(null);
39
+ runEngineCommand('removeLayer', () => {
40
+ if (map.getLayer(id))
41
+ map.removeLayer(id);
42
+ });
43
+ };
44
+ }, [map, source, id, type, beforeId, minzoom, maxzoom]);
45
+ return added === map ? (_jsx(LayerStyle, { map: map, id: id, paint: paint, layout: layout, filter: filter })) : null;
46
+ }
@@ -0,0 +1,10 @@
1
+ import type { Map as MapLibreMap } from 'maplibre-gl';
2
+ type Properties = Readonly<Record<string, unknown>>;
3
+ export declare function LayerStyle({ map, id, paint, layout, filter, }: {
4
+ map: MapLibreMap;
5
+ id: string;
6
+ paint?: Properties;
7
+ layout?: Properties;
8
+ filter?: readonly unknown[];
9
+ }): null;
10
+ export {};
@@ -0,0 +1,45 @@
1
+ /**
2
+ * A layer's paint, layout and filter, bound to its props with maplibre-gl's
3
+ * imperative setters (see `Layer.web.tsx` for why it is its own binding).
4
+ * A property the props drop is reset to the style default.
5
+ */
6
+ import { useLayoutEffect, useRef } from 'react';
7
+ import { runEngineCommand } from './shared';
8
+ const NONE = {};
9
+ export function LayerStyle({ map, id, paint = NONE, layout = NONE, filter, }) {
10
+ // What this binding last set on THIS engine's layer; a rebuilt engine's
11
+ // layer starts from the style's defaults.
12
+ const applied = useRef({
13
+ map: null,
14
+ paint: NONE,
15
+ layout: NONE,
16
+ });
17
+ // Binding: the layer's paint, layout and filter follow the props.
18
+ useLayoutEffect(() => {
19
+ const previous = applied.current.map === map ? applied.current : { map, paint: NONE, layout: NONE };
20
+ runEngineCommand('setLayerStyle', () => {
21
+ for (const key of new Set([...Object.keys(previous.paint), ...Object.keys(paint)])) {
22
+ if (previous.paint[key] !== paint[key])
23
+ setPaint(map, id, key, paint[key]);
24
+ }
25
+ for (const key of new Set([...Object.keys(previous.layout), ...Object.keys(layout)])) {
26
+ if (previous.layout[key] !== layout[key])
27
+ setLayout(map, id, key, layout[key]);
28
+ }
29
+ map.setFilter(id, (filter ?? null));
30
+ });
31
+ applied.current = { map, paint, layout };
32
+ }, [map, id, paint, layout, filter]);
33
+ return null;
34
+ }
35
+ /**
36
+ * maplibre-gl types a property NAME by the layer type it belongs to; a
37
+ * `<Layer>` names its properties in plain style-spec strings, which the
38
+ * engine validates itself (an unknown name is reported, not thrown).
39
+ */
40
+ function setPaint(map, id, name, value) {
41
+ map.setPaintProperty.call(map, id, name, value);
42
+ }
43
+ function setLayout(map, id, name, value) {
44
+ map.setLayoutProperty.call(map, id, name, value);
45
+ }
@@ -0,0 +1,5 @@
1
+ import type { Map as MapLibreMap } from 'maplibre-gl';
2
+ export declare function LightBinding({ map, light, }: {
3
+ map: MapLibreMap;
4
+ light: Readonly<Record<string, unknown>> | null;
5
+ }): null;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The style's light, bound to the `light` prop.
3
+ *
4
+ * A light is a property of the loaded style, and maplibre-gl sets it with an
5
+ * imperative `setLight` — so this component holds the one binding that keeps
6
+ * the engine's light equal to the prop. `null` leaves the document's own
7
+ * light alone. maplibre-gl diffs the light itself and eases the change.
8
+ */
9
+ import { useLayoutEffect } from 'react';
10
+ import { runEngineCommand } from './shared';
11
+ export function LightBinding({ map, light, }) {
12
+ // Binding: the engine's light follows `light` (an imperative style setter).
13
+ useLayoutEffect(() => {
14
+ if (!light)
15
+ return;
16
+ runEngineCommand('setLight', () => map.setLight(light));
17
+ }, [map, light]);
18
+ return null;
19
+ }
@@ -0,0 +1,27 @@
1
+ import { MAP_CREDIT_CLEARANCE } from './creditRise';
2
+ import type { MapCreditDisclosure, MapCreditLink } from './types';
3
+ /**
4
+ * How much of the bottom-right the credit occupies above the bottom edge when
5
+ * it is one line (`creditRise.ts`). Bottom-right chrome a screen floats there
6
+ * starts above it and rises by the credit's measured extra height
7
+ * (`creditHeight.ts`), so the two never overlap.
8
+ */
9
+ export { MAP_CREDIT_CLEARANCE };
10
+ /**
11
+ * The credit GoWay's tiles oblige: OpenMapTiles' schema and OpenStreetMap's
12
+ * data. Always shown, before anything an app adds.
13
+ */
14
+ export declare const MAP_DATA_CREDITS: readonly MapCreditLink[];
15
+ /**
16
+ * @param extra - credits owed while something the app draws is on screen,
17
+ * after the basemap's own.
18
+ */
19
+ declare function MapAttributionComponent({ extra, disclosure, expanded, onToggle, }: {
20
+ extra?: readonly MapCreditLink[];
21
+ /** A line the credit carries while something it names is drawn, `null` otherwise. */
22
+ disclosure?: MapCreditDisclosure | null;
23
+ /** Spelled out, or folded into the ⓘ (`compactCredit.ts`). */
24
+ expanded: boolean;
25
+ onToggle: () => void;
26
+ }): import("react").JSX.Element;
27
+ export declare const MapAttribution: import("react").MemoExoticComponent<typeof MapAttributionComponent>;