forked from MapComplete/MapComplete
		
	Add search, a few flow updates
This commit is contained in:
		
							parent
							
								
									7b9ab77bda
								
							
						
					
					
						commit
						c87c014045
					
				
					 14 changed files with 345 additions and 17 deletions
				
			
		|  | @ -191,9 +191,15 @@ export class FilteredLayer { | |||
|                 layer.on("click", function(e) { | ||||
|                     console.log("Selected ", feature) | ||||
|                     self._selectedElement.setData(feature.properties); | ||||
|                      | ||||
|                     L.DomEvent.stop(e); // Marks the event as consumed
 | ||||
|                     const uiElement = self._showOnPopup.data(); | ||||
|                     layer.bindPopup(uiElement.Render()).openPopup(); | ||||
|                     const popup = L.popup(); | ||||
|                     popup.setContent(uiElement.Render()); | ||||
|                     layer.bindPopup(popup).openPopup(); | ||||
|                     popup.onclose(() => { | ||||
|                         layer.removePopup(popup) | ||||
|                     }); | ||||
|                     uiElement.Update(); | ||||
|                     uiElement.Activate(); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										18
									
								
								Logic/Geocoding.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Logic/Geocoding.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| import * as $ from "jquery" | ||||
| import {UIEventSource} from "../UI/UIEventSource"; | ||||
| 
 | ||||
| export class Geocoding { | ||||
| 
 | ||||
|     private static readonly host = "https://nominatim.openstreetmap.org/search?"; | ||||
| 
 | ||||
|     static Search(query: string, currentLocation: UIEventSource<{ lat: number, lon: number }>, | ||||
|                   handleResult: ((places: { display_name: string, lat: number, lon: number, boundingbox : number[] }[]) => void)) { | ||||
|         $.getJSON( | ||||
|             Geocoding.host + "format=json&accept-language=nl&q=" + query, | ||||
|             function (data) { | ||||
|                handleResult(data); | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | @ -24,6 +24,7 @@ export class StrayClickHandler { | |||
|         const self = this; | ||||
|         const map = basemap.map; | ||||
|         basemap.LastClickLocation.addCallback(function (lastClick) { | ||||
|             selectElement.setData(undefined); | ||||
| 
 | ||||
|             if (self._lastMarker !== undefined) { | ||||
|                 map.removeLayer(self._lastMarker); | ||||
|  | @ -36,8 +37,9 @@ export class StrayClickHandler { | |||
|             self._lastMarker.addTo(map); | ||||
|             self._lastMarker.bindPopup(popup).openPopup(); | ||||
| 
 | ||||
| 
 | ||||
|             self._lastMarker.on("click", () => { | ||||
|                 leftMessage.setData(self._uiToShow); | ||||
|             }); | ||||
| 
 | ||||
|         }); | ||||
| 
 | ||||
|  |  | |||
|  | @ -75,5 +75,8 @@ Images from Wikipedia/Wikimedia | |||
| https://commons.wikimedia.org/wiki/File:Camera_font_awesome.svg | ||||
| Camera Icon, Dave Gandy, CC-BY-SA 3.0 | ||||
| 
 | ||||
| https://commons.wikimedia.org/wiki/File:OOjs_UI_indicator_search-rtl.svg | ||||
| Search Icon, MIT | ||||
|  	 | ||||
| https://commons.wikimedia.org/wiki/File:Home-icon.svg | ||||
| Home icon by Timothy Miller, CC-BY-SA 3.0 | ||||
|  |  | |||
							
								
								
									
										46
									
								
								UI/Base/TextField.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								UI/Base/TextField.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | |||
| import {UIElement} from "../UIElement"; | ||||
| import {UIEventSource} from "../UIEventSource"; | ||||
| 
 | ||||
| 
 | ||||
| export class TextField extends UIElement { | ||||
| 
 | ||||
|     public value = new UIEventSource(""); | ||||
|     /** | ||||
|      * Pings and has the value data | ||||
|      */ | ||||
|     public enterPressed = new UIEventSource<string>(undefined); | ||||
|     private _placeholder: UIEventSource<string>; | ||||
| 
 | ||||
|     constructor(placeholder : UIEventSource<string>) { | ||||
|         super(placeholder); | ||||
|         this._placeholder = placeholder; | ||||
|     } | ||||
| 
 | ||||
|     protected InnerRender(): string { | ||||
|         return "<form onSubmit='return false' class='form-text-field'>" + | ||||
|             "<input type='text' placeholder='"+this._placeholder.data+"' id='text-" + this.id + "'>" + | ||||
|             "</form>"; | ||||
|     } | ||||
| 
 | ||||
|     InnerUpdate(htmlElement: HTMLElement) { | ||||
|         super.InnerUpdate(htmlElement); | ||||
|         const field = document.getElementById('text-' + this.id); | ||||
|         const self = this; | ||||
|         field.oninput = () => { | ||||
|             self.value.setData(field.value); | ||||
|         }; | ||||
| 
 | ||||
|         field.addEventListener("keyup", function (event) { | ||||
|             if (event.key === "Enter") { | ||||
|                 self.enterPressed.setData(field.value); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     Clear() { | ||||
|         const field = document.getElementById('text-' + this.id); | ||||
|         if (field !== undefined) { | ||||
|             field.value = ""; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										73
									
								
								UI/SearchAndGo.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								UI/SearchAndGo.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | |||
| import {UIElement} from "./UIElement"; | ||||
| import {TextField} from "./Base/TextField"; | ||||
| import {VariableUiElement} from "./Base/VariableUIElement"; | ||||
| import {UIEventSource} from "./UIEventSource"; | ||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | ||||
| import {Geocoding} from "../Logic/Geocoding"; | ||||
| import {Basemap} from "../Logic/Basemap"; | ||||
| import {VerticalCombine} from "./Base/VerticalCombine"; | ||||
| 
 | ||||
| 
 | ||||
| export class SearchAndGo extends UIElement { | ||||
| 
 | ||||
|     private _placeholder = new UIEventSource("Ga naar een locatie...") | ||||
|     private _searchField = new TextField(this._placeholder); | ||||
| 
 | ||||
|     private _foundEntries = new UIEventSource([]); | ||||
|     private _map: Basemap; | ||||
|     private _goButton = new FixedUiElement("<img class='search-go' src='./assets/search.svg' alt='GO'>"); | ||||
| 
 | ||||
|     constructor(map: Basemap) { | ||||
|         super(undefined); | ||||
|         this._map = map; | ||||
|         this.ListenTo(this._foundEntries); | ||||
| 
 | ||||
|         const self = this; | ||||
|         this._searchField.enterPressed.addCallback(() => { | ||||
|             self.RunSearch(); | ||||
|         }); | ||||
| 
 | ||||
|         this._goButton.onClick(function () { | ||||
|             self.RunSearch(); | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     // Triggered by 'enter' or onclick
 | ||||
|     private RunSearch() { | ||||
|         const searchString = this._searchField.value.data; | ||||
|         this._searchField.Clear(); | ||||
|         this._placeholder.setData("Bezig met zoeken..."); | ||||
|         const self = this; | ||||
|         Geocoding.Search(searchString, undefined, (result) => { | ||||
| 
 | ||||
|             const bb = result[0].boundingbox; | ||||
|             const bounds = [ | ||||
|                 [bb[0], bb[2]], | ||||
|                 [bb[1], bb[3]] | ||||
|             ] | ||||
|             self._map.map.fitBounds(bounds); | ||||
|             this._placeholder.setData("Ga naar locatie..."); | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     protected InnerRender(): string { | ||||
|         // "<img class='search' src='./assets/search.svg' alt='Search'> " +
 | ||||
|         return this._goButton.Render() + | ||||
|             this._searchField.Render(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     Update() { | ||||
|         super.Update(); | ||||
|         this._searchField.Update(); | ||||
|         this._goButton.Update(); | ||||
|     } | ||||
| 
 | ||||
|     Activate() { | ||||
|         super.Activate(); | ||||
|         this._searchField.Activate(); | ||||
|         this._goButton.Activate(); | ||||
|     } | ||||
| } | ||||
|  | @ -1,4 +1,5 @@ | |||
| import {UIEventSource} from "./UIEventSource"; | ||||
| import {Playground} from "../Layers/Playground"; | ||||
| 
 | ||||
| export abstract class UIElement { | ||||
| 
 | ||||
|  | @ -18,7 +19,7 @@ export abstract class UIElement { | |||
| 
 | ||||
| 
 | ||||
|     protected ListenTo(source: UIEventSource<any>) { | ||||
|         if(source === undefined){ | ||||
|         if (source === undefined) { | ||||
|             return; | ||||
|         } | ||||
|         const self = this; | ||||
|  | @ -27,6 +28,13 @@ export abstract class UIElement { | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private _onClick: () => void; | ||||
| 
 | ||||
|     public onClick(f: (() => void)) { | ||||
|         this._onClick = f; | ||||
|         this.Update(); | ||||
|     } | ||||
| 
 | ||||
|     Update(): void { | ||||
|         let element = document.getElementById(this.id); | ||||
|         if (element === null || element === undefined) { | ||||
|  | @ -35,14 +43,24 @@ export abstract class UIElement { | |||
|         } | ||||
| 
 | ||||
|         element.innerHTML = this.InnerRender(); | ||||
|         if(this._hideIfEmpty){ | ||||
|             if(element.innerHTML === ""){ | ||||
|         if (this._hideIfEmpty) { | ||||
|             if (element.innerHTML === "") { | ||||
|                 element.parentElement.style.display = "none"; | ||||
|             }else{ | ||||
|             } else { | ||||
|                 element.parentElement.style.display = undefined; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (this._onClick !== undefined) { | ||||
|             console.log("Registering") | ||||
|             const self = this; | ||||
|             element.onclick = () => { | ||||
|                 console.log("Clicked!") | ||||
|                 self._onClick(); | ||||
|             } | ||||
|             element.style.cursor = "pointer"; | ||||
|         } | ||||
| 
 | ||||
|         this.InnerUpdate(element); | ||||
|     } | ||||
|      | ||||
|  |  | |||
							
								
								
									
										81
									
								
								assets/arrow-right-go-black.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								assets/arrow-right-go-black.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,81 @@ | |||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
| 
 | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="100" | ||||
|    height="100" | ||||
|    viewBox="0 0 26.458333 26.458334" | ||||
|    version="1.1" | ||||
|    id="svg8" | ||||
|    sodipodi:docname="arrow-right-go-black.svg" | ||||
|    inkscape:version="0.92.4 (5da689c313, 2019-01-14)"> | ||||
|   <defs | ||||
|      id="defs2" /> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="2.8284271" | ||||
|      inkscape:cx="46.174919" | ||||
|      inkscape:cy="90.659821" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="false" | ||||
|      units="px" | ||||
|      showguides="true" | ||||
|      inkscape:guide-bbox="true" | ||||
|      inkscape:window-width="1920" | ||||
|      inkscape:window-height="1001" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="1050" | ||||
|      inkscape:window-maximized="1"> | ||||
|     <sodipodi:guide | ||||
|        position="13.229167,23.859748" | ||||
|        orientation="1,0" | ||||
|        id="guide815" | ||||
|        inkscape:locked="false" /> | ||||
|     <sodipodi:guide | ||||
|        position="14.944824,13.229167" | ||||
|        orientation="0,1" | ||||
|        id="guide817" | ||||
|        inkscape:locked="false" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata5"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title /> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(0,-270.54165)"> | ||||
|     <path | ||||
|        style="fill:none;stroke:#000000;stroke-width:3.69714379;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | ||||
|        d="m 13.091004,274.74719 c 0,0 8.270349,6.58048 8.299659,9.04335 0.02932,2.46286 -8.299659,9.0653 -8.299659,9.0653" | ||||
|        id="path821" | ||||
|        inkscape:connector-curvature="0" /> | ||||
|     <path | ||||
|        style="fill:none;stroke:#000000;stroke-width:3.4395833;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" | ||||
|        d="M 21.297864,283.77082 H 5.4219634 v 0" | ||||
|        id="path815" | ||||
|        inkscape:connector-curvature="0" /> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.6 KiB | 
							
								
								
									
										6
									
								
								assets/search.svg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								assets/search.svg
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"> | ||||
| <g id="search"> | ||||
| <path id="magnifying-glass" d="M1.63 9.474L4.006 7.1l.17-.1a3.45 3.45 0 0 1-.644-2.01A3.478 3.478 0 1 1 7.01 8.47 3.43 3.43 0 0 1 5 7.822l-.098.17-2.375 2.373c-.19.188-.543.142-.79-.105s-.293-.6-.104-.79zm5.378-2.27A2.21 2.21 0 1 0 4.8 4.994 2.21 2.21 0 0 0 7.01 7.21z"/> | ||||
| </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 422 B | 
							
								
								
									
										57
									
								
								index.css
									
										
									
									
									
								
							
							
						
						
									
										57
									
								
								index.css
									
										
									
									
									
								
							|  | @ -122,12 +122,60 @@ body { | |||
|     -webkit-border-radius: 2em; | ||||
|     -moz-border-radius: 2em; | ||||
|     border-radius: 2em; | ||||
|     border-bottom-right-radius: 1.5em; | ||||
|     border-top-right-radius: 1.5em; | ||||
|     transition: all 500ms linear; | ||||
|     margin: 1em; | ||||
|     margin-left: 0; | ||||
|     margin-top: 0; | ||||
|     margin: 0; | ||||
|     margin-bottom: 0.5em; | ||||
| 
 | ||||
|     min-width: 20em; | ||||
|     pointer-events: all; | ||||
| } | ||||
| 
 | ||||
| #userbadge-and-search { | ||||
|     display: inline-block; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #searchbox { | ||||
|     display: inline-block; | ||||
|     text-align: left; | ||||
|     background-color: white; | ||||
|     transition: all 500ms linear; | ||||
|     pointer-events: all; | ||||
|     border-radius: 1.3em; | ||||
|     margin: 0; | ||||
|     margin-bottom: 1em; | ||||
|     width: 100%; | ||||
| } | ||||
| 
 | ||||
| .search { | ||||
|     position: relative; | ||||
|     float: left; | ||||
|     height: 2em; | ||||
|     margin-right: 0.5em; | ||||
| } | ||||
| 
 | ||||
| #searchbox .form-text-field { | ||||
|     position: relative; | ||||
|     float: left; | ||||
|     margin-top: 0.2em; | ||||
| } | ||||
| 
 | ||||
| #searchbox input[type="text"] { | ||||
|     background: transparent; | ||||
|     border: none; | ||||
|     font-size: large; | ||||
| } | ||||
| 
 | ||||
| .search-go { | ||||
|     position: relative; | ||||
|     height: 1.2em; | ||||
|     border: 2px solid black; | ||||
|     border-radius: 2em; | ||||
|     padding: 0.4em; | ||||
|     float: left; | ||||
|     margin-right: 0.5em; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  | @ -159,7 +207,8 @@ body { | |||
| #welcomeMessage { | ||||
|     display: inline-block; | ||||
|     max-width: 30em; | ||||
|     padding: 1em; | ||||
|     padding: 0; | ||||
|     padding-bottom: 1em; | ||||
| } | ||||
| 
 | ||||
| #messagesboxmobilewrapper { | ||||
|  |  | |||
|  | @ -21,8 +21,13 @@ | |||
| </div> | ||||
| 
 | ||||
| <div id="topleft-tools"> | ||||
|     <div id="userbadge-and-search"> | ||||
|         <div id="userbadge"> | ||||
|         Loading... If this message persists, check if javascript is enabled and if no extension (uMatrix) is blocking it. | ||||
|             Loading... If this message persists, check if javascript is enabled and if no extension (uMatrix) is | ||||
|             blocking it. | ||||
|         </div> | ||||
|         <br/> | ||||
|         <div id="searchbox"></div> | ||||
|     </div> | ||||
|     <br/> | ||||
|     <div id="messagesbox"> | ||||
|  |  | |||
							
								
								
									
										3
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								index.ts
									
										
									
									
									
								
							|  | @ -19,6 +19,7 @@ import {GeoLocationHandler} from "./Logic/GeoLocationHandler"; | |||
| import {StrayClickHandler} from "./Logic/StrayClickHandler"; | ||||
| import {SimpleAddUI} from "./UI/SimpleAddUI"; | ||||
| import {VariableUiElement} from "./UI/Base/VariableUIElement"; | ||||
| import {SearchAndGo} from "./UI/SearchAndGo"; | ||||
| 
 | ||||
| let dryRun = false; | ||||
| 
 | ||||
|  | @ -182,6 +183,8 @@ const pendingChanges = new PendingChanges( | |||
| new UserBadge(osmConnection.userDetails, pendingChanges, bm) | ||||
|     .AttachTo('userbadge'); | ||||
| 
 | ||||
| new SearchAndGo(bm).AttachTo("searchbox"); | ||||
| 
 | ||||
| var welcomeMessage = () => { | ||||
|     return new VariableUiElement( | ||||
|         osmConnection.userDetails.map((userdetails) => { | ||||
|  |  | |||
|  | @ -5,7 +5,8 @@ | |||
|     <link href="index.css" rel="stylesheet"/>    | ||||
| </head> | ||||
| <body> | ||||
| <div id="maindiv"e>Hello World</div> | ||||
| <div id="maindiv">'maindiv' not attached</div> | ||||
| <div id="extradiv">'extradiv' not attached</div> | ||||
| <script src="./test.ts"></script> | ||||
| </body> | ||||
| </html> | ||||
|  |  | |||
							
								
								
									
										17
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -0,0 +1,17 @@ | |||
| import {Geocoding} from "./Logic/Geocoding"; | ||||
| import {SearchAndGo} from "./UI/SearchAndGo"; | ||||
| import {TextField} from "./UI/Base/TextField"; | ||||
| import {VariableUiElement} from "./UI/Base/VariableUIElement"; | ||||
| 
 | ||||
| console.log("HI"); | ||||
| 
 | ||||
|  new SearchAndGo().AttachTo("maindiv"); | ||||
| /*const tf = new TextField(); | ||||
| tf.AttachTo("maindiv"); | ||||
| tf.enterPressed.addCallback(() => {alert("Searching")}); | ||||
| new VariableUiElement(tf.value).AttachTo("extradiv"); | ||||
| /*/ | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //*/
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue