Last active
August 23, 2023 15:06
-
-
Save passosleo/6e353e39891d7cfc84e0a6d973619a4b to your computer and use it in GitHub Desktop.
React Google Maps Implementation
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
import React from "react"; | |
import { useMap } from "@/components/Map/hooks/useMap"; | |
import { Map, MarkerObject } from "@/components/Map"; | |
export function Example() { | |
const { settings, zoomOut, zoomIn, panTo, directions } = useMap({ | |
defaultCenter: { | |
lat: 0, | |
lng: 0, | |
}, | |
defaultZoom: 15, | |
}); | |
return ( | |
<Map | |
settings={settings} | |
directions={directions} | |
markers={ | |
[ | |
{ | |
position: { lat: 0, lng: 0 }, | |
onClick: () => { | |
console.log("marker clicked"); | |
zoomOut(); | |
}, | |
}, | |
{ | |
position: { lat: 0, lng: 0 }, | |
onClick: () => { | |
console.log("marker clicked"); | |
zoomIn(); | |
}, | |
}, | |
{ | |
position: { lat: 0, lng: 0 }, | |
onClick: () => { | |
console.log("marker clicked"); | |
panTo( | |
{ lat: 0, lng: 0 }, | |
{ | |
enableZoom: true, | |
zoom: 10, | |
timeout: 1000, | |
onFinish: () => { | |
console.log("pan to finished"); | |
}, | |
} | |
); | |
}, | |
}, | |
] as MarkerObject[] | |
} | |
/> | |
); | |
} |
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
import React, { memo } from 'react'; | |
import { | |
DirectionsRenderer, | |
GoogleMap, | |
GoogleMapProps, | |
Marker, | |
useJsApiLoader | |
} from '@react-google-maps/api'; | |
type Position = { | |
lat: number; | |
lng: number; | |
}; | |
export type MarkerObject = { | |
icon: string; | |
position: Position; | |
onClick?: (position: Position) => void; | |
}; | |
type Props = { | |
markers: MarkerObject[]; | |
settings: GoogleMapProps; | |
directions?: google.maps.DirectionsResult | null; | |
width?: string; | |
height?: string; | |
}; | |
export function MapComponent({ | |
markers, | |
settings, | |
directions, | |
width = '100%' | |
height = '100vh' | |
}: Props) { | |
const { isLoaded } = useJsApiLoader({ | |
id: 'google-map-script', | |
googleMapsApiKey: 'YOUR-API-KEY' | |
}); | |
return isLoaded ? ( | |
<GoogleMap | |
mapContainerStyle={{ width, height }} | |
options={{ mapTypeControl: false }} | |
{...settings} | |
> | |
{markers?.map(({ icon, position, onClick }, index) => ( | |
<Marker | |
key={index} | |
position={position} | |
icon={icon} | |
zIndex={index} | |
onClick={() => onClick?.(position)} | |
/> | |
))} | |
{directions && <DirectionsRenderer directions={directions} />} | |
</GoogleMap> | |
) : ( | |
<></> | |
); | |
} | |
export const Map = memo(MapComponent); |
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
import { GoogleMapProps } from '@react-google-maps/api'; | |
import { useState, useEffect, useCallback } from 'react'; | |
type Props = { | |
defaultCenter: GoogleMapProps['center']; | |
defaultZoom: GoogleMapProps['zoom']; | |
}; | |
type GetRouteOptions = { | |
onRouteFetched: (result: google.maps.DirectionsResult) => void; | |
destination: google.maps.LatLngLiteral; | |
travelMode?: google.maps.TravelMode; | |
origin: google.maps.LatLngLiteral; | |
}; | |
type PanToOptions = { | |
onFinish?: () => void; | |
enableZoom?: boolean; | |
timeout?: number; | |
zoom?: number; | |
}; | |
export function useMap({ defaultCenter, defaultZoom }: Props) { | |
const [map, setMap] = useState<google.maps.Map | null>(null); | |
const initialSettings: GoogleMapProps = { | |
onLoad: useCallback((map: google.maps.Map) => setMap(map), []), | |
onUnmount: useCallback(() => setMap(null), []), | |
center: defaultCenter, | |
zoom: defaultZoom | |
}; | |
const [settings] = useState<GoogleMapProps>(initialSettings); | |
const [userLocation, setUserLocation] = | |
useState<google.maps.LatLngLiteral | null>(null); | |
const [directions, setDirections] = | |
useState<google.maps.DirectionsResult | null>(null); | |
function getUserLocation() { | |
if (navigator.geolocation) { | |
navigator.geolocation.getCurrentPosition(({ coords }) => { | |
setUserLocation({ | |
lat: coords.latitude, | |
lng: coords.longitude | |
}); | |
}); | |
} | |
} | |
function zoomOut(number: number = 7) { | |
if (!map) return; | |
map.setZoom(number); | |
} | |
function zoomIn(number: number = 8) { | |
if (!map) return; | |
map.setZoom(number); | |
} | |
function panTo( | |
{ lat, lng }: google.maps.LatLngLiteral, | |
{ | |
timeout = 500, | |
enableZoom = false, | |
zoom = 18, | |
onFinish | |
}: PanToOptions = {} | |
) { | |
if (!map) return; | |
const currentCenter = map.getCenter(); | |
if (currentCenter) { | |
const currentLat = currentCenter.lat().toFixed(3); | |
const currentLng = currentCenter.lng().toFixed(3); | |
const destinationLat = lat.toFixed(3); | |
const destinationLng = lng.toFixed(3); | |
if (currentLat === destinationLat && currentLng === destinationLng) { | |
if (onFinish) onFinish(); | |
return; | |
} | |
} | |
map.panTo({ lat, lng }); | |
if (!enableZoom && !onFinish) return; | |
setTimeout(() => { | |
if (enableZoom) zoomIn(zoom); | |
if (onFinish) onFinish(); | |
}, timeout); | |
} | |
function getRoute({ | |
origin, | |
destination, | |
travelMode = google.maps.TravelMode.DRIVING, | |
onRouteFetched | |
}: GetRouteOptions) { | |
const directionsService = new google.maps.DirectionsService(); | |
directionsService.route( | |
{ | |
origin, | |
destination, | |
travelMode | |
}, | |
(result, status) => { | |
if (status === google.maps.DirectionsStatus.OK && result) { | |
onRouteFetched(result); | |
} | |
} | |
); | |
} | |
useEffect(() => { | |
getUserLocation(); | |
}, []); | |
useEffect(() => { | |
if (userLocation) { | |
panTo(userLocation); | |
} | |
}, [userLocation]); | |
return { | |
setDirections, | |
userLocation, | |
directions, | |
getRoute, | |
settings, | |
zoomOut, | |
zoomIn, | |
panTo, | |
map | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment