Add search, a few flow updates

This commit is contained in:
Pieter Vander Vennet 2020-07-01 02:12:33 +02:00
parent 7b9ab77bda
commit c87c014045
14 changed files with 345 additions and 17 deletions

View file

@ -191,9 +191,15 @@ export class FilteredLayer {
layer.on("click", function(e) { layer.on("click", function(e) {
console.log("Selected ", feature) console.log("Selected ", feature)
self._selectedElement.setData(feature.properties); self._selectedElement.setData(feature.properties);
L.DomEvent.stop(e); // Marks the event as consumed L.DomEvent.stop(e); // Marks the event as consumed
const uiElement = self._showOnPopup.data(); 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.Update();
uiElement.Activate(); uiElement.Activate();

18
Logic/Geocoding.ts Normal file
View 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);
});
}
}

View file

@ -24,6 +24,7 @@ export class StrayClickHandler {
const self = this; const self = this;
const map = basemap.map; const map = basemap.map;
basemap.LastClickLocation.addCallback(function (lastClick) { basemap.LastClickLocation.addCallback(function (lastClick) {
selectElement.setData(undefined);
if (self._lastMarker !== undefined) { if (self._lastMarker !== undefined) {
map.removeLayer(self._lastMarker); map.removeLayer(self._lastMarker);
@ -36,8 +37,9 @@ export class StrayClickHandler {
self._lastMarker.addTo(map); self._lastMarker.addTo(map);
self._lastMarker.bindPopup(popup).openPopup(); self._lastMarker.bindPopup(popup).openPopup();
self._lastMarker.on("click", () => {
leftMessage.setData(self._uiToShow); leftMessage.setData(self._uiToShow);
});
}); });

View file

@ -75,5 +75,8 @@ Images from Wikipedia/Wikimedia
https://commons.wikimedia.org/wiki/File:Camera_font_awesome.svg https://commons.wikimedia.org/wiki/File:Camera_font_awesome.svg
Camera Icon, Dave Gandy, CC-BY-SA 3.0 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 https://commons.wikimedia.org/wiki/File:Home-icon.svg
Home icon by Timothy Miller, CC-BY-SA 3.0 Home icon by Timothy Miller, CC-BY-SA 3.0

46
UI/Base/TextField.ts Normal file
View 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
View 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();
}
}

View file

@ -1,4 +1,5 @@
import {UIEventSource} from "./UIEventSource"; import {UIEventSource} from "./UIEventSource";
import {Playground} from "../Layers/Playground";
export abstract class UIElement { export abstract class UIElement {
@ -18,7 +19,7 @@ export abstract class UIElement {
protected ListenTo(source: UIEventSource<any>) { protected ListenTo(source: UIEventSource<any>) {
if(source === undefined){ if (source === undefined) {
return; return;
} }
const self = this; const self = this;
@ -27,22 +28,39 @@ export abstract class UIElement {
}) })
} }
private _onClick: () => void;
public onClick(f: (() => void)) {
this._onClick = f;
this.Update();
}
Update(): void { Update(): void {
let element = document.getElementById(this.id); let element = document.getElementById(this.id);
if (element === null || element === undefined) { if (element === null || element === undefined) {
// The element is not painted // The element is not painted
return; return;
} }
element.innerHTML = this.InnerRender(); element.innerHTML = this.InnerRender();
if(this._hideIfEmpty){ if (this._hideIfEmpty) {
if(element.innerHTML === ""){ if (element.innerHTML === "") {
element.parentElement.style.display = "none"; element.parentElement.style.display = "none";
}else{ } else {
element.parentElement.style.display = undefined; 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); this.InnerUpdate(element);
} }

View 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
View 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

View file

@ -122,12 +122,60 @@ body {
-webkit-border-radius: 2em; -webkit-border-radius: 2em;
-moz-border-radius: 2em; -moz-border-radius: 2em;
border-radius: 2em; border-radius: 2em;
border-bottom-right-radius: 1.5em;
border-top-right-radius: 1.5em;
transition: all 500ms linear; transition: all 500ms linear;
margin: 1em; margin: 0;
margin-left: 0; margin-bottom: 0.5em;
margin-top: 0;
min-width: 20em; min-width: 20em;
pointer-events: all; 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 { #welcomeMessage {
display: inline-block; display: inline-block;
max-width: 30em; max-width: 30em;
padding: 1em; padding: 0;
padding-bottom: 1em;
} }
#messagesboxmobilewrapper { #messagesboxmobilewrapper {

View file

@ -21,8 +21,13 @@
</div> </div>
<div id="topleft-tools"> <div id="topleft-tools">
<div id="userbadge"> <div id="userbadge-and-search">
Loading... If this message persists, check if javascript is enabled and if no extension (uMatrix) is blocking it. <div id="userbadge">
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> </div>
<br/> <br/>
<div id="messagesbox"> <div id="messagesbox">

View file

@ -19,6 +19,7 @@ import {GeoLocationHandler} from "./Logic/GeoLocationHandler";
import {StrayClickHandler} from "./Logic/StrayClickHandler"; import {StrayClickHandler} from "./Logic/StrayClickHandler";
import {SimpleAddUI} from "./UI/SimpleAddUI"; import {SimpleAddUI} from "./UI/SimpleAddUI";
import {VariableUiElement} from "./UI/Base/VariableUIElement"; import {VariableUiElement} from "./UI/Base/VariableUIElement";
import {SearchAndGo} from "./UI/SearchAndGo";
let dryRun = false; let dryRun = false;
@ -182,6 +183,8 @@ const pendingChanges = new PendingChanges(
new UserBadge(osmConnection.userDetails, pendingChanges, bm) new UserBadge(osmConnection.userDetails, pendingChanges, bm)
.AttachTo('userbadge'); .AttachTo('userbadge');
new SearchAndGo(bm).AttachTo("searchbox");
var welcomeMessage = () => { var welcomeMessage = () => {
return new VariableUiElement( return new VariableUiElement(
osmConnection.userDetails.map((userdetails) => { osmConnection.userDetails.map((userdetails) => {

View file

@ -5,7 +5,8 @@
<link href="index.css" rel="stylesheet"/> <link href="index.css" rel="stylesheet"/>
</head> </head>
<body> <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> <script src="./test.ts"></script>
</body> </body>
</html> </html>

17
test.ts
View file

@ -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");
/*/
//*/