Skip to content

Commit 429b018

Browse files
committed
Updated how markers & callouts are rendered to maintain types + added default callout for title/description
1 parent bd510d6 commit 429b018

File tree

4 files changed

+121
-39
lines changed

4 files changed

+121
-39
lines changed
Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
import * as React from "react";
2-
import { Callout as MapCalloutComponent } from "./react-native-maps";
32
import type { MapCalloutProps as MapCalloutComponentProps } from "react-native-maps";
3+
import { Callout as MapCalloutComponent } from "./react-native-maps";
44

55
export interface MapCalloutProps
66
extends Omit<MapCalloutComponentProps, "tooltip"> {
77
showTooltip?: boolean;
88
}
99

10-
// Has to be a function named 'Callout' to be matched as a Callout component and not a custom view for marker
11-
// See: https://github.com/teovillanueva/react-native-web-maps/blob/81278079c6f26a707d915d69de9a00080c305957/packages/react-native-web-maps/src/components/marker.web.tsx#L79
12-
export function Callout({
13-
showTooltip,
14-
...rest
15-
}: React.PropsWithChildren<MapCalloutProps>) {
16-
return <MapCalloutComponent tooltip={!showTooltip} {...rest} />;
10+
/**
11+
* Renders nothing, serves as placeholder for props
12+
* Rendering exposed as function to avoid having an intermediary component that changes the type
13+
*
14+
* This is done because the underlying package has logic dependant on the type of child
15+
* See: https://github.com/teovillanueva/react-native-web-maps/blob/5f3d0ec7c24f789c3df30c1d6d7223e638ff5868/packages/react-native-web-maps/src/components/marker.web.tsx#L79
16+
*/
17+
const MapCallout: React.FC<React.PropsWithChildren<MapCalloutProps>> = () => {
18+
return null;
19+
};
20+
21+
export function renderCallout(props: MapCalloutProps, key: React.Key) {
22+
return (
23+
<MapCalloutComponent key={key} tooltip={!props.showTooltip} {...props} />
24+
);
1725
}
26+
27+
export default MapCallout;
Lines changed: 72 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import * as React from "react";
2-
import { Image, ImageSourcePropType } from "react-native";
2+
import {
3+
Image,
4+
ImageSourcePropType,
5+
View,
6+
StyleSheet,
7+
Text,
8+
} from "react-native";
39
import { Marker as MapMarkerComponent } from "./react-native-maps";
410
import type { MapMarkerProps as MapMarkerComponentProps } from "react-native-maps";
11+
import MapCallout, { renderCallout } from "./MapCallout";
512

613
export interface MapMarkerProps
714
extends Omit<MapMarkerComponentProps, "onPress" | "coordinate"> {
@@ -12,17 +19,55 @@ export interface MapMarkerProps
1219
onPress?: (latitude: number, longitude: number) => void;
1320
}
1421

15-
const MapMarker: React.FC<React.PropsWithChildren<MapMarkerProps>> = ({
16-
latitude,
17-
longitude,
18-
pinImage,
19-
pinImageSize = 50,
20-
onPress,
21-
children,
22-
...rest
23-
}) => {
22+
/**
23+
* Renders nothing, serves as placeholder for props
24+
* Rendering exposed as function to avoid having an intermediary component that changes the type
25+
*
26+
* This is done because the underlying package has logic dependant on the type of child
27+
*/
28+
const MapMarker: React.FC<React.PropsWithChildren<MapMarkerProps>> = () => {
29+
return null;
30+
};
31+
32+
export function renderMarker(
33+
{
34+
latitude,
35+
longitude,
36+
pinImage,
37+
pinImageSize = 50,
38+
onPress,
39+
children,
40+
title,
41+
description,
42+
...rest
43+
}: MapMarkerProps,
44+
key: React.Key
45+
) {
46+
const childrenArray = React.Children.toArray(children);
47+
48+
const calloutChildren = childrenArray.filter(
49+
(child) => (child as React.ReactElement).type === MapCallout
50+
);
51+
52+
const nonCalloutChildren = childrenArray.filter(
53+
(child) => (child as React.ReactElement).type !== MapCallout
54+
);
55+
56+
// Add default callout for title/description
57+
if (calloutChildren.length === 0 && (title || description)) {
58+
calloutChildren.push(
59+
<MapCallout showTooltip>
60+
<View>
61+
{title && <Text style={style.title}>{title}</Text>}
62+
{description && <Text style={style.description}>{description}</Text>}
63+
</View>
64+
</MapCallout>
65+
);
66+
}
67+
2468
return (
2569
<MapMarkerComponent
70+
key={key}
2671
coordinate={{
2772
latitude,
2873
longitude,
@@ -33,6 +78,8 @@ const MapMarker: React.FC<React.PropsWithChildren<MapMarkerProps>> = ({
3378
}}
3479
{...rest}
3580
>
81+
{nonCalloutChildren}
82+
3683
{pinImage && (
3784
<Image
3885
source={typeof pinImage === "string" ? { uri: pinImage } : pinImage}
@@ -43,9 +90,22 @@ const MapMarker: React.FC<React.PropsWithChildren<MapMarkerProps>> = ({
4390
}}
4491
/>
4592
)}
46-
{children}
93+
94+
{calloutChildren.map((callout, index) =>
95+
renderCallout((callout as React.ReactElement).props, index)
96+
)}
4797
</MapMarkerComponent>
4898
);
49-
};
99+
}
100+
101+
const style = StyleSheet.create({
102+
title: {
103+
fontWeight: "600",
104+
textAlign: "center",
105+
},
106+
description: {
107+
textAlign: "center",
108+
},
109+
});
50110

51111
export default MapMarker;

packages/maps/src/components/MapView.tsx

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
Region,
88
MapViewProps as MapViewComponentProps,
99
} from "react-native-maps";
10+
import MapMarker, { renderMarker } from "./MapMarker";
1011

1112
export interface MapViewProps<T> extends MapViewComponentProps {
1213
apiKey: string;
@@ -69,6 +70,32 @@ class MapView<T> extends React.Component<
6970
this.mapRef.current.animateCamera(camera);
7071
}
7172

73+
getMarkers(): React.ReactElement[] {
74+
const { markersData, renderItem, keyExtractor, children } = this.props;
75+
76+
if (markersData && renderItem) {
77+
const markers: React.ReactElement[] = [];
78+
79+
markersData.forEach((item, index) => {
80+
const component = renderItem?.({ item, index });
81+
82+
if (component && component.type === MapMarker) {
83+
const key = keyExtractor ? keyExtractor(item, index) : index;
84+
markers.push(
85+
React.cloneElement(component, {
86+
key,
87+
})
88+
);
89+
}
90+
});
91+
return markers;
92+
} else {
93+
return React.Children.toArray(children).filter(
94+
(child) => (child as React.ReactElement).type === MapMarker
95+
) as React.ReactElement[];
96+
}
97+
}
98+
7299
render() {
73100
const {
74101
apiKey,
@@ -78,12 +105,8 @@ class MapView<T> extends React.Component<
78105
zoom,
79106
showsCompass = false,
80107
loadingEnabled = true,
81-
markersData,
82-
renderItem,
83-
keyExtractor,
84108
onRegionChange,
85109
style,
86-
children,
87110
...rest
88111
} = this.props;
89112

@@ -112,20 +135,9 @@ class MapView<T> extends React.Component<
112135
style={[styles.map, style]}
113136
{...rest}
114137
>
115-
{markersData && renderItem
116-
? markersData.map((item, index) => {
117-
const component = renderItem({ item, index });
118-
119-
if (!component) {
120-
return null;
121-
}
122-
123-
const key = keyExtractor ? keyExtractor(item, index) : index;
124-
return React.cloneElement(component, {
125-
key,
126-
});
127-
})
128-
: children}
138+
{this.getMarkers().map((marker, index) =>
139+
renderMarker(marker.props, index)
140+
)}
129141
</MapViewComponent>
130142
);
131143
}

packages/maps/src/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export { default as MapView } from "./components/MapView";
22
export { default as MapMarker } from "./components/MapMarker";
3-
export { Callout as MapCallout } from "./components/MapCallout";
3+
export { default as MapCallout } from "./components/MapCallout";

0 commit comments

Comments
 (0)