Created
December 24, 2019 15:39
-
-
Save evgeniidatsiuk/e61c50b8a38d732d8597770b40dc3083 to your computer and use it in GitHub Desktop.
Google Maps
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
google api key - https://developers.google.com/maps/documentation/javascript/get-api-key | |
doc - https://github.com/tomchentw/react-google-maps | |
//--authorization | |
import React from "react" | |
import { compose, withProps } from "recompose" | |
import { withScriptjs, withGoogleMap } from "react-google-maps" | |
export default Map => | |
compose( | |
withProps({ | |
googleMapURL: `https://maps.googleapis.com/maps/api/js?key=${document.querySelector('meta[name="google_maps_key"]').content}&v=3.exp&libraries=geometry,drawing,places`, | |
loadingElement: <div style={{ height: `100%` }} />, | |
containerElement: <div style={{ height: `600px` }} />, | |
mapElement: <div style={{ height: `100%` }} />, | |
}), | |
withScriptjs, | |
withGoogleMap | |
)((props) => | |
<Map | |
defaultZoom={16} | |
defaultCenter = {{ | |
lat: -1.2884, | |
lng: 36.8233 | |
}} | |
{...props} | |
/> | |
) | |
//--track you location | |
import React from "react" | |
import { compose, withState, lifecycle } from "recompose" | |
export default CurrentLocation => | |
compose( | |
withState('currentLocation', 'setCurrentLocation'), | |
lifecycle({ | |
componentDidMount() { | |
console.log('CurrentLocation componentDidMount'); | |
if (navigator.geolocation) { | |
navigator.geolocation.getCurrentPosition((position) =>{ | |
let location = { | |
lat: position.coords.latitude, | |
lng: position.coords.longitude | |
}; | |
this.props.setCurrentLocation(location); | |
}, ()=>{ | |
console.log('Error: The Geolocation service failed.') | |
}); | |
} else { | |
console.log('Error: Your browser doesn\'t support geolocation.') | |
} | |
} | |
}) | |
)((props)=> | |
<CurrentLocation | |
{...props} | |
/> | |
) | |
//-- first component MapMarker | |
import React from "react" | |
import { compose, withProps, withHandlers, withState, lifecycle } from "recompose" | |
import { GoogleMap, Marker } from "react-google-maps" | |
import SearchBox from "react-google-maps/lib/components/places/SearchBox"; | |
import GoogleApiWrapper from './api' | |
import GoogleCurrentLocationWrapper from './current-location' | |
const styles={ | |
deleteMarker:{ | |
position: 'relative', | |
top: 0, | |
left: 0, | |
zIndex: 5 | |
} | |
} | |
const MapMarker = compose( | |
withState('bounds', 'setBounds'), | |
withState('zoom', 'setZoom', 16), | |
withHandlers(()=> { | |
const refs = { | |
map: undefined, | |
} | |
return { | |
onMapMounted: ()=> ref=> { | |
refs.map = ref | |
}, | |
onMarkerMounted: () => ref => { | |
refs.marker = ref | |
}, | |
onSearchBoxMounted: ()=> ref => { | |
refs.searchBox = ref; | |
}, | |
onBoundsChanged: (props)=> () => { | |
props.setBounds(refs.map.getBounds()) | |
}, | |
onPlacesChanged: ()=> ()=> { | |
const places = refs.searchBox.getPlaces(); | |
const bounds = new google.maps.LatLngBounds(); | |
places.forEach(place => { | |
if (place.geometry.viewport) { | |
bounds.union(place.geometry.viewport) | |
} else { | |
bounds.extend(place.geometry.location) | |
} | |
}); | |
refs.map.fitBounds(bounds); | |
refs.map.panToBounds(bounds); | |
}, | |
onMapClick: (props)=> (event)=>{ | |
const markerPosition = { | |
lat: event.latLng.lat(), | |
lng: event.latLng.lng() | |
}; | |
console.log('onMapClick', markerPosition); | |
props.onChange && props.onChange(markerPosition); | |
if(props.onChangeAddress){ | |
new google.maps.Geocoder().geocode({'location': event.latLng}, (results, status) => { | |
let address = ''; | |
if (status === 'OK') { | |
if (results[0]) { | |
address = results[0].formatted_address; | |
} else { | |
console.log('No results found'); | |
} | |
} else { | |
console.log('Geocoder failed due to: ' + status); | |
} | |
props.onChangeAddress(address); | |
}) | |
} | |
}, | |
deleteMarker: (props)=> ()=>{ | |
props.onChange && props.onChange(null); | |
props.onChangeAddress && props.onChangeAddress(''); | |
}, | |
onLoad: (props)=> ()=>{ | |
if(props.markerPosition || props.currentLocation){ | |
const bounds = new google.maps.LatLngBounds(); | |
bounds.extend(props.markerPosition || props.currentLocation); | |
refs.map.fitBounds(bounds); | |
refs.map.panToBounds(bounds); | |
} | |
} | |
} | |
}), | |
lifecycle({ | |
componentDidMount() { | |
this.props.onLoad() | |
}, | |
componentDidUpdate(prevProps, prevState){ | |
((this.props.markerPosition && !prevProps.markerPosition) || | |
(this.props.markerPosition && this.props.markerPosition.lat != prevProps.markerPosition.lat && this.props.markerPosition.lng != prevProps.markerPosition.lng) || | |
(this.props.currentLocation && !prevProps.currentLocation && !this.props.markerPosition) | |
) | |
&& this.props.onLoad() | |
} | |
}) | |
)((props)=> | |
<GoogleMap | |
ref={props.onMapMounted} | |
defaultCenter = {props.defaultCenter} | |
defaultZoom = {props.defaultZoom} | |
onBoundsChanged={props.onBoundsChanged} | |
zoom={props.zoom} | |
onClick={props.onMapClick} | |
> | |
<SearchBox | |
ref={props.onSearchBoxMounted} | |
bounds={props.bounds} | |
controlPosition={google.maps.ControlPosition.TOP_LEFT} | |
onPlacesChanged={props.onPlacesChanged} | |
> | |
<input | |
type="text" | |
placeholder="Search" | |
style={{ | |
boxSizing: `border-box`, | |
border: `1px solid transparent`, | |
width: `240px`, | |
height: `32px`, | |
marginTop: `16px`, | |
padding: `0 12px`, | |
borderRadius: `3px`, | |
boxShadow: `0 2px 6px rgba(0, 0, 0, 0.3)`, | |
fontSize: `14px`, | |
outline: `none`, | |
textOverflow: `ellipses`, | |
color: 'black' | |
}} | |
/> | |
</SearchBox> | |
{ | |
props.markerPosition != null ? | |
<Marker | |
ref={props.onMarkerMounted} | |
position={props.markerPosition} | |
/> | |
: | |
null | |
} | |
{ | |
(props.markerPosition != null && props.onChange != null) ? | |
<input | |
style={styles.deleteMarker} | |
onClick={props.deleteMarker} | |
type="button" | |
value="Delete Marker" | |
/> | |
: | |
null | |
} | |
</GoogleMap> | |
) | |
export default compose( | |
GoogleApiWrapper, | |
GoogleCurrentLocationWrapper | |
)(MapMarker) | |
module ApplicationHelper | |
def meta_tag(tag, text) | |
content_for :"meta_#{tag}", text | |
end | |
def yield_meta_tag(tag, default_text='') | |
content_for?(:"meta_#{tag}") ? content_for(:"meta_#{tag}") : default_text | |
end | |
end | |
module ValidationHelper | |
UNICODE_TEXT = /\A([\p{L}\p{P}\d\s])+\z/ | |
end | |
<% meta_tag :google_maps_key, "key" %> | |
<meta name='google_maps_key' | |
content='<%= yield_meta_tag(:google_maps_key, 'invalid_key') %>' /> | |
validates :latitude, | |
numericality: { | |
message: "Latitude must be numericality", | |
allow_blank: true, | |
greater_than_or_equal_to: -90, | |
less_than_or_equal_to: 90, | |
} | |
validates :longitude, | |
numericality: { | |
message: "Longitude must be numericality", | |
allow_blank: true, | |
greater_than_or_equal_to: -180, | |
less_than_or_equal_to: 180, | |
} | |
validates :address, | |
presence: {message: 'Address can\'t be blank.', if: -> {latitude.present? && longitude.present?}}, | |
length: {maximum: 100, message: "Address must be in range", allow_blank: true}, | |
format: {with: ValidationHelper::UNICODE_TEXT, message: "Incorrect format of the address", allow_blank: true} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment