forked from MapComplete/MapComplete
		
	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…
	
	Add table
		Add a link
		
	
		Reference in a new issue