@goway.to/maps 0.0.0-stage → 0.1.1
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 +36 -0
- package/LICENSE +676 -0
- package/NOTICE +25 -0
- package/README.md +59 -2
- package/app.plugin.js +19 -0
- package/dist/CameraBinding.web.d.ts +24 -0
- package/dist/CameraBinding.web.js +46 -0
- package/dist/DefaultMapMarker.d.ts +8 -0
- package/dist/DefaultMapMarker.js +30 -0
- package/dist/GeoJSONSource.d.ts +2 -0
- package/dist/GeoJSONSource.js +17 -0
- package/dist/GeoJSONSource.web.d.ts +2 -0
- package/dist/GeoJSONSource.web.js +68 -0
- package/dist/InteractionBinding.web.d.ts +7 -0
- package/dist/InteractionBinding.web.js +35 -0
- package/dist/Layer.d.ts +2 -0
- package/dist/Layer.js +21 -0
- package/dist/Layer.web.d.ts +2 -0
- package/dist/Layer.web.js +46 -0
- package/dist/LayerStyle.web.d.ts +10 -0
- package/dist/LayerStyle.web.js +45 -0
- package/dist/LightBinding.web.d.ts +5 -0
- package/dist/LightBinding.web.js +19 -0
- package/dist/MapAttribution.d.ts +27 -0
- package/dist/MapAttribution.js +149 -0
- package/dist/MapBrand.d.ts +22 -0
- package/dist/MapBrand.js +91 -0
- package/dist/MapErrorState.d.ts +9 -0
- package/dist/MapErrorState.js +58 -0
- package/dist/MapPlugin.d.ts +6 -0
- package/dist/MapPlugin.js +3 -0
- package/dist/MapPlugin.web.d.ts +2 -0
- package/dist/MapPlugin.web.js +43 -0
- package/dist/MapView.d.ts +2 -0
- package/dist/MapView.js +425 -0
- package/dist/MapView.web.d.ts +5 -0
- package/dist/MapView.web.js +418 -0
- package/dist/Marker.d.ts +13 -0
- package/dist/Marker.js +11 -0
- package/dist/Marker.web.d.ts +15 -0
- package/dist/Marker.web.js +51 -0
- package/dist/Markers.d.ts +2 -0
- package/dist/Markers.js +13 -0
- package/dist/Markers.web.d.ts +2 -0
- package/dist/Markers.web.js +13 -0
- package/dist/PluginProps.web.d.ts +7 -0
- package/dist/PluginProps.web.js +19 -0
- package/dist/RasterDEMSource.d.ts +2 -0
- package/dist/RasterDEMSource.js +7 -0
- package/dist/RasterDEMSource.web.d.ts +2 -0
- package/dist/RasterDEMSource.web.js +44 -0
- package/dist/SourceData.web.d.ts +6 -0
- package/dist/SourceData.web.js +14 -0
- package/dist/UserLocation.d.ts +2 -0
- package/dist/UserLocation.js +19 -0
- package/dist/UserLocation.web.d.ts +2 -0
- package/dist/UserLocation.web.js +21 -0
- package/dist/brand/BrandedChrome.d.ts +47 -0
- package/dist/brand/BrandedChrome.js +51 -0
- package/dist/brand/GowayLogo.d.ts +28 -0
- package/dist/brand/GowayLogo.js +49 -0
- package/dist/brand/artwork.d.ts +130 -0
- package/dist/brand/artwork.js +176 -0
- package/dist/brand/logoAccessibility.d.ts +26 -0
- package/dist/brand/logoAccessibility.js +7 -0
- package/dist/cameraReports.web.d.ts +50 -0
- package/dist/cameraReports.web.js +117 -0
- package/dist/cameraTarget.d.ts +14 -0
- package/dist/cameraTarget.js +8 -0
- package/dist/chromeLift.d.ts +15 -0
- package/dist/chromeLift.js +15 -0
- package/dist/clusters.d.ts +57 -0
- package/dist/clusters.js +102 -0
- package/dist/commands.web.d.ts +16 -0
- package/dist/commands.web.js +158 -0
- package/dist/compactCredit.d.ts +52 -0
- package/dist/compactCredit.js +40 -0
- package/dist/creditHeight.d.ts +3 -0
- package/dist/creditHeight.js +12 -0
- package/dist/creditLabels.d.ts +30 -0
- package/dist/creditLabels.js +30 -0
- package/dist/creditRise.d.ts +28 -0
- package/dist/creditRise.js +33 -0
- package/dist/dragAxes.web.d.ts +65 -0
- package/dist/dragAxes.web.js +70 -0
- package/dist/engineContext.web.d.ts +13 -0
- package/dist/engineContext.web.js +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/index.web.d.ts +13 -0
- package/dist/index.web.js +13 -0
- package/dist/labels.d.ts +202 -0
- package/dist/labels.js +451 -0
- package/dist/loadedCamera.d.ts +64 -0
- package/dist/loadedCamera.js +56 -0
- package/dist/locationStore.d.ts +16 -0
- package/dist/locationStore.js +132 -0
- package/dist/mapRef.d.ts +4 -0
- package/dist/mapRef.js +13 -0
- package/dist/moveMarkers.d.ts +27 -0
- package/dist/moveMarkers.js +22 -0
- package/dist/nativeCameraReports.d.ts +44 -0
- package/dist/nativeCameraReports.js +73 -0
- package/dist/plugin.d.ts +63 -0
- package/dist/plugin.js +2 -0
- package/dist/queryLayers.d.ts +19 -0
- package/dist/queryLayers.js +54 -0
- package/dist/shared-exports.d.ts +17 -0
- package/dist/shared-exports.js +13 -0
- package/dist/shared.d.ts +170 -0
- package/dist/shared.js +337 -0
- package/dist/sourceContext.d.ts +2 -0
- package/dist/sourceContext.js +9 -0
- package/dist/styleUrl.d.ts +19 -0
- package/dist/styleUrl.js +22 -0
- package/dist/testing.d.ts +41 -0
- package/dist/testing.js +80 -0
- package/dist/types.d.ts +348 -0
- package/dist/types.js +1 -0
- package/dist/useMarkerSlots.d.ts +6 -0
- package/dist/useMarkerSlots.js +56 -0
- package/dist/useUserLocation.d.ts +44 -0
- package/dist/useUserLocation.js +247 -0
- package/dist/zoom.d.ts +2 -0
- package/dist/zoom.js +10 -0
- package/metro.cjs +70 -0
- package/package.json +115 -4
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Map data credit — rendered by `MapView` on every map, identical on web and
|
|
4
|
+
* native, with no prop to turn it off.
|
|
5
|
+
*
|
|
6
|
+
* Both engines ship an attribution ornament of their own (maplibre-gl's
|
|
7
|
+
* `AttributionControl`, MapLibre Native's "i" button); `MapView` disables both
|
|
8
|
+
* and draws this instead, so the credit sits inside the app's layout (chrome
|
|
9
|
+
* can be positioned against it) and looks the same on both platforms.
|
|
10
|
+
*
|
|
11
|
+
* It POSITIONS itself. A credit pinned 4 px off the window edge disappears the
|
|
12
|
+
* moment anything parks at that edge — a half-sheet, a tab bar — and it
|
|
13
|
+
* disappears silently, which for a licence obligation is the worst way to
|
|
14
|
+
* fail. Reading Bloom's bottom-edge registry here means the credit rises above
|
|
15
|
+
* whatever is claimed without either knowing about the other.
|
|
16
|
+
*
|
|
17
|
+
* ## On the right, after the mark
|
|
18
|
+
*
|
|
19
|
+
* The GoWay mark (`MapBrand`) takes the bottom-left corner and the credit the
|
|
20
|
+
* bottom-right, starting after {@link MAP_BRAND_CLEARANCE} so a credit that
|
|
21
|
+
* wraps on a narrow phone never runs under the mark.
|
|
22
|
+
*
|
|
23
|
+
* ## Compact, as the OSMF guidelines allow
|
|
24
|
+
*
|
|
25
|
+
* The OSMF Attribution Guidelines
|
|
26
|
+
* (https://osmfoundation.org/wiki/Licence/Attribution_Guidelines) accept a
|
|
27
|
+
* credit that is visible when the map loads and then collapses — after a few
|
|
28
|
+
* seconds or once the reader interacts — into a control that shows it again.
|
|
29
|
+
* That is this component's two states (`compactCredit.ts` holds the rule and
|
|
30
|
+
* its test): spelled out, with an ⓘ at the end to fold it; or just the ⓘ, in
|
|
31
|
+
* the same corner, which opens it. The links stay links.
|
|
32
|
+
*
|
|
33
|
+
* ## A disclosure rides here
|
|
34
|
+
*
|
|
35
|
+
* While an app draws something the reader must not mistake for data (GoWay's
|
|
36
|
+
* simulated traffic), the credit says so: spelled out, its first line is the
|
|
37
|
+
* whole sentence; folded, a word sits beside the ⓘ. Labelled in BOTH states,
|
|
38
|
+
* for exactly as long as it is drawn.
|
|
39
|
+
*/
|
|
40
|
+
import { memo, useCallback } from 'react';
|
|
41
|
+
import { Linking, Pressable, StyleSheet, View, } from 'react-native';
|
|
42
|
+
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated';
|
|
43
|
+
import { localizedLabel } from '@goway.to/sdk';
|
|
44
|
+
import { useBloomLocale } from '@oxy.so/bloom/locale';
|
|
45
|
+
import { useBottomEdgeInset } from '@oxy.so/bloom/layout';
|
|
46
|
+
import { RiInformationLine } from '@oxy.so/bloom/icons/RiInformationLine';
|
|
47
|
+
import { useTheme } from '@oxy.so/bloom/theme';
|
|
48
|
+
import { Text } from '@oxy.so/bloom/typography';
|
|
49
|
+
import { useBrandedChrome } from './brand/BrandedChrome';
|
|
50
|
+
import { useMapChromeLift } from './chromeLift';
|
|
51
|
+
import { creditDisclosure } from './compactCredit';
|
|
52
|
+
import { useMapCreditHeight } from './creditHeight';
|
|
53
|
+
import { CREDIT_LABELS } from './creditLabels';
|
|
54
|
+
import { CREDIT_GAP, MAP_CREDIT_CLEARANCE } from './creditRise';
|
|
55
|
+
import { MAP_BRAND_CLEARANCE } from './MapBrand';
|
|
56
|
+
/** Breathing room between the credit and whatever is below it. */
|
|
57
|
+
const ATTRIBUTION_GAP = CREDIT_GAP;
|
|
58
|
+
/** From the left edge when no mark is drawn there: the plate's own right inset. */
|
|
59
|
+
const CREDIT_SIDE_INSET = 8;
|
|
60
|
+
/**
|
|
61
|
+
* How much of the bottom-right the credit occupies above the bottom edge when
|
|
62
|
+
* it is one line (`creditRise.ts`). Bottom-right chrome a screen floats there
|
|
63
|
+
* starts above it and rises by the credit's measured extra height
|
|
64
|
+
* (`creditHeight.ts`), so the two never overlap.
|
|
65
|
+
*/
|
|
66
|
+
export { MAP_CREDIT_CLEARANCE };
|
|
67
|
+
/**
|
|
68
|
+
* The credit GoWay's tiles oblige: OpenMapTiles' schema and OpenStreetMap's
|
|
69
|
+
* data. Always shown, before anything an app adds.
|
|
70
|
+
*/
|
|
71
|
+
export const MAP_DATA_CREDITS = [
|
|
72
|
+
{ label: '© OpenMapTiles', href: 'https://www.openmaptiles.org/' },
|
|
73
|
+
{ label: '© OpenStreetMap', href: 'https://www.openstreetmap.org/copyright' },
|
|
74
|
+
];
|
|
75
|
+
const NO_EXTRA = [];
|
|
76
|
+
/**
|
|
77
|
+
* @param extra - credits owed while something the app draws is on screen,
|
|
78
|
+
* after the basemap's own.
|
|
79
|
+
*/
|
|
80
|
+
function MapAttributionComponent({ extra = NO_EXTRA, disclosure = null, expanded, onToggle, }) {
|
|
81
|
+
const locale = useBloomLocale();
|
|
82
|
+
const theme = useTheme();
|
|
83
|
+
const toggleLabel = localizedLabel(expanded ? CREDIT_LABELS.hide : CREDIT_LABELS.show, locale ?? 'en');
|
|
84
|
+
const links = extra.length > 0 ? [...MAP_DATA_CREDITS, ...extra] : MAP_DATA_CREDITS;
|
|
85
|
+
const shown = creditDisclosure(expanded, disclosure);
|
|
86
|
+
const bottomEdge = useBottomEdgeInset();
|
|
87
|
+
const chromeLift = useMapChromeLift();
|
|
88
|
+
// The mark is not drawn while the app's own chrome carries it, so the
|
|
89
|
+
// credit takes the corner's full width.
|
|
90
|
+
const brandDrawn = !useBrandedChrome();
|
|
91
|
+
const heightOut = useMapCreditHeight();
|
|
92
|
+
const measure = useCallback((event) => {
|
|
93
|
+
heightOut?.set(withTiming(event.nativeEvent.layout.height, { duration: 160 }));
|
|
94
|
+
}, [heightOut]);
|
|
95
|
+
const open = useCallback((href) => {
|
|
96
|
+
void Linking.openURL(href).catch(() => {
|
|
97
|
+
// A link the platform will not open costs the tap, not the map.
|
|
98
|
+
});
|
|
99
|
+
}, []);
|
|
100
|
+
const secondary = { color: theme.colors.textSecondary };
|
|
101
|
+
const primary = { color: theme.colors.text };
|
|
102
|
+
return (_jsx(CreditBox, { lift: chromeLift, style: {
|
|
103
|
+
bottom: bottomEdge > 0 ? bottomEdge + ATTRIBUTION_GAP : ATTRIBUTION_GAP,
|
|
104
|
+
left: brandDrawn ? MAP_BRAND_CLEARANCE : CREDIT_SIDE_INSET,
|
|
105
|
+
}, children: _jsxs(View, { style: styles.plate, onLayout: heightOut ? measure : undefined, testID: "map-credit", children: [_jsx(View, { pointerEvents: "none", style: [StyleSheet.absoluteFill, styles.fill, { backgroundColor: theme.colors.card }] }), shown.line && disclosure ? (_jsx(Text, { variant: "caption-1-semibold", accessibilityLabel: `${shown.line}. ${disclosure.detail}`, style: [styles.disclosure, primary], testID: "map-credit-simulated", children: shown.line })) : null, _jsxs(View, { style: styles.row, children: [expanded
|
|
106
|
+
? links.map((link) => (_jsx(Pressable, { accessibilityRole: "link", accessibilityLabel: link.label, onPress: () => open(link.href), hitSlop: 6, children: _jsx(Text, { variant: "caption-1-regular", style: [styles.link, secondary], children: link.label }) }, `${link.label}|${link.href}`)))
|
|
107
|
+
: null, _jsxs(Pressable, { accessibilityRole: "button", accessibilityLabel: shown.tag && disclosure ? `${disclosure.text}. ${toggleLabel}` : toggleLabel, "aria-expanded": expanded, onPress: onToggle, hitSlop: 10, style: ({ pressed }) => [styles.toggle, pressed ? styles.pressed : null], testID: "map-credit-toggle", children: [shown.tag ? (_jsx(Text, { variant: "caption-1-semibold", style: primary, testID: "map-credit-simulated-short", children: shown.tag })) : null, _jsx(RiInformationLine, { width: 18, height: 18, fill: theme.colors.textSecondary })] })] })] }) }));
|
|
108
|
+
}
|
|
109
|
+
function CreditBox({ lift, style, children, }) {
|
|
110
|
+
if (!lift) {
|
|
111
|
+
return (_jsx(View, { pointerEvents: "box-none", style: [styles.box, style], children: children }));
|
|
112
|
+
}
|
|
113
|
+
return (_jsx(LiftedCreditBox, { lift: lift, style: style, children: children }));
|
|
114
|
+
}
|
|
115
|
+
function LiftedCreditBox({ lift, style, children, }) {
|
|
116
|
+
const { lift: rise, opacity } = lift;
|
|
117
|
+
const animated = useAnimatedStyle(() => ({ transform: [{ translateY: -rise.value }], opacity: opacity.value }), [rise, opacity]);
|
|
118
|
+
return (_jsx(Animated.View, { pointerEvents: "box-none", style: [styles.box, style, animated], children: children }));
|
|
119
|
+
}
|
|
120
|
+
export const MapAttribution = memo(MapAttributionComponent);
|
|
121
|
+
const styles = StyleSheet.create({
|
|
122
|
+
box: { position: 'absolute', right: 8, alignItems: 'flex-end' },
|
|
123
|
+
plate: {
|
|
124
|
+
alignItems: 'flex-end',
|
|
125
|
+
gap: 2,
|
|
126
|
+
borderRadius: 8,
|
|
127
|
+
overflow: 'hidden',
|
|
128
|
+
paddingHorizontal: 8,
|
|
129
|
+
paddingVertical: 2,
|
|
130
|
+
},
|
|
131
|
+
fill: { opacity: 0.8 },
|
|
132
|
+
disclosure: { textAlign: 'right' },
|
|
133
|
+
row: {
|
|
134
|
+
flexDirection: 'row',
|
|
135
|
+
flexWrap: 'wrap',
|
|
136
|
+
alignItems: 'center',
|
|
137
|
+
justifyContent: 'flex-end',
|
|
138
|
+
gap: 4,
|
|
139
|
+
},
|
|
140
|
+
link: { textDecorationLine: 'underline' },
|
|
141
|
+
toggle: {
|
|
142
|
+
flexDirection: 'row',
|
|
143
|
+
alignItems: 'center',
|
|
144
|
+
justifyContent: 'center',
|
|
145
|
+
gap: 4,
|
|
146
|
+
borderRadius: 9999,
|
|
147
|
+
},
|
|
148
|
+
pressed: { opacity: 0.7 },
|
|
149
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How wide the logo draws on the map.
|
|
3
|
+
*
|
|
4
|
+
* 76px puts the lockup at 48px tall, which is the smallest it stays legible at
|
|
5
|
+
* — the wordmark's two rows mean its cap height is roughly a fifth of its
|
|
6
|
+
* width, so it dies much earlier than a single-line logo would. Google's
|
|
7
|
+
* bottom-left badge is 66px wide and one row tall; this is the two-row
|
|
8
|
+
* equivalent, not a bigger claim on the canvas.
|
|
9
|
+
*/
|
|
10
|
+
export declare const MAP_BRAND_WIDTH = 76;
|
|
11
|
+
/**
|
|
12
|
+
* Where the credit has to start so it cannot run under the mark.
|
|
13
|
+
*
|
|
14
|
+
* Exported because `MapAttribution` is the one that has to respect it, and the
|
|
15
|
+
* two must not each hold a private copy of the other's geometry.
|
|
16
|
+
*/
|
|
17
|
+
export declare const MAP_BRAND_CLEARANCE: number;
|
|
18
|
+
/** How tall the mark draws. Layout that has to clear it reads this. */
|
|
19
|
+
export declare const MAP_BRAND_HEIGHT: number;
|
|
20
|
+
declare function MapBrandComponent(): import("react").JSX.Element | null;
|
|
21
|
+
export declare const MapBrand: import("react").MemoExoticComponent<typeof MapBrandComponent>;
|
|
22
|
+
export {};
|
package/dist/MapBrand.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The GoWay mark on the map — bottom-left, on every map GoWay draws.
|
|
4
|
+
*
|
|
5
|
+
* Google's logo is in the bottom-left corner of every Google map, in their app,
|
|
6
|
+
* in a `<GoogleMap>` in somebody's React page and in a bare `<iframe>` of
|
|
7
|
+
* maps.google.com; Apple's wordmark is in the bottom-left corner of every Apple
|
|
8
|
+
* map. Neither is a decoration and neither is optional, because a map platform
|
|
9
|
+
* that is invisible inside other people's pages is a map platform nobody has
|
|
10
|
+
* heard of. This is GoWay's.
|
|
11
|
+
*
|
|
12
|
+
* ## Why `MapView` renders it and no screen can
|
|
13
|
+
*
|
|
14
|
+
* Exactly the reasoning in `MapAttribution` — a thing that must be on every map
|
|
15
|
+
* cannot be something a screen remembers to add. `MapView` renders both, both
|
|
16
|
+
* are bare components with no `enabled` prop, and no screen can turn either
|
|
17
|
+
* off — the same rule Mapbox keeps for its wordmark.
|
|
18
|
+
*
|
|
19
|
+
* One exception exists, and it is not a prop. A surface whose own chrome
|
|
20
|
+
* already shows the GoWay logo says so through `BrandedChromeProvider`, and
|
|
21
|
+
* the badge stands down. The direction is what keeps it safe — a surface opts
|
|
22
|
+
* IN to being branded already, so forgetting shows the badge rather than
|
|
23
|
+
* hiding it. See `brand/BrandedChrome.tsx`.
|
|
24
|
+
*
|
|
25
|
+
* ## Why there is no plate behind it
|
|
26
|
+
*
|
|
27
|
+
* Because the artwork already is one. GoWay's logo is a sticker: every letter
|
|
28
|
+
* carries a heavy `#004aad` outline, and that outline is what separates it from
|
|
29
|
+
* whatever it is standing on. It was checked rather than assumed — rendered at
|
|
30
|
+
* 90px over the eight colours that actually cover a GoWay map: light land `#f6f4eb`, built-up `#fef4df`, roads
|
|
31
|
+
* `#fefefe`, light water `#8ddbf6`, dark land `#34445b`, dark built-up
|
|
32
|
+
* `#45476e`, dark water `#1c347a`, and black. It reads on all eight, and it
|
|
33
|
+
* reads for two different reasons: on the light four the blue outline carries
|
|
34
|
+
* it, and on the dark four the outline recedes into the basemap and the bright
|
|
35
|
+
* `#acd0ff` and `#ffffff` counters carry it instead. A card behind it would add
|
|
36
|
+
* a rectangle to the map and buy nothing.
|
|
37
|
+
*
|
|
38
|
+
* Which also means it needs no light/dark variant. One drawing, both
|
|
39
|
+
* appearances — and no dependence on the map's appearance, which matters
|
|
40
|
+
* because the map's appearance and the app's theme are separate settings and
|
|
41
|
+
* this component can see only one of them.
|
|
42
|
+
*
|
|
43
|
+
* ## It is not a link, on purpose
|
|
44
|
+
*
|
|
45
|
+
* Inside somebody else's page it would be a click target the page did not
|
|
46
|
+
* ask for, and in GoWay's own app it would go where the reader already is.
|
|
47
|
+
*/
|
|
48
|
+
import { memo } from 'react';
|
|
49
|
+
import { View } from 'react-native';
|
|
50
|
+
import { useBottomEdgeInset } from '@oxy.so/bloom/layout';
|
|
51
|
+
import { useBrandedChrome } from './brand/BrandedChrome';
|
|
52
|
+
import { GowayLogo, gowayLogoHeight } from './brand/GowayLogo';
|
|
53
|
+
/**
|
|
54
|
+
* How wide the logo draws on the map.
|
|
55
|
+
*
|
|
56
|
+
* 76px puts the lockup at 48px tall, which is the smallest it stays legible at
|
|
57
|
+
* — the wordmark's two rows mean its cap height is roughly a fifth of its
|
|
58
|
+
* width, so it dies much earlier than a single-line logo would. Google's
|
|
59
|
+
* bottom-left badge is 66px wide and one row tall; this is the two-row
|
|
60
|
+
* equivalent, not a bigger claim on the canvas.
|
|
61
|
+
*/
|
|
62
|
+
export const MAP_BRAND_WIDTH = 76;
|
|
63
|
+
/** Breathing room between the mark and whatever is below it. */
|
|
64
|
+
const BRAND_GAP = 4;
|
|
65
|
+
/**
|
|
66
|
+
* Where the credit has to start so it cannot run under the mark.
|
|
67
|
+
*
|
|
68
|
+
* Exported because `MapAttribution` is the one that has to respect it, and the
|
|
69
|
+
* two must not each hold a private copy of the other's geometry.
|
|
70
|
+
*/
|
|
71
|
+
export const MAP_BRAND_CLEARANCE = MAP_BRAND_WIDTH + 16;
|
|
72
|
+
/** How tall the mark draws. Layout that has to clear it reads this. */
|
|
73
|
+
export const MAP_BRAND_HEIGHT = gowayLogoHeight(MAP_BRAND_WIDTH);
|
|
74
|
+
function MapBrandComponent() {
|
|
75
|
+
const bottomEdge = useBottomEdgeInset();
|
|
76
|
+
const chromeIsBranded = useBrandedChrome();
|
|
77
|
+
// A surface whose own chrome carries the logo above the map. Drawing it here
|
|
78
|
+
// too is the brand twice on one screen, the second copy on the cartography.
|
|
79
|
+
if (chromeIsBranded)
|
|
80
|
+
return null;
|
|
81
|
+
return (_jsx(View, { pointerEvents: "none",
|
|
82
|
+
// Same rule as the credit: a claim on the bottom edge already folds in
|
|
83
|
+
// the safe area, so the plain gap is added to it and not to the
|
|
84
|
+
// safe-area-aware one.
|
|
85
|
+
style: {
|
|
86
|
+
position: 'absolute',
|
|
87
|
+
left: 8,
|
|
88
|
+
bottom: bottomEdge > 0 ? bottomEdge + BRAND_GAP : BRAND_GAP,
|
|
89
|
+
}, testID: "goway-map-brand", children: _jsx(GowayLogo, { width: MAP_BRAND_WIDTH }) }));
|
|
90
|
+
}
|
|
91
|
+
export const MapBrand = memo(MapBrandComponent);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { MapError } from './types';
|
|
2
|
+
export interface MapErrorStateProps {
|
|
3
|
+
error: MapError;
|
|
4
|
+
onRetry?: () => void;
|
|
5
|
+
retryLabel?: string;
|
|
6
|
+
}
|
|
7
|
+
declare function MapErrorStateComponent({ error, onRetry, retryLabel }: MapErrorStateProps): import("react").JSX.Element;
|
|
8
|
+
export declare const MapErrorState: import("react").MemoExoticComponent<typeof MapErrorStateComponent>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The map's degraded state.
|
|
4
|
+
*
|
|
5
|
+
* A MapLibre canvas whose style or tiles never arrive renders a plain coloured
|
|
6
|
+
* rectangle and reports nothing — indistinguishable from an app that is
|
|
7
|
+
* simply slow, forever. So both forks watch for the two failures a hosted
|
|
8
|
+
* source can produce (the style document, and the tiles it points at) and
|
|
9
|
+
* cover the CANVAS — only the canvas — with this. Search, saved places, the sheet and every other piece of
|
|
10
|
+
* chrome stay mounted and usable above it, because they are siblings of the
|
|
11
|
+
* canvas, not children.
|
|
12
|
+
*/
|
|
13
|
+
import { memo } from 'react';
|
|
14
|
+
import { StyleSheet, View } from 'react-native';
|
|
15
|
+
import { Button } from '@oxy.so/bloom/button';
|
|
16
|
+
import { Text } from '@oxy.so/bloom/typography';
|
|
17
|
+
import { RiRefreshLine } from '@oxy.so/bloom/icons/RiRefreshLine';
|
|
18
|
+
import { RiErrorWarningLine } from '@oxy.so/bloom/icons/RiErrorWarningLine';
|
|
19
|
+
import { useTheme } from '@oxy.so/bloom/theme';
|
|
20
|
+
/** One sentence per reason — what happened, in the user's terms, not ours. */
|
|
21
|
+
const MESSAGES = {
|
|
22
|
+
style: {
|
|
23
|
+
title: 'Map unavailable',
|
|
24
|
+
body: "We couldn't load the map's design. Everything else still works.",
|
|
25
|
+
},
|
|
26
|
+
tiles: {
|
|
27
|
+
title: 'Map data unavailable',
|
|
28
|
+
body: "We couldn't reach the map data for this area. Check your connection and try again.",
|
|
29
|
+
},
|
|
30
|
+
unsupported: {
|
|
31
|
+
title: 'Map not supported here',
|
|
32
|
+
body: 'This build has no map renderer for the current platform.',
|
|
33
|
+
},
|
|
34
|
+
runtime: {
|
|
35
|
+
title: 'Map stopped',
|
|
36
|
+
body: 'The map ran into a problem. Reloading it usually fixes it.',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
function MapErrorStateComponent({ error, onRetry, retryLabel = 'Try again' }) {
|
|
40
|
+
const theme = useTheme();
|
|
41
|
+
const copy = MESSAGES[error.reason] ?? MESSAGES.runtime;
|
|
42
|
+
return (_jsxs(View, { accessibilityRole: "alert", style: [styles.cover, { backgroundColor: theme.colors.backgroundSecondary }], testID: "map-error-state", children: [_jsx(RiErrorWarningLine, { width: 28, height: 28, fill: theme.colors.textSecondary }), _jsx(Text, { variant: "headline-semibold", style: [styles.centred, { color: theme.colors.text }], children: copy.title }), _jsx(Text, { variant: "body-2-regular", style: [styles.centred, { color: theme.colors.textSecondary }], children: copy.body }), onRetry ? (_jsx(Button, { appearance: "outline", tone: "neutral", size: "sm", leadingIcon: RiRefreshLine, onPress: onRetry, children: retryLabel })) : null] }));
|
|
43
|
+
}
|
|
44
|
+
export const MapErrorState = memo(MapErrorStateComponent);
|
|
45
|
+
const styles = StyleSheet.create({
|
|
46
|
+
cover: {
|
|
47
|
+
position: 'absolute',
|
|
48
|
+
top: 0,
|
|
49
|
+
right: 0,
|
|
50
|
+
bottom: 0,
|
|
51
|
+
left: 0,
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
justifyContent: 'center',
|
|
54
|
+
gap: 12,
|
|
55
|
+
paddingHorizontal: 24,
|
|
56
|
+
},
|
|
57
|
+
centred: { textAlign: 'center' },
|
|
58
|
+
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<MapPlugin>`, native — nothing. MapLibre Native has no custom-layer API, so
|
|
3
|
+
* an engine plugin is a web-only extension (`plugin.ts`).
|
|
4
|
+
*/
|
|
5
|
+
import type { MapPluginProps } from './plugin';
|
|
6
|
+
export declare function MapPlugin<Props>(_props: MapPluginProps<Props>): null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* `<MapPlugin>`, web — the one place an app's engine plugin is bound to the
|
|
4
|
+
* engine (`plugin.ts`).
|
|
5
|
+
*
|
|
6
|
+
* Attached once per engine and plugin (this file's binding), handed every new
|
|
7
|
+
* `props` through `update` (`PluginProps.web.tsx`), detached on unmount or
|
|
8
|
+
* when the engine is rebuilt. While attached it is consulted for presses, the
|
|
9
|
+
* cursor and the errors of the sources it owns.
|
|
10
|
+
*/
|
|
11
|
+
import { useContext, useLayoutEffect, useState } from 'react';
|
|
12
|
+
import { WebEngineContext } from './engineContext.web';
|
|
13
|
+
import { PluginProps } from './PluginProps.web';
|
|
14
|
+
import { reportMapDefect } from './shared';
|
|
15
|
+
export function MapPlugin(props) {
|
|
16
|
+
const engine = useContext(WebEngineContext);
|
|
17
|
+
return engine ? _jsx(AttachedPlugin, { engine: engine, ...props }) : null;
|
|
18
|
+
}
|
|
19
|
+
function AttachedPlugin({ engine, plugin, props, }) {
|
|
20
|
+
const [attachment, setAttachment] = useState(null);
|
|
21
|
+
// Binding: `plugin` is attached to the engine for as long as both are.
|
|
22
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: a plugin is attached with the props of the render that attached it; later props reach it through `PluginProps`, never by re-attaching
|
|
23
|
+
useLayoutEffect(() => {
|
|
24
|
+
let attached = null;
|
|
25
|
+
try {
|
|
26
|
+
attached = plugin.attach(engine.map, props, { container: engine.container });
|
|
27
|
+
}
|
|
28
|
+
catch (cause) {
|
|
29
|
+
// A plugin that cannot attach costs the plugin, not the map.
|
|
30
|
+
reportMapDefect('plugin:attach', `A map plugin failed to attach: ${String(cause)}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const registered = attached;
|
|
34
|
+
engine.plugins.add(registered);
|
|
35
|
+
setAttachment({ handle: attached, props });
|
|
36
|
+
return () => {
|
|
37
|
+
engine.plugins.delete(registered);
|
|
38
|
+
setAttachment(null);
|
|
39
|
+
attached?.detach();
|
|
40
|
+
};
|
|
41
|
+
}, [engine, plugin]);
|
|
42
|
+
return attachment ? (_jsx(PluginProps, { handle: attachment.handle, attachedWith: attachment.props, props: props })) : null;
|
|
43
|
+
}
|