@hsluoyz/bingmaps-react
Version:
An easy to use Bing Maps component for React apps
167 lines (137 loc) • 4.8 kB
JavaScript
// DEPENDENCIES
import React, { useEffect, useRef, useCallback } from "react";
export default function BingMapsReact({
bingMapsKey,
height,
mapOptions,
pushPins,
pushPinsWithInfoboxes,
viewOptions,
width
}) {
// refs
const mapContainer = useRef(null);
const map = useRef(null); // removes pushpins
function removePushpins(map, Maps) {
for (var i = map.entities.getLength() - 1; i >= 0; i--) {
var pushpin = map.entities.get(i);
if (pushpin instanceof Maps.Pushpin) {
map.entities.removeAt(i);
}
}
} // add pushpins with infoboxes
const addPushpinsWithInfoboxes = useCallback((pushPinsToAdd, infobox, map, Maps) => {
removePushpins(map, Maps);
pushPinsToAdd.forEach(pushPin => {
const newPin = new Maps.Pushpin(pushPin.center, pushPin.options);
newPin.metadata = pushPin.metadata;
Maps.Events.addHandler(newPin, "mouseover", e => {
infobox.setOptions({
location: e.target.getLocation(),
title: e.target.metadata.title,
description: e.target.metadata.description,
visible: true,
htmlContent: pushPin.infoboxHtml
});
});
Maps.Events.addHandler(newPin, "mouseout", e => {
infobox.setOptions({
visible: false
});
});
map.entities.push(newPin);
});
}, []); // add pushpins
function addPushpins(pushPinsToAdd, map, Maps) {
pushPinsToAdd.forEach(pushPin => {
const newPin = new Maps.Pushpin(pushPin.center, pushPin.options);
map.entities.push(newPin);
});
} // set view options
function setMapViewOptions(map, viewOptions, Maps) {
const options = { ...viewOptions
};
if (viewOptions.mapTypeId) {
options.mapTypeId = Maps.MapTypeId[viewOptions.mapTypeId];
}
if (viewOptions.hideRoadLabels) {
options.labelOverlay = Maps.LabelOverlay.hidden;
}
map.setView(options);
} // set map options
function setMapOptions(map, mapOptions, Maps) {
const options = { ...mapOptions
}; // some map options require values from the Maps class
// these conditional statements handle those cases
if (mapOptions.navigationBarMode) {
options.navigationBarMode = Maps.NavigationBarMode[mapOptions.navigationBarMode];
}
if (mapOptions.navigationBarOrientation) {
options.navigationBarOrientation = Maps.NavigationBarOrientation[mapOptions.navigationBarOrientation];
}
if (mapOptions.supportedMapTypes) {
options.supportedMapTypes = mapOptions.supportedMapTypes.map(type => Maps.MapTypeId[type]);
}
map.setOptions(options);
} // make map, set options, add pins
const makeMap = useCallback(() => {
const {
Maps
} = window.Microsoft; // only make a new map if one doesn't already exist
if (!map.current) {
map.current = new Maps.Map(mapContainer.current);
} // set viewOptions, if any
if (viewOptions) {
setMapViewOptions(map.current, viewOptions, Maps);
} // set mapOptions, if any
if (mapOptions) {
setMapOptions(map.current, mapOptions, Maps);
} // add push pins, if any
if (pushPins) {
addPushpins(pushPins, map.current, Maps);
} // add infoboxes, if any
if (pushPinsWithInfoboxes) {
const infobox = new Maps.Infobox(map.current.getCenter(), {
visible: false
});
infobox.setMap(map.current);
addPushpinsWithInfoboxes(pushPinsWithInfoboxes, infobox, map.current, Maps);
}
}, [mapOptions, viewOptions, pushPins, pushPinsWithInfoboxes, addPushpinsWithInfoboxes]); // attach makeMap to window for the bingmaps script callback
window.makeMap = makeMap; // append bingmaps script to body
const appendBingMapsScript = useCallback(() => {
const scriptTag = document.createElement("script");
scriptTag.setAttribute("defer", "");
scriptTag.setAttribute("async", "");
scriptTag.setAttribute("type", "text/javascript");
scriptTag.setAttribute("src", `https://www.bing.com/api/maps/mapcontrol?key=${bingMapsKey}&callback=makeMap`);
document.body.appendChild(scriptTag);
}, [bingMapsKey]); // make a new map if bingmaps classes already exist
// append the script if not
const initMap = useCallback(() => {
if (window.Microsoft && window.Microsoft.Maps) {
makeMap();
} else {
appendBingMapsScript();
}
}, [appendBingMapsScript, makeMap]);
useEffect(() => {
initMap();
}, [bingMapsKey, initMap]);
return React.createElement("div", {
ref: mapContainer,
style: {
height: height,
width: width
}
});
}
BingMapsReact.defaultProps = {
bingMapsKey: null,
mapOptions: null,
height: "100%",
pushPins: null,
pushPinsWithInfoboxes: null,
viewOptions: null,
width: "100%"
};