Add fancy direction picker

This commit is contained in:
Pieter Vander Vennet 2020-11-15 03:10:44 +01:00
parent 67bd817a38
commit 7ef2f429f2
11 changed files with 386 additions and 53 deletions

10
Svg.ts

File diff suppressed because one or more lines are too long

View file

@ -1,39 +0,0 @@
import {InputElement} from "./InputElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import Combine from "../Base/Combine";
import {FixedUiElement} from "../Base/FixedUiElement";
/**
* Selects a direction in degrees
*/
export default class Direction extends InputElement<number>{
private readonly value: UIEventSource<number>;
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
constructor(value?: UIEventSource<number>) {
super();
this.value = value ?? new UIEventSource<number>(undefined);
}
GetValue(): UIEventSource<number> {
return this.value;
}
InnerRender(): string {
return new Combine([
new FixedUiElement("").SetStyle(
"position: absolute;top: calc(50% - 0.5em);left: calc(50% - 0.5em);width: 1em;height: 1em;background: red;border-radius: 1em"),
])
.SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em")
.Render();
}
IsValid(t: number): boolean {
return t >= 0 && t <= 360;
}
}

View file

@ -0,0 +1,92 @@
import {InputElement} from "./InputElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import Combine from "../Base/Combine";
import Svg from "../../Svg";
/**
* Selects a direction in degrees
*/
export default class DirectionInput extends InputElement<string> {
private readonly value: UIEventSource<string>;
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
constructor(value?: UIEventSource<string>) {
super();
this.dumbMode = false;
this.value = value ?? new UIEventSource<string>(undefined);
this.value.addCallbackAndRun(rotation => {
const selfElement = document.getElementById(this.id);
if (selfElement === null) {
return;
}
const cone = selfElement.getElementsByClassName("direction-svg")[0] as HTMLElement
cone.style.rotate = rotation + "deg";
})
}
GetValue(): UIEventSource<string> {
return this.value;
}
InnerRender(): string {
console.log("Inner render direction")
return new Combine([
Svg.direction_svg().SetStyle(
`position: absolute;top: 0;left: 0;width: 100%;height: 100%;rotategs:${this.value.data}deg;`)
.SetClass("direction-svg"),
Svg.compass_svg().SetStyle(
"position: absolute;top: 0;left: 0;width: 100%;height: 100%;")
])
.SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em")
.Render();
}
protected InnerUpdate(htmlElement: HTMLElement) {
console.log("Inner update direction")
super.InnerUpdate(htmlElement);
const self = this;
function onPosChange(x: number, y: number) {
const rect = htmlElement.getBoundingClientRect();
const dx = -(rect.left + rect.right) / 2 + x;
const dy = (rect.top + rect.bottom) / 2 - y;
const angle = 180 * Math.atan2(dy, dx) / Math.PI;
const angleGeo = Math.floor((450 - angle) % 360);
self.value.setData(""+angleGeo)
}
htmlElement.ontouchmove = (ev: TouchEvent) => {
onPosChange(ev.touches[0].clientX, ev.touches[0].clientY);
}
let isDown = false;
htmlElement.onmousedown = (ev: MouseEvent) => {
isDown = true;
onPosChange(ev.x, ev.y);
ev.preventDefault();
}
htmlElement.onmouseup = (ev) => {
isDown = false; ev.preventDefault();
}
htmlElement.onmousemove = (ev: MouseEvent) => {
if (isDown) {
onPosChange(ev.x, ev.y);
} ev.preventDefault();
}
}
IsValid(str: string): boolean {
const t = Number(str);
return !isNaN(t) && t >= 0 && t <= 360;
}
}

View file

@ -9,6 +9,7 @@ import {UIEventSource} from "../../Logic/UIEventSource";
import CombinedInputElement from "./CombinedInputElement";
import SimpleDatePicker from "./SimpleDatePicker";
import OpeningHoursInput from "./OpeningHours/OpeningHoursInput";
import DirectionInput from "./DirectionInput";
interface TextFieldDef {
name: string,
@ -97,6 +98,17 @@ export default class ValidatedTextField {
str = "" + str;
return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) > 0
}),
ValidatedTextField.tp(
"direction",
"A geographical direction, in degrees. 0° is north, 90° is east",
(str) => {
str = "" + str;
return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) > 0 && Number(str) <= 360
},str => str,
(value) => {
return new DirectionInput(value);
}
),
ValidatedTextField.tp(
"float",
"A decimal",

View file

@ -32,9 +32,17 @@ export class ShareScreen extends UIElement {
const optionCheckboxes: UIElement[] = []
const optionParts: (UIEventSource<string>)[] = [];
function check() {
return Svg.checkmark_svg().SetStyle("width: 1.5em; display:inline-block;");
}
function nocheck() {
return Svg.no_checkmark_svg().SetStyle("width: 1.5em; display: inline-block;");
}
const includeLocation = new CheckBox(
new Combine([Svg.checkmark, tr.fsIncludeCurrentLocation]),
new Combine([Svg.no_checkmark, tr.fsIncludeCurrentLocation]),
new Combine([check(), tr.fsIncludeCurrentLocation]),
new Combine([nocheck(), tr.fsIncludeCurrentLocation]),
true
)
optionCheckboxes.push(includeLocation);
@ -68,8 +76,8 @@ export class ShareScreen extends UIElement {
return tr.fsIncludeCurrentBackgroundMap.Subs({name: layer?.name ?? ""}).Render();
}));
const includeCurrentBackground = new CheckBox(
new Combine([Svg.checkmark, currentBackground]),
new Combine([Svg.no_checkmark, currentBackground]),
new Combine([check(), currentBackground]),
new Combine([nocheck(), currentBackground]),
true
)
optionCheckboxes.push(includeCurrentBackground);
@ -83,8 +91,8 @@ export class ShareScreen extends UIElement {
const includeLayerChoices = new CheckBox(
new Combine([Svg.checkmark, tr.fsIncludeCurrentLayers]),
new Combine([Svg.no_checkmark, tr.fsIncludeCurrentLayers]),
new Combine([check(), tr.fsIncludeCurrentLayers]),
new Combine([nocheck(), tr.fsIncludeCurrentLayers]),
true
)
optionCheckboxes.push(includeLayerChoices);
@ -113,8 +121,8 @@ export class ShareScreen extends UIElement {
for (const swtch of switches) {
const checkbox = new CheckBox(
new Combine([Svg.checkmark, Translations.W(swtch.human)]),
new Combine([Svg.no_checkmark, Translations.W(swtch.human)]), !swtch.reverse
new Combine([check(), Translations.W(swtch.human)]),
new Combine([nocheck(), Translations.W(swtch.human)]), !swtch.reverse
);
optionCheckboxes.push(checkbox);
optionParts.push(checkbox.isEnabled.map((isEn) => {

199
assets/svg/compass.svg Normal file
View file

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
id="svg8"
version="1.1"
viewBox="0 0 100 100"
height="100"
width="100"
sodipodi:docname="compass.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1001"
id="namedview6"
showgrid="false"
inkscape:zoom="4.72"
inkscape:cx="59.602211"
inkscape:cy="33.556025"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg8" />
<metadata
id="metadata8">
<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>
<defs
id="defs6" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:27.00369644px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#f00000;fill-opacity:1;stroke:none;stroke-width:1;"
x="39.290302"
y="25.678265"
id="text818"><tspan
sodipodi:role="line"
id="tspan816"
x="39.290302"
y="25.678265"
style="stroke-width:1;fill:#f00000;fill-opacity:1;">N</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:26.37678909px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;"
x="41.584614"
y="90.136795"
id="text822"><tspan
sodipodi:role="line"
id="tspan820"
x="41.584614"
y="90.136795"
style="stroke-width:1;fill:#ffffff;fill-opacity:1;">S</tspan><tspan
sodipodi:role="line"
x="41.584614"
y="123.10778"
id="tspan824"
style="stroke-width:1;fill:#ffffff;fill-opacity:1;" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:26.37678909px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;"
x="75.969528"
y="59.416515"
id="text822-3"><tspan
sodipodi:role="line"
id="tspan820-6"
x="75.969528"
y="59.416515"
style="stroke-width:1;fill:#ffffff;fill-opacity:1;">E</tspan><tspan
sodipodi:role="line"
x="75.969528"
y="92.387505"
id="tspan824-7"
style="stroke-width:1;fill:#ffffff;fill-opacity:1;" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:23.66192627px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;"
x="9.1795397"
y="54.821461"
id="text822-5"
transform="scale(0.8970738,1.1147355)"><tspan
sodipodi:role="line"
id="tspan820-3"
x="9.1795397"
y="54.821461"
style="stroke-width:1;fill:#ffffff;fill-opacity:1;">W</tspan><tspan
sodipodi:role="line"
x="9.1795397"
y="84.398872"
id="tspan824-5"
style="stroke-width:1;fill:#ffffff;fill-opacity:1;" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:9.30213261px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;"
x="18.772408"
y="9.4910574"
id="text822-5-3"
transform="scale(0.89707379,1.1147355)"><tspan
sodipodi:role="line"
id="tspan820-3-6"
x="18.772408"
y="17.954838"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;"></tspan><tspan
sodipodi:role="line"
x="18.772408"
y="29.582502"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;"
id="tspan846">NW</tspan><tspan
sodipodi:role="line"
x="18.772408"
y="41.210167"
id="tspan824-5-7"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:9.30213261px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;"
x="20.798214"
y="50.509953"
id="text822-5-3-5"
transform="scale(0.89707379,1.1147355)"><tspan
sodipodi:role="line"
id="tspan820-3-6-3"
x="20.798214"
y="58.973732"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;" /><tspan
sodipodi:role="line"
x="20.798214"
y="70.601395"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;"
id="tspan846-5">SW</tspan><tspan
sodipodi:role="line"
x="20.798214"
y="82.229057"
id="tspan824-5-7-6"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:9.30213261px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;"
x="74.850243"
y="9.8078442"
id="text822-5-3-2"
transform="scale(0.89707379,1.1147355)"><tspan
sodipodi:role="line"
id="tspan820-3-6-9"
x="74.850243"
y="18.271624"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;" /><tspan
sodipodi:role="line"
x="74.850243"
y="29.899288"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;"
id="tspan846-1">NE</tspan><tspan
sodipodi:role="line"
x="74.850243"
y="41.526955"
id="tspan824-5-7-2"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;" /></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:9.30213261px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;"
x="76.876053"
y="50.826736"
id="text822-5-3-5-7"
transform="scale(0.89707379,1.1147355)"><tspan
sodipodi:role="line"
id="tspan820-3-6-3-0"
x="76.876053"
y="59.290516"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;" /><tspan
sodipodi:role="line"
x="76.876053"
y="70.918182"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;"
id="tspan846-5-9">SE</tspan><tspan
sodipodi:role="line"
x="76.876053"
y="82.545845"
id="tspan824-5-7-6-3"
style="fill:#ffffff;fill-opacity:1;stroke-width:1;" /></text>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

31
assets/svg/direction.svg Normal file
View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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"
id="svg8"
version="1.1"
viewBox="0 0 100 100"
height="100"
width="100">
<metadata
id="metadata8">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs6" />
<path
id="path821"
d="M 49.787737,49.857275 20.830626,9.2566092 C 35.979158,-2.144159 60.514289,-3.8195259 78.598237,9.0063685 Z"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 1,017 B

View file

@ -96,6 +96,17 @@
}
]
},
{
"question": {
"en": "In which geographical direction does this camera film?",
"nl": "Naar welke geografische richting filmt deze camera?"
},
"render": "Films to {camera:direction}",
"freeform": {
"key": "camera:direction",
"type": "direction"
}
},
{
"freeform": {
"key": "operator"

View file

@ -26,6 +26,8 @@ a {
svg {
fill: var(--foreground-color) !important;
stroke: var(--foreground-color) !important;
width: 100%;
height: 100%;
}
svg path {
@ -33,6 +35,10 @@ svg path {
stroke: var(--foreground-color) !important;
}
.direction-svg svg path{
fill: var(--catch-detail-color) !important;
}
#leafletDiv {
height: 100%;

6
package-lock.json generated
View file

@ -1774,9 +1774,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001066",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001066.tgz",
"integrity": "sha512-Gfj/WAastBtfxLws0RCh2sDbTK/8rJuSeZMecrSkNGYxPcv7EzblmDGfWQCFEQcSqYE2BRgQiJh8HOD07N5hIw=="
"version": "1.0.30001157",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001157.tgz",
"integrity": "sha512-gOerH9Wz2IRZ2ZPdMfBvyOi3cjaz4O4dgNwPGzx8EhqAs4+2IL/O+fJsbt+znSigujoZG8bVcIAUM/I/E5K3MA=="
},
"canvas": {
"version": "2.6.1",

View file

@ -1,10 +1,13 @@
//*
import Direction from "./UI/Input/Direction";
new Direction().AttachTo("maindiv")
import Direction from "./UI/Input/DirectionInput";
import {UIEventSource} from "./Logic/UIEventSource";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
const d = new UIEventSource(90);
new Direction(d).AttachTo("maindiv")
new VariableUiElement(d.map(d => ""+d+"°")).AttachTo("extradiv")
/*/