react-native-maps
Version:
React Native Mapview component for iOS + Android
332 lines (285 loc) • 8.37 kB
JavaScript
'use strict';
var React = require('react-native');
var {
EdgeInsetsPropType,
NativeMethodsMixin,
Platform,
ReactNativeViewAttributes,
View,
Animated,
requireNativeComponent,
NativeModules,
PropTypes,
} = React;
var MapMarker = require('./MapMarker');
var MapPolyline = require('./MapPolyline');
var MapPolygon = require('./MapPolygon');
var MapCircle = require('./MapCircle');
var MapCallout = require('./MapCallout');
var deepDiffer = require('deepDiffer');
var insetsDiffer = require('insetsDiffer');
var merge = require('merge');
var MapView = React.createClass({
mixins: [NativeMethodsMixin],
viewConfig: {
uiViewClassName: 'AIRMap',
validAttributes: {
region: true,
},
},
propTypes: {
...View.propTypes,
/**
* Used to style and layout the `MapView`. See `StyleSheet.js` and
* `ViewStylePropTypes.js` for more info.
*/
style: View.propTypes.style,
/**
* If `true` the app will ask for the user's location and focus on it.
* Default value is `false`.
*
* **NOTE**: You need to add NSLocationWhenInUseUsageDescription key in
* Info.plist to enable geolocation, otherwise it is going
* to *fail silently*!
*/
showsUserLocation: PropTypes.bool,
/**
* If `false` points of interest won't be displayed on the map.
* Default value is `true`.
*
* @platform ios
*/
showsPointsOfInterest: PropTypes.bool,
/**
* If `false` compass won't be displayed on the map.
* Default value is `true`.
*
* @platform ios
*/
showsCompass: PropTypes.bool,
/**
* If `false` the user won't be able to pinch/zoom the map.
* Default value is `true`.
*
* @platform ios
*/
zoomEnabled: PropTypes.bool,
/**
* If `false` the user won't be able to change the map region being displayed.
* Default value is `true`.
*
* @platform ios
*/
scrollEnabled: PropTypes.bool,
/**
* A Boolean indicating whether the map shows scale information.
* Default value is `false`
*
* @platform ios
*/
showsScale: PropTypes.bool,
/**
* A Boolean indicating whether the map displays extruded building information.
* Default value is `true`.
*/
showsBuildings: PropTypes.bool,
/**
* A Boolean value indicating whether the map displays traffic information.
* Default value is `false`.
*/
showsTraffic: PropTypes.bool,
/**
* A Boolean indicating whether indoor maps should be enabled.
* Default value is `false`
*
* @platform android
*/
showsIndoors: PropTypes.bool,
/**
* The map type to be displayed.
*
* - standard: standard road map (default)
* - satellite: satellite view
* - hybrid: satellite view with roads and points of interest overlayed
*/
mapType: PropTypes.oneOf([
'standard',
'satellite',
'hybrid',
]),
/**
* The region to be displayed by the map.
*
* The region is defined by the center coordinates and the span of
* coordinates to display.
*/
region: PropTypes.shape({
/**
* Coordinates for the center of the map.
*/
latitude: PropTypes.number.isRequired,
longitude: PropTypes.number.isRequired,
/**
* Difference between the minimun and the maximum latitude/longitude
* to be displayed.
*/
latitudeDelta: PropTypes.number.isRequired,
longitudeDelta: PropTypes.number.isRequired,
}),
/**
* The initial region to be displayed by the map. Use this prop instead of `region`
* only if you don't want to control the viewport of the map besides the initial region.
*
* Changing this prop after the component has mounted will not result in a region change.
*
* This is similar to the `initialValue` prop of a text input.
*/
initialRegion: PropTypes.shape({
/**
* Coordinates for the center of the map.
*/
latitude: PropTypes.number.isRequired,
longitude: PropTypes.number.isRequired,
/**
* Difference between the minimun and the maximum latitude/longitude
* to be displayed.
*/
latitudeDelta: PropTypes.number.isRequired,
longitudeDelta: PropTypes.number.isRequired,
}),
/**
* Maximum size of area that can be displayed.
*
* @platform ios
*/
maxDelta: PropTypes.number,
/**
* Minimum size of area that can be displayed.
*
* @platform ios
*/
minDelta: PropTypes.number,
/**
* Insets for the map's legal label, originally at bottom left of the map.
* See `EdgeInsetsPropType.js` for more information.
*/
legalLabelInsets: EdgeInsetsPropType,
/**
* Callback that is called continuously when the user is dragging the map.
*/
onRegionChange: PropTypes.func,
/**
* Callback that is called once, when the user is done moving the map.
*/
onRegionChangeComplete: PropTypes.func,
/**
* Callback that is called when user taps on the map.
*/
onPress: PropTypes.func,
/**
* Callback that is called when user makes a "long press" somewhere on the map.
*/
onLongPress: PropTypes.func,
/**
* Callback that is called when a marker on the map is tapped by the user.
*/
onMarkerPress: PropTypes.func,
/**
* Callback that is called when a marker on the map becomes selected. This will be called when
* the callout for that marker is about to be shown.
*
* @platform ios
*/
onMarkerSelect: PropTypes.func,
/**
* Callback that is called when a marker on the map becomes deselected. This will be called when
* the callout for that marker is about to be hidden.
*
* @platform ios
*/
onMarkerDeselect: PropTypes.func,
/**
* Callback that is called when a callout is tapped by the user.
*/
onCalloutPress: PropTypes.func,
},
componentDidMount: function() {
if (this.props.region) {
this.refs.map.setNativeProps({ region: this.props.region });
}
},
componentWillUpdate: function(nextProps) {
var a = this.__lastRegion;
var b = nextProps.region;
if (!a || !b) return;
if (
a.latitude !== b.latitude ||
a.longitude !== b.longitude ||
a.latitudeDelta !== b.latitudeDelta ||
a.longitudeDelta !== b.longitudeDelta
) {
this.refs.map.setNativeProps({ region: b });
}
},
_onChange: function(event: Event) {
this.__lastRegion = event.nativeEvent.region;
if (event.nativeEvent.continuous) {
this.props.onRegionChange &&
this.props.onRegionChange(event.nativeEvent.region);
} else {
this.props.onRegionChangeComplete &&
this.props.onRegionChangeComplete(event.nativeEvent.region);
}
},
animateToRegion: function (region, duration) {
this._runCommand('animateToRegion', [region, duration || 500]);
},
animateToCoordinate: function (latLng, duration) {
this._runCommand('animateToCoordinate', [latLng, duration || 500]);
},
fitToElements: function(animated) {
this._runCommand('fitToElements', [animated]);
},
_getHandle: function() {
return React.findNodeHandle(this.refs.map);
},
_runCommand: function (name, args) {
switch (Platform.OS) {
case 'android':
NativeModules.UIManager.dispatchViewManagerCommand(
this._getHandle(),
NativeModules.UIManager.AIRMap.Commands[name],
args
);
break;
case 'ios':
NativeModules.AIRMapManager[name].apply(
NativeModules.AIRMapManager[name],
[this._getHandle(), ...args]
);
break;
}
},
render: function() {
return (
<AIRMap
ref="map"
{...this.props}
region={null}
onChange={this._onChange}
/>
);
},
});
var AIRMap = requireNativeComponent('AIRMap', MapView, {
nativeOnly: {
onChange: true,
},
});
MapView.Marker = MapMarker;
MapView.Polyline = MapPolyline;
MapView.Polygon = MapPolygon;
MapView.Circle = MapCircle;
MapView.Callout = MapCallout;
MapView.Animated = Animated.createAnimatedComponent(MapView);
module.exports = MapView;