Right-clicking an element (if no presets are defined) will open up the popup of the element

This commit is contained in:
Pieter Vander Vennet 2023-01-04 17:41:11 +01:00
parent 98866b4a57
commit 057c3fde4f
4 changed files with 90 additions and 55 deletions

View file

@ -4,8 +4,10 @@ import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
import { ElementStorage } from "../../Logic/ElementStorage" import { ElementStorage } from "../../Logic/ElementStorage"
import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource" import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource"
import ScrollableFullScreen from "../Base/ScrollableFullScreen" import ScrollableFullScreen from "../Base/ScrollableFullScreen"
import { LeafletMouseEvent } from "leaflet" import { LeafletMouseEvent, PathOptions } from "leaflet"
import Hash from "../../Logic/Web/Hash" import Hash from "../../Logic/Web/Hash"
import { BBox } from "../../Logic/BBox"
import { Utils } from "../../Utils"
/* /*
// import 'leaflet-polylineoffset'; // import 'leaflet-polylineoffset';
We don't actually import it here. It is imported in the 'MinimapImplementation'-class, which'll result in a patched 'L' object. We don't actually import it here. It is imported in the 'MinimapImplementation'-class, which'll result in a patched 'L' object.
@ -47,6 +49,7 @@ export default class ShowDataLayerImplementation {
string, string,
{ feature: any; activateFunc: (event: LeafletMouseEvent) => void } { feature: any; activateFunc: (event: LeafletMouseEvent) => void }
>() >()
private readonly showDataLayerid: number private readonly showDataLayerid: number
private readonly createPopup: ( private readonly createPopup: (
tags: UIEventSource<any>, tags: UIEventSource<any>,
@ -81,7 +84,7 @@ export default class ShowDataLayerImplementation {
} }
const self = this const self = this
options.leafletMap.addCallback((_) => { options.leafletMap.addCallback(() => {
return self.update(options) return self.update(options)
}) })
@ -171,17 +174,8 @@ export default class ShowDataLayerImplementation {
} }
const self = this const self = this
const data = {
type: "FeatureCollection", this.geoLayer = new L.LayerGroup()
features: [],
}
// @ts-ignore
this.geoLayer = L.geoJSON(data, {
style: (feature) => self.createStyleFor(feature),
pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng),
onEachFeature: (feature, leafletLayer) =>
self.postProcessFeature(feature, leafletLayer),
})
const selfLayer = this.geoLayer const selfLayer = this.geoLayer
const allFeats = this._features.features.data const allFeats = this._features.features.data
@ -189,6 +183,31 @@ export default class ShowDataLayerImplementation {
if (feat === undefined) { if (feat === undefined) {
continue continue
} }
// Why not one geojson layer with _all_ features, and attaching a right-click onto every feature individually?
// Because that somehow doesn't work :(
const feature = feat
const geojsonLayer = L.geoJSON(feature, {
style: (feature) => <PathOptions>self.createStyleFor(feature),
pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng),
onEachFeature: (feature, leafletLayer) =>
self.postProcessFeature(feature, leafletLayer),
})
if (feature.geometry.type === "Point") {
geojsonLayer.on({
contextmenu: (e) => {
const o = self.leafletLayersPerId.get(feature?.properties?.id)
o?.activateFunc(<LeafletMouseEvent>e)
Utils.preventDefaultOnMouseEvent(e.originalEvent)
},
dblclick: (e) => {
const o = self.leafletLayersPerId.get(feature?.properties?.id)
o?.activateFunc(<LeafletMouseEvent>e)
Utils.preventDefaultOnMouseEvent(e.originalEvent)
},
})
}
this.geoLayer.addLayer(geojsonLayer)
try { try {
if (feat.geometry.type === "LineString") { if (feat.geometry.type === "LineString") {
const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates) const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates)
@ -229,7 +248,7 @@ export default class ShowDataLayerImplementation {
return self.geoLayer !== selfLayer return self.geoLayer !== selfLayer
}) })
} else { } else {
this.geoLayer.addData(feat) geojsonLayer.addData(feat)
} }
} catch (e) { } catch (e) {
console.error( console.error(
@ -242,14 +261,14 @@ export default class ShowDataLayerImplementation {
} }
} }
if (options.zoomToFeatures ?? false) { if ((options.zoomToFeatures ?? false) && allFeats.length > 0) {
if (this.geoLayer.getLayers().length > 0) { let bound = undefined
try { for (const feat of allFeats) {
const bounds = this.geoLayer.getBounds() const fbound = BBox.get(feat)
mp.fitBounds(bounds, { animate: false }) bound = bound?.unionWith(fbound) ?? fbound
} catch (e) { }
console.debug("Invalid bounds", e) if (bound !== undefined) {
} mp.fitBounds(bound?.toLeaflet(), { animate: false })
} }
} }
@ -312,29 +331,7 @@ export default class ShowDataLayerImplementation {
icon: L.divIcon(style), icon: L.divIcon(style),
}) })
} }
private createActivateFunction(feature, key: string, layer: LayerConfig): (event) => void {
/**
* Post processing - basically adding the popup
* @param feature
* @param leafletLayer
* @private
*/
private postProcessFeature(feature, leafletLayer: L.Evented) {
const layer: LayerConfig = this._layerToShow
if (layer.title === undefined || !this._enablePopups) {
// No popup action defined -> Don't do anything
// or probably a map in the popup - no popups needed!
return
}
const key = feature.properties.id
if (this.leafletLayersPerId.has(key)) {
const activate = this.leafletLayersPerId.get(key)
leafletLayer.addEventListener("click", activate.activateFunc)
if (Hash.hash.data === key) {
activate.activateFunc(null)
}
return
}
let infobox: ScrollableFullScreen = undefined let infobox: ScrollableFullScreen = undefined
const self = this const self = this
@ -354,17 +351,36 @@ export default class ShowDataLayerImplementation {
self._selectedElement.setData( self._selectedElement.setData(
self.allElements.ContainingFeatures.get(feature.id) ?? feature self.allElements.ContainingFeatures.get(feature.id) ?? feature
) )
event?.originalEvent?.preventDefault() }
event?.originalEvent?.stopPropagation() return activate
event?.originalEvent?.stopImmediatePropagation() }
if (event?.originalEvent) { /**
// This is a total workaround, as 'preventDefault' and everything above seems to be not working * Post processing - basically adding the popup
event.originalEvent["dismissed"] = true * @param feature
} * @param leafletLayer
* @private
*/
private postProcessFeature(feature, leafletLayer: L.Evented) {
const layer: LayerConfig = this._layerToShow
if (layer.title === undefined || !this._enablePopups) {
// No popup action defined -> Don't do anything
// or probably a map in the popup - no popups needed!
return
}
const key = feature.properties.id
let activate: (event) => void
if (this.leafletLayersPerId.has(key)) {
activate = this.leafletLayersPerId.get(key).activateFunc
} else {
activate = this.createActivateFunction(feature, key, layer)
} }
leafletLayer.addEventListener("click", activate) // We also have to open on rightclick, doubleclick, ... as users sometimes do this. See #1219
leafletLayer.on({
dblclick: activate,
contextmenu: activate,
click: activate,
})
// Add the feature to the index to open the popup when needed // Add the feature to the index to open the popup when needed
this.leafletLayersPerId.set(key, { this.leafletLayersPerId.set(key, {
feature: feature, feature: feature,

View file

@ -900,7 +900,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
url: string, url: string,
maxCacheTimeMs: number, maxCacheTimeMs: number,
headers?: any headers?: any
): Promise<any | { error: string; url: string; statuscode?: number }> { ): Promise<{ content: any } | { error: string; url: string; statuscode?: number }> {
const cached = Utils._download_cache.get(url) const cached = Utils._download_cache.get(url)
if (cached !== undefined) { if (cached !== undefined) {
if (new Date().getTime() - cached.timestamp <= maxCacheTimeMs) { if (new Date().getTime() - cached.timestamp <= maxCacheTimeMs) {
@ -1074,6 +1074,16 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
) )
} }
public static preventDefaultOnMouseEvent(event: any) {
event?.originalEvent?.preventDefault()
event?.originalEvent?.stopPropagation()
event?.originalEvent?.stopImmediatePropagation()
if (event?.originalEvent) {
// This is a total workaround, as 'preventDefault' and everything above seems to be not working
event.originalEvent["dismissed"] = true
}
}
public static OsmChaLinkFor(daysInThePast, theme = undefined): string { public static OsmChaLinkFor(daysInThePast, theme = undefined): string {
const now = new Date() const now = new Date()
const lastWeek = new Date(now.getTime() - daysInThePast * 24 * 60 * 60 * 1000) const lastWeek = new Date(now.getTime() - daysInThePast * 24 * 60 * 60 * 1000)

View file

@ -1876,6 +1876,11 @@ body {
box-sizing: initial !important; box-sizing: initial !important;
} }
.leaflet-marker-icon img {
-webkit-touch-callout: none;
/* prevent callout to copy image, etc when tap to hold */
}
.leaflet-control-attribution { .leaflet-control-attribution {
display: block ruby; display: block ruby;
} }

View file

@ -113,6 +113,10 @@ body {
box-sizing: initial !important; box-sizing: initial !important;
} }
.leaflet-marker-icon img {
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
}
.leaflet-control-attribution { .leaflet-control-attribution {
display: block ruby; display: block ruby;
} }