maplair
Version:
JavaScript library for mobile-friendly interactive maps
224 lines (223 loc) • 8 kB
JavaScript
import { Point } from "../geometry/Point.js";
import Browser_default from "../core/Browser.js";
import { disableClickPropagation, disableScrollPropagation, on, stop, stopPropagation } from "../dom/DomEvent.js";
import { create, getPosition, setPosition } from "../dom/DomUtil.js";
import { I18n } from "../core/I18n.js";
import { Layer } from "./Layer.js";
import { FeatureGroup } from "./FeatureGroup.js";
import { Path } from "./vector/Path.js";
import { DivOverlay } from "./DivOverlay.js";
//#region src/layer/Popup.js
var Popup = class Popup extends DivOverlay {
static {
this.setDefaultOptions({
pane: "popupPane",
offset: [0, 7],
maxWidth: 300,
minWidth: 50,
maxHeight: null,
autoPan: !Browser_default.reducedMotion,
autoPanPaddingTopLeft: null,
autoPanPaddingBottomRight: null,
autoPanPadding: [5, 5],
keepInView: false,
closeButton: true,
autoClose: true,
closeOnEscapeKey: true,
className: "",
trackResize: true
});
}
constructor(...args) {
super(...args);
this.callInitHooks(Popup);
}
openOn(map) {
map = arguments.length ? map : this._source._map;
if (!map.hasLayer(this) && map._popup && map._popup.options.autoClose) map.removeLayer(map._popup);
map._popup = this;
return super.openOn(map);
}
onAdd(map) {
super.onAdd(map);
map.fire("popupopen", { popup: this });
if (this._source) {
this._source.fire("popupopen", { popup: this }, true);
if (!(this._source instanceof Path)) this._source.on("preclick", stopPropagation);
}
}
onRemove(map) {
super.onRemove(map);
map.fire("popupclose", { popup: this });
if (this._source) {
this._source.fire("popupclose", { popup: this }, true);
if (!(this._source instanceof Path)) this._source.off("preclick", stopPropagation);
}
}
getEvents() {
const events = super.getEvents();
if (this.options.closeOnClick ?? this._map.options.closePopupOnClick) events.preclick = this.close;
if (this.options.keepInView) events.moveend = this._adjustPan;
return events;
}
_initLayout() {
const prefix = "maplair-popup", container = this._container = create("div", `${prefix} ${this.options.className || ""} maplair-zoom-animated`);
const wrapper = this._wrapper = create("div", `${prefix}-content-wrapper`, container);
this._contentNode = create("div", `${prefix}-content`, wrapper);
disableClickPropagation(container);
disableScrollPropagation(this._contentNode);
on(container, "contextmenu", stopPropagation);
this._tipContainer = create("div", `${prefix}-tip-container`, container);
this._tip = create("div", `${prefix}-tip`, this._tipContainer);
if (this.options.closeButton) {
const closeButton = this._closeButton = create("a", `${prefix}-close-button`, container);
closeButton.setAttribute("role", "button");
closeButton.setAttribute("aria-label", I18n.translate("Close popup"));
closeButton.href = "#close";
closeButton.innerHTML = "<span aria-hidden=\"true\">×</span>";
on(closeButton, "click", (ev) => {
ev.preventDefault();
this.close();
});
}
if (this.options.trackResize) {
this._resizeObserver = new ResizeObserver(((entries) => {
if (!this._map) return;
this._containerWidth = entries[0]?.contentRect?.width;
this._containerHeight = entries[0]?.contentRect?.height;
this._updateLayout();
this._updatePosition();
this._adjustPan();
}));
this._resizeObserver.observe(this._contentNode);
}
}
_updateLayout() {
const container = this._contentNode, style = container.style;
style.maxWidth = `${this.options.maxWidth}px`;
style.minWidth = `${this.options.minWidth}px`;
const maxHeight = this.options.maxHeight, scrolledClass = "maplair-popup-scrolled";
if (maxHeight && (this._containerHeight ?? container.offsetHeight) > maxHeight) {
style.height = `${maxHeight}px`;
container.classList.add(scrolledClass);
} else container.classList.remove(scrolledClass);
this._containerWidth = this._container.offsetWidth;
this._containerHeight = this._container.offsetHeight;
this._containerMarginBottom = parseInt(getComputedStyle(this._container).marginBottom, 10) || 0;
}
_animateZoom(e) {
const pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center), anchor = this._getAnchor();
setPosition(this._container, pos.add(anchor));
}
_adjustPan() {
if (!this.options.autoPan) return;
this._map._panAnim?.stop();
if (this._autopanning) {
this._autopanning = false;
return;
}
const map = this._map, marginBottom = this._containerMarginBottom ?? (parseInt(getComputedStyle(this._container).marginBottom, 10) || 0), containerHeight = this._containerHeight + marginBottom, containerWidth = this._containerWidth, layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
layerPos._add(getPosition(this._container));
const containerPos = map.layerPointToContainerPoint(layerPos), padding = new Point(this.options.autoPanPadding), paddingTL = new Point(this.options.autoPanPaddingTopLeft ?? padding), paddingBR = new Point(this.options.autoPanPaddingBottomRight ?? padding), size = map.getSize();
let dx = 0, dy = 0;
if (containerPos.x + containerWidth + paddingBR.x > size.x) dx = containerPos.x + containerWidth - size.x + paddingBR.x;
if (containerPos.x - dx - paddingTL.x < 0) dx = containerPos.x - paddingTL.x;
if (containerPos.y + containerHeight + paddingBR.y > size.y) dy = containerPos.y + containerHeight - size.y + paddingBR.y;
if (containerPos.y - dy - paddingTL.y < 0) dy = containerPos.y - paddingTL.y;
if (dx || dy) {
if (this.options.keepInView) this._autopanning = true;
map.fire("autopanstart").panBy([dx, dy]);
}
}
_getAnchor() {
return new Point(this._source?._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
}
static register(MaplairMap) {
MaplairMap.mergeOptions({ closePopupOnClick: true });
MaplairMap.include({
openPopup(popup, latlng, options) {
this._initOverlay(Popup, popup, latlng, options).openOn(this);
return this;
},
closePopup(popup) {
popup = arguments.length ? popup : this._popup;
popup?.close();
return this;
}
});
Layer.include({
bindPopup(content, options) {
this._popup = this._initOverlay(Popup, this._popup, content, options);
if (!this._popupHandlersAdded) {
this.on({
click: this._openPopup,
keypress: this._onKeyPress,
remove: this.closePopup,
move: this._movePopup
});
this._popupHandlersAdded = true;
}
return this;
},
unbindPopup() {
if (this._popup) {
this.off({
click: this._openPopup,
keypress: this._onKeyPress,
remove: this.closePopup,
move: this._movePopup
});
this._popupHandlersAdded = false;
this._popup = null;
}
return this;
},
openPopup(latlng) {
if (this._popup) {
if (!(this instanceof FeatureGroup)) this._popup._source = this;
if (this._popup._prepareOpen(latlng || this._latlng)) this._popup.openOn(this._map);
}
return this;
},
closePopup() {
this._popup?.close();
return this;
},
togglePopup() {
this._popup?.toggle(this);
return this;
},
isPopupOpen() {
return this._popup?.isOpen() ?? false;
},
setPopupContent(content) {
this._popup?.setContent(content);
return this;
},
getPopup() {
return this._popup;
},
_openPopup(e) {
if (!this._popup || !this._map) return;
stop(e);
const target = e.propagatedFrom ?? e.target;
if (this._popup._source === target && !(target instanceof Path)) {
if (this._map.hasLayer(this._popup)) this.closePopup();
else this.openPopup(e.latlng);
return;
}
this._popup._source = target;
this.openPopup(e.latlng);
},
_movePopup(e) {
this._popup.setLatLng(e.latlng);
},
_onKeyPress(e) {
if (e.originalEvent.code === "Enter") this._openPopup(e);
}
});
}
};
//#endregion
export { Popup };
//# sourceMappingURL=Popup.js.map