import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { StyleSheet, Platform, NativeModules, Animated, Image, findNodeHandle, NativeMethods, } from 'react-native'; import { ColorPropType, ViewPropTypes, } from 'deprecated-react-native-prop-types'; import decorateMapComponent, { DecoratedComponent, SUPPORTED, USES_DEFAULT_IMPLEMENTATION, } from './decorateMapComponent'; import { Coordinate, MarkerProps } from 'react-native-maps'; const viewConfig = { uiViewClassName: 'AIRMapMarker', validAttributes: { coordinate: true, }, }; const propTypes = { ...ViewPropTypes, // TODO(lmr): get rid of these? identifier: PropTypes.string, reuseIdentifier: PropTypes.string, /** * The title of the marker. This is only used if the component has no children that * are a ``, in which case the default callout behavior will be used, which * will show both the `title` and the `description`, if provided. */ title: PropTypes.string, /** * The description of the marker. This is only used if the component has no children * that are a ``, in which case the default callout behavior will be used, * which will show both the `title` and the `description`, if provided. */ description: PropTypes.string, /** * Test ID for end to end test automation */ testID: PropTypes.string, /** * A custom image to be used as the marker's icon. Only local image resources are allowed to be * used. */ image: PropTypes.any, /** * Marker icon to render (equivalent to `icon` property of GMSMarker Class). * Using this property has an advantage over `image` in term of performance, battery usage... * because it doesn't trigger tracksViewChanges. * (tracksViewChanges is set to YES by default if iconView is not nil). */ icon: PropTypes.any, /** * Opacity level of view/image based markers */ opacity: PropTypes.number, /** * If no custom marker view or custom image is provided, the platform default pin will be used, * which can be customized by this color. Ignored if a custom marker is being used. */ pinColor: ColorPropType, /** * When true, the marker will be pre-selected. Setting this to true allows the user to * drag the marker without needing to tap on it once to focus on it. */ isPreselected: PropTypes.bool, /** * The coordinate for the marker. */ coordinate: PropTypes.shape({ /** * Coordinates for the anchor point of the marker. */ latitude: PropTypes.number.isRequired, longitude: PropTypes.number.isRequired, }).isRequired, /** * The offset (in points) at which to display the view. * * By default, the center point of an annotation view is placed at the coordinate point of the * associated annotation. You can use this property to reposition the annotation view as * needed. This x and y offset values are measured in points. Positive offset values move the * annotation view down and to the right, while negative values move it up and to the left. * * For android, see the `anchor` prop. * * @platform ios */ centerOffset: PropTypes.shape({ /** * Offset from the anchor point */ x: PropTypes.number.isRequired, y: PropTypes.number.isRequired, }), /** * The offset (in points) at which to place the callout bubble. * * This property determines the additional distance by which to move the callout bubble. When * this property is set to (0, 0), the anchor point of the callout bubble is placed on the * top-center point of the marker view’s frame. Specifying positive offset values moves the * callout bubble down and to the right, while specifying negative values moves it up and to * the left. * * For android, see the `calloutAnchor` prop. * * @platform ios */ calloutOffset: PropTypes.shape({ /** * Offset to the callout */ x: PropTypes.number.isRequired, y: PropTypes.number.isRequired, }), /** * Sets the anchor point for the marker. * * The anchor specifies the point in the icon image that is anchored to the marker's position * on the Earth's surface. * * The anchor point is specified in the continuous space [0.0, 1.0] x [0.0, 1.0], where (0, 0) * is the top-left corner of the image, and (1, 1) is the bottom-right corner. The anchoring * point in a W x H image is the nearest discrete grid point in a (W + 1) x (H + 1) grid, * obtained by scaling the then rounding. For example, in a 4 x 2 image, the anchor point * (0.7, 0.6) resolves to the grid point at (3, 1). * * For ios, see the `centerOffset` prop. * * @platform android */ anchor: PropTypes.shape({ /** * Offset to the callout */ x: PropTypes.number.isRequired, y: PropTypes.number.isRequired, }), /** * Specifies the point in the marker image at which to anchor the callout when it is displayed. * This is specified in the same coordinate system as the anchor. See the `andor` prop for more * details. * * The default is the top middle of the image. * * For ios, see the `calloutOffset` prop. * * @platform android */ calloutAnchor: PropTypes.shape({ /** * Offset to the callout */ x: PropTypes.number.isRequired, y: PropTypes.number.isRequired, }), /** * Sets whether this marker should be flat against the map true or a billboard facing the * camera false. * * @platform android */ flat: PropTypes.bool, draggable: PropTypes.bool, tappable: PropTypes.bool, /** * Sets whether this marker should track view changes true. */ tracksViewChanges: PropTypes.bool, /** * Sets whether this marker should track view changes in info window true. * * @platform ios */ tracksInfoWindowChanges: PropTypes.bool, /** * Stops Marker onPress events from propagating to and triggering MapView onPress events. * * @platform ios */ stopPropagation: PropTypes.bool, /** * Callback that is called when the user presses on the marker */ onPress: PropTypes.func, /** * Callback that is called when the user selects the marker, before the callout is shown. * * @platform ios */ onSelect: PropTypes.func, /** * Callback that is called when the marker is deselected, before the callout is hidden. * * @platform ios */ onDeselect: PropTypes.func, /** * Callback that is called when the user taps the callout view. */ onCalloutPress: PropTypes.func, /** * Callback that is called when the user initiates a drag on this marker (if it is draggable) */ onDragStart: PropTypes.func, /** * Callback called continuously as the marker is dragged */ onDrag: PropTypes.func, /** * Callback that is called when a drag on this marker finishes. This is usually the point you * will want to setState on the marker's coordinate again */ onDragEnd: PropTypes.func, }; const defaultProps = { stopPropagation: false, }; class MapMarker extends DecoratedComponent { static propTypes = propTypes; static defaultProps = defaultProps; static viewConfig = viewConfig; marker: (Component & Readonly) | null = null; static Animated: Animated.AnimatedComponent>; constructor(props: MarkerProps) { super(props); this.showCallout = this.showCallout.bind(this); this.hideCallout = this.hideCallout.bind(this); this.redrawCallout = this.redrawCallout.bind(this); this.animateMarkerToCoordinate = this.animateMarkerToCoordinate.bind(this); } setNativeProps(props: MarkerProps) { this.marker?.setNativeProps(props); } showCallout() { this._runCommand('showCallout', []); } hideCallout() { this._runCommand('hideCallout', []); } redrawCallout() { this._runCommand('redrawCallout', []); } animateMarkerToCoordinate(coordinate: Coordinate, duration?: number) { this._runCommand('animateMarkerToCoordinate', [ coordinate, duration || 500, ]); } redraw() { this._runCommand('redraw', []); } _getHandle() { return findNodeHandle(this.marker); } _runCommand(name: string, args: any) { switch (Platform.OS) { case 'android': NativeModules.UIManager.dispatchViewManagerCommand( this._getHandle(), this.getUIManagerCommand(name), args ); break; case 'ios': this.getMapManagerCommand(name)(this._getHandle(), ...args); break; default: break; } } render() { /* let image; if (this.props.image) { image = Image.resolveAssetSource(this.props.image) || {}; image = image.uri || this.props.image; } let icon; if (this.props.icon) { icon = Image.resolveAssetSource(this.props.icon) || {}; icon = icon.uri; }*/ const AIRMapMarker = this.getAirComponent(); const { children } = this.props; const calculatedChildren = children == null && this.props.image ? ( ) : ( children ); // console.log('CH', children, calculatedChildren); return ( { this.marker = ref; }} {...this.props} // image={image} //icon={icon} style={[styles.marker, this.props.style]} onPress={(event: any) => { if (this.props.stopPropagation) { event.stopPropagation(); } if (this.props.onPress) { this.props.onPress(event); } }} > {calculatedChildren} ); } } const styles = StyleSheet.create({ marker: { position: 'absolute', backgroundColor: 'transparent', }, }); MapMarker.Animated = Animated.createAnimatedComponent(MapMarker); export default decorateMapComponent(MapMarker, { componentType: 'Marker', providers: { google: { ios: SUPPORTED, android: USES_DEFAULT_IMPLEMENTATION, }, }, });