UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

163 lines (162 loc) 5.72 kB
// 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(); } }