@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
163 lines (162 loc) • 5.72 kB
JavaScript
// SPDX-License-Identifier: Apache-2.0
import { Overlay } from 'ol';
import { v4 as uuidv4 } from 'uuid';
export var EntryInteractionType;
(function (EntryInteractionType) {
EntryInteractionType[EntryInteractionType["ENABLED"] = 0] = "ENABLED";
EntryInteractionType[EntryInteractionType["DISABLED"] = 1] = "DISABLED";
EntryInteractionType[EntryInteractionType["NOT_SHOWN"] = 2] = "NOT_SHOWN";
})(EntryInteractionType || (EntryInteractionType = {}));
/**
* Show a simple context menu when clicking on the map
*/
export class ContextMenu {
name;
_active = false;
isExclusive;
overlay;
container;
openEventListener;
closeEventListener;
context;
menuEntries;
menuEntryDivs = {};
get map() {
return this.context.mapManager.getMap();
}
constructor(context, menuEntries = [], isExclusive = false, openCondition = () => true) {
this.name = `contextmenu-${uuidv4()}`;
this.context = context;
this.menuEntries = menuEntries;
this.isExclusive = isExclusive;
this.container = document.createElement('div');
this.container.classList.add('contextmenu');
this.container.classList.add('hidden');
this.updateMenuEntries();
this.overlay = new Overlay({
element: this.container,
position: undefined
});
this.map.addOverlay(this.overlay);
this.openEventListener = (evt) => {
if (this.context.userInteractionManager.canListenerExecute('map.contextmenu', this.name)) {
this.handleContextmenuEvent(evt, openCondition);
}
};
this.closeEventListener = (_evt) => {
if (!this.active) {
return;
}
if (this.visible) {
this.closeMenu();
}
};
this.enable();
}
get visible() {
return !!this.overlay.getPosition();
}
get active() {
return this._active;
}
setActive(active) {
if (active) {
this.enable();
}
else {
this.disable();
}
}
enable() {
if (this.context.userInteractionManager.registerListener('map.contextmenu', this.isExclusive, this.name)) {
this.map.getViewport().addEventListener('contextmenu', this.openEventListener);
}
this.map.getViewport().addEventListener('click', this.closeEventListener);
this._active = true;
}
disable() {
this._active = false;
this.context.userInteractionManager.unregisterAllListenersOfTool(this.name);
this.map.getViewport().removeEventListener('contextmenu', this.openEventListener);
this.map.getViewport().removeEventListener('click', this.closeEventListener);
}
handleContextmenuEvent(evt, openCondition) {
if (!this._active) {
return;
}
if (this.visible) {
this.closeMenu();
}
evt.preventDefault();
const mapCoordinate = this.map.getCoordinateFromPixel([
// Pixel coordinates musbe in device independant pixels ("dips")
evt.offsetX / window.devicePixelRatio,
evt.offsetY / window.devicePixelRatio
]);
if (openCondition(evt, mapCoordinate)) {
this.prepareMenuEntries(evt, mapCoordinate);
this.openMenu(mapCoordinate);
}
else {
evt.stopPropagation();
}
}
updateMenuEntries() {
this.container.replaceChildren('');
this.menuEntryDivs = {};
for (const entry of this.menuEntries) {
const div = document.createElement('div');
div.classList.add('menu-entry');
div.innerHTML = this.context.i18nManager.getTranslation(entry.entry);
div.addEventListener('click', (evt) => this.onClickMenuEntry(evt, entry.callback));
this.container.appendChild(div);
this.menuEntryDivs[entry.entry] = div;
}
}
prepareMenuEntries(evt, mapCoordinate) {
for (const entry of this.menuEntries) {
const interactionType = entry.prepare?.(evt, mapCoordinate) ?? EntryInteractionType.ENABLED;
const menuEntryDiv = this.menuEntryDivs[entry.entry];
switch (interactionType) {
case EntryInteractionType.ENABLED:
menuEntryDiv.ariaDisabled = 'false';
menuEntryDiv.style.display = 'block';
break;
case EntryInteractionType.DISABLED:
menuEntryDiv.ariaDisabled = 'true';
menuEntryDiv.style.display = 'block';
break;
case EntryInteractionType.NOT_SHOWN:
menuEntryDiv.ariaDisabled = 'true';
menuEntryDiv.style.display = 'none';
break;
}
}
}
openMenu(mapCoordinates) {
if (!this.container.parentNode) {
// Add menu to the DOM
const popupDiv = document.getElementById('map-contextmenu');
popupDiv?.appendChild(this.container);
}
this.overlay.setPosition(mapCoordinates);
this.container.classList.remove('hidden');
}
closeMenu() {
this.overlay.setPosition(undefined);
this.container.classList.add('hidden');
}
onClickMenuEntry(evt, callback) {
try {
callback(evt, this.overlay.getPosition());
}
finally {
this.closeMenu();
}
}
remove() {
this.disable();
this.map.removeOverlay(this.overlay);
this.container.remove();
}
}