maplair
Version:
JavaScript library for mobile-friendly interactive maps
228 lines (227 loc) • 7.72 kB
JavaScript
import { stamp } from "../core/Util.js";
import { Point } from "../geometry/Point.js";
import { off, on } from "../dom/DomEvent.js";
import { create, setPosition } from "../dom/DomUtil.js";
import { Layer } from "./Layer.js";
import { FeatureGroup } from "./FeatureGroup.js";
import { DivOverlay } from "./DivOverlay.js";
//#region src/layer/Tooltip.js
var Tooltip = class Tooltip extends DivOverlay {
static {
this.setDefaultOptions({
pane: "tooltipPane",
offset: [0, 0],
direction: "auto",
permanent: false,
sticky: false,
opacity: .9
});
}
constructor(...args) {
super(...args);
this.callInitHooks(Tooltip);
}
onAdd(map) {
super.onAdd(map);
this.setOpacity(this.options.opacity);
map.fire("tooltipopen", { tooltip: this });
if (this._source) {
this.addEventParent(this._source);
this._source.fire("tooltipopen", { tooltip: this }, true);
}
}
onRemove(map) {
super.onRemove(map);
map.fire("tooltipclose", { tooltip: this });
if (this._source) {
this.removeEventParent(this._source);
this._source.fire("tooltipclose", { tooltip: this }, true);
}
}
getEvents() {
const events = super.getEvents();
if (!this.options.permanent) events.preclick = this.close;
return events;
}
_initLayout() {
const className = `maplair-tooltip ${this.options.className || ""} maplair-zoom-${this._zoomAnimated ? "animated" : "hide"}`;
this._contentNode = this._container = create("div", className);
this._container.setAttribute("role", "tooltip");
this._container.setAttribute("id", `maplair-tooltip-${stamp(this)}`);
}
_updateLayout() {}
_adjustPan() {}
_setPosition(pos) {
let subX, subY, direction = this.options.direction;
const map = this._map, container = this._container, centerPoint = map.latLngToContainerPoint(map.getCenter()), tooltipPoint = map.layerPointToContainerPoint(pos), tooltipWidth = container.offsetWidth, tooltipHeight = container.offsetHeight, offset = new Point(this.options.offset), anchor = this._getAnchor();
if (direction === "top") {
subX = tooltipWidth / 2;
subY = tooltipHeight;
} else if (direction === "bottom") {
subX = tooltipWidth / 2;
subY = 0;
} else if (direction === "center") {
subX = tooltipWidth / 2;
subY = tooltipHeight / 2;
} else if (direction === "right") {
subX = 0;
subY = tooltipHeight / 2;
} else if (direction === "left") {
subX = tooltipWidth;
subY = tooltipHeight / 2;
} else if (tooltipPoint.x < centerPoint.x) {
direction = "right";
subX = 0;
subY = tooltipHeight / 2;
} else {
direction = "left";
subX = tooltipWidth + (offset.x + anchor.x) * 2;
subY = tooltipHeight / 2;
}
pos = pos.subtract(new Point(subX, subY, true)).add(offset).add(anchor);
container.classList.remove("maplair-tooltip-right", "maplair-tooltip-left", "maplair-tooltip-top", "maplair-tooltip-bottom");
container.classList.add(`maplair-tooltip-${direction}`);
setPosition(container, pos);
}
_updatePosition() {
const pos = this._map.latLngToLayerPoint(this._latlng);
this._setPosition(pos);
}
setOpacity(opacity) {
this.options.opacity = opacity;
if (this._container) this._container.style.opacity = opacity;
}
_animateZoom(e) {
const pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
this._setPosition(pos);
}
_getAnchor() {
return new Point(this._source?._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
}
static register(MaplairMap) {
MaplairMap.include({
openTooltip(tooltip, latlng, options) {
this._initOverlay(Tooltip, tooltip, latlng, options).openOn(this);
return this;
},
closeTooltip(tooltip) {
tooltip.close();
return this;
}
});
Layer.include({
bindTooltip(content, options) {
if (this._tooltip && this.isTooltipOpen()) this.unbindTooltip();
this._tooltip = this._initOverlay(Tooltip, this._tooltip, content, options);
this._initTooltipInteractions();
if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) this.openTooltip();
return this;
},
unbindTooltip() {
if (this._tooltip) {
this._initTooltipInteractions(true);
this.closeTooltip();
this._tooltip = null;
}
return this;
},
_initTooltipInteractions(remove) {
if (!remove && this._tooltipHandlersAdded) return;
const onOff = remove ? "off" : "on", events = {
remove: this.closeTooltip,
move: this._moveTooltip
};
if (!this._tooltip.options.permanent) {
events.pointerover = this._openTooltip;
events.pointerout = this.closeTooltip;
events.click = this._openTooltip;
if (this._map) this._addFocusListeners(remove);
else events.add = () => this._addFocusListeners(remove);
} else events.add = this._openTooltip;
if (this._tooltip.options.sticky) events.pointermove = this._moveTooltip;
this[onOff](events);
this._tooltipHandlersAdded = !remove;
},
openTooltip(latlng) {
if (this._tooltip) {
if (!(this instanceof FeatureGroup)) this._tooltip._source = this;
if (this._tooltip._prepareOpen(latlng)) {
this._tooltip.openOn(this._map);
if (this.getElement) this._setAriaDescribedByOnLayer(this);
else if (this.eachLayer) this.eachLayer(this._setAriaDescribedByOnLayer, this);
}
}
return this;
},
closeTooltip() {
if (this._tooltip) return this._tooltip.close();
},
toggleTooltip() {
this._tooltip?.toggle(this);
return this;
},
isTooltipOpen() {
return this._tooltip.isOpen();
},
setTooltipContent(content) {
this._tooltip?.setContent(content);
return this;
},
getTooltip() {
return this._tooltip;
},
_addFocusListeners(remove) {
if (this.getElement) this._addFocusListenersOnLayer(this, remove);
else if (this.eachLayer) this.eachLayer((layer) => this._addFocusListenersOnLayer(layer, remove), this);
},
_addFocusListenersOnLayer(layer, remove) {
const el = typeof layer.getElement === "function" && layer.getElement();
if (el) {
const toggle = remove ? off : on;
if (!remove) {
el._maplair_focus_handler && off(el, "focus", el._maplair_focus_handler, this);
el._maplair_focus_handler = () => {
if (this._tooltip) {
this._tooltip._source = layer;
this.openTooltip();
}
};
}
el._maplair_focus_handler && toggle(el, "focus", el._maplair_focus_handler, this);
toggle(el, "blur", this.closeTooltip, this);
if (remove) delete el._maplair_focus_handler;
}
},
_setAriaDescribedByOnLayer(layer) {
(typeof layer.getElement === "function" && layer.getElement())?.setAttribute?.("aria-describedby", this._tooltip._container.id);
},
_openTooltip(e) {
if (!this._tooltip || !this._map) return;
if (this._map.dragging?.moving()) {
if (e.type === "add" && !this._moveEndOpensTooltip) {
this._moveEndOpensTooltip = true;
this._map.once("moveend", () => {
this._moveEndOpensTooltip = false;
this._openTooltip(e);
});
}
return;
}
this._tooltip._source = e.propagatedFrom ?? e.target;
this.openTooltip(this._tooltip.options.sticky ? e.latlng : void 0);
},
_moveTooltip(e) {
let latlng = e.latlng, containerPoint, layerPoint;
if (this._tooltip.options.sticky && e.originalEvent) {
containerPoint = this._map.pointerEventToContainerPoint(e.originalEvent);
layerPoint = this._map.containerPointToLayerPoint(containerPoint);
latlng = this._map.layerPointToLatLng(layerPoint);
}
this._tooltip.setLatLng(latlng);
}
});
}
};
//#endregion
export { Tooltip };
//# sourceMappingURL=Tooltip.js.map