UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

167 lines (166 loc) 5.57 kB
import { Overlay } from 'ol'; import UserInteractionManager from '../../../tools/state/userInteractionManager'; import MapManager from '../../../tools/state/mapManager'; import { v4 as uuidv4 } from 'uuid'; /** * Show a simple context menu when clicking on the map */ export class ContextMenu { constructor(menuEntries = [], isExclusive = false, openCondition = () => true) { Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_active", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "isExclusive", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "map", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "overlay", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "container", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "userInteractionManager", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "openEventListener", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "closeEventListener", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.name = `contextmenu-${uuidv4()}`; this.map = MapManager.getInstance().getMap(); this.userInteractionManager = UserInteractionManager.getInstance(); this.isExclusive = isExclusive; this.container = document.createElement('div'); this.container.classList.add('contextmenu'); this.container.classList.add('hidden'); this.updateMenuEntries(menuEntries); this.overlay = new Overlay({ element: this.container, position: undefined }); this.map.addOverlay(this.overlay); this.openEventListener = (evt) => { if (this.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) { active ? this.enable() : this.disable(); } enable() { if (this.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.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([evt.offsetX, evt.offsetY]); if (openCondition(evt, mapCoordinate)) { this.openMenu(mapCoordinate); } else { evt.stopPropagation(); } } updateMenuEntries(menuEntries) { this.container.replaceChildren(''); menuEntries.forEach((entry) => { const div = document.createElement('div'); div.classList.add('menu-entry'); div.innerHTML = entry.entry; div.addEventListener('click', (evt) => this.onClickMenuEntry(evt, entry.callback)); this.container.appendChild(div); }); } 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(); } }