Skip to content

Commit

Permalink
Updated how markers & callouts are rendered to maintain types + added…
Browse files Browse the repository at this point in the history
… default callout for title/description
  • Loading branch information
YoussefHenna committed May 15, 2023
1 parent bd510d6 commit 429b018
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 39 deletions.
26 changes: 18 additions & 8 deletions packages/maps/src/components/MapCallout.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
import * as React from "react";
import { Callout as MapCalloutComponent } from "./react-native-maps";
import type { MapCalloutProps as MapCalloutComponentProps } from "react-native-maps";
import { Callout as MapCalloutComponent } from "./react-native-maps";

export interface MapCalloutProps
extends Omit<MapCalloutComponentProps, "tooltip"> {
showTooltip?: boolean;
}

// Has to be a function named 'Callout' to be matched as a Callout component and not a custom view for marker
// See: https://github.com/teovillanueva/react-native-web-maps/blob/81278079c6f26a707d915d69de9a00080c305957/packages/react-native-web-maps/src/components/marker.web.tsx#L79
export function Callout({
showTooltip,
...rest
}: React.PropsWithChildren<MapCalloutProps>) {
return <MapCalloutComponent tooltip={!showTooltip} {...rest} />;
/**
* Renders nothing, serves as placeholder for props
* Rendering exposed as function to avoid having an intermediary component that changes the type
*
* This is done because the underlying package has logic dependant on the type of child
* See: https://github.com/teovillanueva/react-native-web-maps/blob/5f3d0ec7c24f789c3df30c1d6d7223e638ff5868/packages/react-native-web-maps/src/components/marker.web.tsx#L79
*/
const MapCallout: React.FC<React.PropsWithChildren<MapCalloutProps>> = () => {
return null;
};

export function renderCallout(props: MapCalloutProps, key: React.Key) {
return (
<MapCalloutComponent key={key} tooltip={!props.showTooltip} {...props} />
);
}

export default MapCallout;
84 changes: 72 additions & 12 deletions packages/maps/src/components/MapMarker.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import * as React from "react";
import { Image, ImageSourcePropType } from "react-native";
import {
Image,
ImageSourcePropType,
View,
StyleSheet,
Text,
} from "react-native";
import { Marker as MapMarkerComponent } from "./react-native-maps";
import type { MapMarkerProps as MapMarkerComponentProps } from "react-native-maps";
import MapCallout, { renderCallout } from "./MapCallout";

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

const MapMarker: React.FC<React.PropsWithChildren<MapMarkerProps>> = ({
latitude,
longitude,
pinImage,
pinImageSize = 50,
onPress,
children,
...rest
}) => {
/**
* Renders nothing, serves as placeholder for props
* Rendering exposed as function to avoid having an intermediary component that changes the type
*
* This is done because the underlying package has logic dependant on the type of child
*/
const MapMarker: React.FC<React.PropsWithChildren<MapMarkerProps>> = () => {
return null;
};

export function renderMarker(
{
latitude,
longitude,
pinImage,
pinImageSize = 50,
onPress,
children,
title,
description,
...rest
}: MapMarkerProps,
key: React.Key
) {
const childrenArray = React.Children.toArray(children);

const calloutChildren = childrenArray.filter(
(child) => (child as React.ReactElement).type === MapCallout
);

const nonCalloutChildren = childrenArray.filter(
(child) => (child as React.ReactElement).type !== MapCallout
);

// Add default callout for title/description
if (calloutChildren.length === 0 && (title || description)) {
calloutChildren.push(
<MapCallout showTooltip>
<View>
{title && <Text style={style.title}>{title}</Text>}
{description && <Text style={style.description}>{description}</Text>}
</View>
</MapCallout>
);
}

return (
<MapMarkerComponent
key={key}
coordinate={{
latitude,
longitude,
Expand All @@ -33,6 +78,8 @@ const MapMarker: React.FC<React.PropsWithChildren<MapMarkerProps>> = ({
}}
{...rest}
>
{nonCalloutChildren}

{pinImage && (
<Image
source={typeof pinImage === "string" ? { uri: pinImage } : pinImage}
Expand All @@ -43,9 +90,22 @@ const MapMarker: React.FC<React.PropsWithChildren<MapMarkerProps>> = ({
}}
/>
)}
{children}

{calloutChildren.map((callout, index) =>
renderCallout((callout as React.ReactElement).props, index)
)}
</MapMarkerComponent>
);
};
}

const style = StyleSheet.create({
title: {
fontWeight: "600",
textAlign: "center",
},
description: {
textAlign: "center",
},
});

export default MapMarker;
48 changes: 30 additions & 18 deletions packages/maps/src/components/MapView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
Region,
MapViewProps as MapViewComponentProps,
} from "react-native-maps";
import MapMarker, { renderMarker } from "./MapMarker";

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

getMarkers(): React.ReactElement[] {
const { markersData, renderItem, keyExtractor, children } = this.props;

if (markersData && renderItem) {
const markers: React.ReactElement[] = [];

markersData.forEach((item, index) => {
const component = renderItem?.({ item, index });

if (component && component.type === MapMarker) {
const key = keyExtractor ? keyExtractor(item, index) : index;
markers.push(
React.cloneElement(component, {
key,
})
);
}
});
return markers;
} else {
return React.Children.toArray(children).filter(
(child) => (child as React.ReactElement).type === MapMarker
) as React.ReactElement[];
}
}

render() {
const {
apiKey,
Expand All @@ -78,12 +105,8 @@ class MapView<T> extends React.Component<
zoom,
showsCompass = false,
loadingEnabled = true,
markersData,
renderItem,
keyExtractor,
onRegionChange,
style,
children,
...rest
} = this.props;

Expand Down Expand Up @@ -112,20 +135,9 @@ class MapView<T> extends React.Component<
style={[styles.map, style]}
{...rest}
>
{markersData && renderItem
? markersData.map((item, index) => {
const component = renderItem({ item, index });

if (!component) {
return null;
}

const key = keyExtractor ? keyExtractor(item, index) : index;
return React.cloneElement(component, {
key,
});
})
: children}
{this.getMarkers().map((marker, index) =>
renderMarker(marker.props, index)
)}
</MapViewComponent>
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/maps/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { default as MapView } from "./components/MapView";
export { default as MapMarker } from "./components/MapMarker";
export { Callout as MapCallout } from "./components/MapCallout";
export { default as MapCallout } from "./components/MapCallout";

0 comments on commit 429b018

Please sign in to comment.