<style>
    .example-container {
        height: 100vh;
    }
</style>

<div class="example-container">
    <div data-react="google-map" class="google-map"></div>
</div>
<style>
    .example-container {
        height: 100vh;
    }
</style>

<div class="example-container">
    <div data-react="google-map" class="google-map"></div>
</div>
/* No context defined. */
  • Content:
    import React, { forwardRef, useRef, useEffect, useImperativeHandle } from 'react';
    import PropTypes from 'prop-types';
    import mapOptions from './mapOptions';
    import { addZoomControl, addMarkers, createMarker } from './utils';
    import { GoogleMapsContext } from '../../context/GoogleMapsProvider';
    
    const GoogleMap = forwardRef((props, ref) => {
        const mapRef = useRef();
        const [ map, setMap ] = React.useState();
        const [ targetMarker, setTargetMarker ] = React.useState(); // eslint-disable-line
        const [ markers, setMarkers ] = React.useState();
        const { googleMapsApi } = React.useContext(GoogleMapsContext);
    
        const initGoogleMap = () => {
            setMap(() => new googleMapsApi.Map(mapRef.current, mapOptions));
        };
    
        const getPlaceLatLng = (place) => {
            return (new googleMapsApi.LatLng(
                place.Latitude,
                place.Longitude
            ));
        };
    
        const getVisiblePlaces = () => {
            var bounds = map.getBounds();
    
            return (
                props.places.reduce((acc, place) => {
                    const isPlaceVisible = bounds.contains(getPlaceLatLng(place));
                    return ([
                        ...acc,
                        ...(isPlaceVisible ? [place.Id] : [])
                    ]);
                }, [])
            );
        };
    
        const getTargetArea = (targetLatLng) => {
            const distance = window.innerWidth > 767 ? 30000 : 15000; // Change radius between different viewports.
    
            return googleMapsApi.geometry.spherical.computeOffset(targetLatLng, distance, 0);
        };
    
        const findClosestMarker = (targetLatLng) => {
            var distances = [];
            var closest = -1;
            for (let i = 0; i < props.places.length; i++) {
                var d = googleMapsApi.geometry.spherical.computeDistanceBetween(
                    targetLatLng,
                    getPlaceLatLng(props.places[i])
                );
                distances[i] = d;
                if (closest === -1 || d < distances[closest]) {
                    closest = i;
                }
            }
            return new googleMapsApi.LatLng(
                props.places[closest].Latitude,
                props.places[closest].Longitude
            );
        };
    
        const updateTargetMarker = (lonLat) => {
            setTargetMarker((prevMarker) => {
                // remove previous marker
                if (prevMarker) {
                    prevMarker.setMap(null);
                }
                return createMarker(
                    'search',
                    {
                        position: lonLat
                    },
                    googleMapsApi,
                    map
                );
            });
        };
    
        const openInfoWindow = (id) => {
            markers.forEach(marker => {
                marker.infoWindow.close(map, marker);
            });
            const marker = markers.find((marker) => marker.Id === id);
            if (marker) {
                marker.infoWindow.open(map, marker);
                map.setCenter(marker.getPosition());
            }
        };
    
        useImperativeHandle(ref, () => ({
            openInfoWindow
        }));
    
        // Initialize map when ready
        useEffect(() => {
            const _mapRef = mapRef.current;
            if (googleMapsApi && _mapRef) {
                initGoogleMap();
            }
        }, [googleMapsApi, mapRef]);
    
        // Set map focus based on a given tartget place
        useEffect(() => {
            // break execution if targetPlaceCords are undefined
            if (!props.targetPlaceCords) {
                return;
            }
            if (googleMapsApi && map) {
                const targetLatLng = props.targetPlaceCords;
                const bounds = new googleMapsApi.LatLngBounds();
    
                bounds.extend(targetLatLng);
                bounds.extend(getTargetArea(targetLatLng));
                map.fitBounds(bounds);
    
                const listener = google.maps.event.addListener(map, 'idle', () => {
                    map.setCenter(targetLatLng);
    
                    const isAnyPlaceVisible = getVisiblePlaces(targetLatLng);
                    // If map doesn't contains any markers then find closest one.
                    if (!isAnyPlaceVisible.length) {
                        bounds.extend(findClosestMarker(targetLatLng));
                        map.fitBounds(bounds);
                    }
    
                    google.maps.event.removeListener(listener);
                });
                updateTargetMarker(targetLatLng);
            }
        }, [props.targetPlaceCords, map]);
    
        useEffect(() => {
            if (map) {
                addZoomControl(map);
                if (props.places) {
                    setMarkers(() => addMarkers(map, googleMapsApi, props.places, props.onInfoWindowClose, props.onMarkerClick));
                    googleMapsApi.event.addListener(map, 'idle', function() {
                        props.onVisibleMarkersUpdate(getVisiblePlaces());
                    });
                }
            }
        }, [map]);
    
        return (
            <div ref={mapRef} className="google-map__map" />
        );
    });
    
    GoogleMap.displayName = 'GoogleMap';
    
    GoogleMap.defaultProps = {
        onVisibleMarkersUpdate: () => {},
        onInfoWindowClose: () => {},
        onMarkerClick: () => {}
    };
    
    GoogleMap.propTypes = {
        targetPlaceCords: PropTypes.object,
        onVisibleMarkersUpdate: PropTypes.func,
        onMarkerClick: PropTypes.func
    };
    
    export default GoogleMap;
    
  • URL: /components/raw/google-map/GoogleMap.jsx
  • Filesystem Path: src/components/google-map/GoogleMap.jsx
  • Size: 5.2 KB
  • Content:
    // map
    
    .google-map {
        height: 100%;
        width: 100%;
    }
    
    .google-map__map {
        height: 100%;
    }
    
    .google-map__info-window {
        background-color: $color-white;
        color: $color-black;
    }
    
    .google-map__info-window-detail {
        display: flex;
        line-height: size(2.25);
    }
    
    .google-map__info-window-icon {
        display: inline-block;
        width: size(3);
        height: size(2);
    }
    
    // zoom
    
    .google-map-zoom-control {
        position: absolute;
        bottom: size(2) !important;
        right: size(2) !important;
        width: size(6);
        height: size(13);
    }
    
    .google-map-zoom-control__inner {
        display: flex;
        flex-direction: column;
    }
    
    .google-map-zoom-control__button {
        width: size(6);
        height: size(6);
        border-radius: 50%;
        background: no-repeat center center;
        background-size: size(3);
        background-color: $color-white;
    
        &--in {
            background-image: url('./img/plus.svg');
            margin-bottom: size(1);
        }
    
        &--out {
            background-image: url('./img/minus.svg');
        }
    }
    
    // info-window
    .google-map-info-window {
        background-color: $color-white;
        color: $color-black;
        text-align: left;
        font-weight: normal;
        max-width: 360px;
    }
    
    .google-map-info-window__detail {
        display: flex;
        line-height: size(2.25);
    }
    
    .google-map-info-window__icon {
        display: inline-block;
        width: size(3);
        height: size(2);
    }
    
    .google-map-info-window__link {
        color: $color-green;
    }
  • URL: /components/raw/google-map/google-map.scss
  • Filesystem Path: src/components/google-map/google-map.scss
  • Size: 1.4 KB
  • Content:
    export default {
        disableDefaultUI: true,
        zoomControl: false,
        zoom: 7,
        mapTypeControl: false,
        minZoom: 3,
        scrollwheel: false,
        streetViewControl: false,
        maxZoom: 22,
        cityZoomLevel: 17,
        countryZoomLevel: 7,
        center: {
            lat: 57.708416,
            lng: 11.981097
        },
        styles: [
            {
                elementType: 'geometry',
                stylers: [{
                    color: '#eaeaea'
                }]
            },
            {
                elementType: 'labels.icon',
                stylers: [{
                    'visibility': 'off'
                }]
            },
            {
                elementType: 'labels.text.fill',
                stylers: [{
                    color: '#616161'
                }]
            },
            {
                elementType: 'labels.text.stroke',
                stylers: [{
                    color: '#eaeaea'
                }]
            },
            {
                featureType: 'administrative.land_parcel',
                elementType: 'labels.text.fill',
                stylers: [{
                    color: '#bdbdbd'
                }]
            },
            {
                featureType: 'poi',
                elementType: 'geometry',
                stylers: [{
                    color: '#eeeeee'
                }]
            },
            {
                featureType: 'poi',
                elementType: 'labels.text.fill',
                stylers: [{
                    color: '#757575'
                }]
            },
            {
                featureType: 'poi.park',
                elementType: 'geometry',
                stylers: [{
                    color: '#e5e5e5'
                }]
            },
            {
                featureType: 'poi.park',
                elementType: 'labels.text.fill',
                stylers: [{
                    color: '#9e9e9e'
                }]
            },
            {
                featureType: 'road',
                elementType: 'geometry',
                stylers: [{
                    color: '#ffffff'
                }]
            },
            {
                featureType: 'road.arterial',
                elementType: 'labels.text.fill',
                stylers: [{
                    color: '#757575'
                }]
            },
            {
                featureType: 'road.highway',
                elementType: 'geometry',
                stylers: [{
                    color: '#dadada'
                }]
            },
            {
                featureType: 'road.highway',
                elementType: 'labels.text.fill',
                stylers: [{
                    color: '#616161'
                }]
            },
            {
                featureType: 'road.local',
                elementType: 'labels.text.fill',
                stylers: [{
                    color: '#9e9e9e'
                }]
            },
            {
                featureType: 'transit.line',
                elementType: 'geometry',
                stylers: [{
                    color: '#e5e5e5'
                }]
            },
            {
                featureType: 'transit.station',
                elementType: 'geometry',
                stylers: [{
                    color: '#eeeeee'
                }]
            },
            {
                featureType: 'water',
                elementType: 'geometry',
                stylers: [{
                    color: '#c9c9c9'
                }]
            },
            {
                featureType: 'water',
                elementType: 'labels.text.fill',
                stylers: [{
                    color: '#9e9e9e'
                }]
            }
        ]
    };
    
  • URL: /components/raw/google-map/mapOptions.js
  • Filesystem Path: src/components/google-map/mapOptions.js
  • Size: 3.3 KB
  • Content:
    import { setInfoWindowContent } from '../index';
    import mapPin from './img/map-pin.svg';
    import mapPinSelected from './img/map-pin-selected.svg';
    
    const addMultipleMarkers = (dealers, map, infoWindow) => {
        let markersArr = [];
        const icon = {
            url: mapPin,
            size: new google.maps.Size(37, 52)
        };
    
        for (let dealer of dealers) {
            const { title, position } = dealer;
    
            const marker = addSingleMarker(position, title, icon, map);
            markersArr.push(marker);
    
            google.maps.event.addListener(marker, 'click', () => {
                markersArr.forEach((m) => m.setIcon(mapPin));
                marker.setIcon(mapPinSelected);
    
                infoWindow.setContent(setInfoWindowContent(dealer));
                infoWindow.open(map, marker);
    
                map.setCenter(marker.getPosition());
            });
        }
    };
    
    const addSingleMarker = (position, title, icon, map) => {
        const marker = new google.maps.Marker({
            position,
            title,
            icon,
            map
        });
    
        return marker;
    };
    
    export default {
        addSingleMarker,
        addMultipleMarkers
    };
    
  • URL: /components/raw/google-map/marker.js
  • Filesystem Path: src/components/google-map/marker.js
  • Size: 1.1 KB

React component for rendering Google Maps.

Can only be used by in the context of React, where it can be fed with places/markers via props.

For example used as part of the Find Installer component.