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) {
|
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
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 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);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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
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 {UIEventSource} from "./UIEventSource";
|
||||||
|
import {Playground} from "../Layers/Playground";
|
||||||
|
|
||||||
export abstract class UIElement {
|
export abstract class UIElement {
|
||||||
|
|
||||||
|
@ -27,6 +28,13 @@ 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) {
|
||||||
|
@ -43,6 +51,16 @@ export abstract class UIElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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;
|
-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 {
|
||||||
|
|
|
@ -21,8 +21,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="topleft-tools">
|
<div id="topleft-tools">
|
||||||
|
<div id="userbadge-and-search">
|
||||||
<div id="userbadge">
|
<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>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<div id="messagesbox">
|
<div id="messagesbox">
|
||||||
|
|
3
index.ts
3
index.ts
|
@ -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) => {
|
||||||
|
|
|
@ -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
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…
Reference in a new issue