@paperbits/core
Version:
Paperbits core components.
137 lines (111 loc) • 5.09 kB
text/typescript
import * as ko from "knockout";
import { Loader, LoaderOptions } from "@googlemaps/js-api-loader";
import { Events } from "@paperbits/common/events";
import { MapRuntimeConfig } from "./runtime/mapRuntimeConfig";
export class GooglmapsBindingHandler {
constructor() {
const attach = this.attach.bind(this);
ko.bindingHandlers["googlemap"] = {
update(element: HTMLElement, valueAccessor: () => MapRuntimeConfig): void {
const configuration = valueAccessor();
try {
attach(element, ko.toJS(configuration));
}
catch (error) {
console.warn(`Unable to load map.`);
}
}
};
}
private async attach(element: HTMLElement, configuration: MapRuntimeConfig): Promise<void> {
if (!configuration.apiKey) {
return;
}
const options: Partial<LoaderOptions> = {/* todo */ };
const loader = new Loader({ apiKey: configuration.apiKey, ...options });
loader.deleteScript();
await loader.load();
const markerWidth = 50;
const markerHeight = 50;
const mapOptions: google.maps.MapOptions = {};
const map = new google.maps.Map(element, mapOptions);
if (!configuration.zoom) {
configuration.zoom = 17;
}
else if (typeof configuration.zoom === "string") {
configuration.zoom = parseInt(configuration.zoom);
}
map.setOptions({
streetViewControl: false,
mapTypeControl: false,
scaleControl: true,
rotateControl: false,
scrollwheel: false,
disableDoubleClickZoom: true,
draggable: false,
disableDefaultUI: true,
mapTypeId: configuration.mapType,
zoom: configuration.zoom,
styles: configuration.customizations
});
class PopupAnchor extends google.maps.OverlayView {
private readonly content: HTMLElement;
constructor(private readonly position: google.maps.LatLng) {
super();
this.content = document.createElement("div");
}
/** Called when the popup is added to the map. */
public onAdd(): void {
PopupAnchor.preventMapHitsAndGesturesFrom(this.content);
this.content.setAttribute("data-toggle", "popup");
this.content.setAttribute("data-target", `#${configuration.markerPopupKey.replace("popups/", "popups")}`);
this.content.setAttribute("data-position", "top");
this.getPanes().floatPane.appendChild(this.content);
document.dispatchEvent(new CustomEvent(Events.PopupRequest, { detail: configuration.markerPopupKey }));
}
/** Called each frame when the popup needs to draw itself. */
public draw(): void {
const elementPosition = this.getProjection().fromLatLngToDivPixel(this.position);
this.content.style.left = elementPosition.x - Math.floor(markerWidth / 2) + "px";
this.content.style.top = elementPosition.y - Math.floor(markerHeight / 2) + "px";
this.content.style.width = markerWidth + "px";
this.content.style.height = markerHeight + "px";
document.dispatchEvent(new CustomEvent("onPopupRepositionRequested", {
detail: {
element: this.content,
placement: "top"
// offsetX:
// offsetY:
}
}));
}
}
const position = new google.maps.LatLng({ lat: configuration.location.lat, lng: configuration.location.lng });
const marker: google.maps.Marker = new google.maps.Marker();
marker.setMap(map);
if (configuration.markerIcon) {
const icon: google.maps.Icon = {
url: configuration.markerIcon,
scaledSize: new google.maps.Size(markerWidth, markerHeight)
};
marker.setIcon(icon);
}
marker.setPosition(position);
map.setCenter(position);
if (configuration.markerPopupKey) {
const anchor = new PopupAnchor(position);
anchor.setMap(map);
marker.addListener(Events.Click, () => document.dispatchEvent(new CustomEvent(Events.PopupRequest, { detail: configuration.markerPopupKey })));
}
else {
const infowindow = new google.maps.InfoWindow();
infowindow.setContent(configuration.caption);
if (configuration.caption.length > 0) {
infowindow.open(map, marker);
}
else {
infowindow.close();
}
}
}
}