Right-clicking an element (if no presets are defined) will open up the popup of the element
This commit is contained in:
parent
98866b4a57
commit
057c3fde4f
4 changed files with 90 additions and 55 deletions
|
@ -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,
|
||||||
|
|
12
Utils.ts
12
Utils.ts
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue