forked from MapComplete/MapComplete
Merge master
This commit is contained in:
commit
aa50d33b81
53 changed files with 1094 additions and 411 deletions
|
@ -2,21 +2,30 @@ import {InputElement} from "./InputElement";
|
|||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Combine from "../Base/Combine";
|
||||
import Svg from "../../Svg";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import {Utils} from "../../Utils";
|
||||
import Loc from "../../Models/Loc";
|
||||
|
||||
|
||||
/**
|
||||
* Selects a direction in degrees
|
||||
*/
|
||||
export default class DirectionInput extends InputElement<string> {
|
||||
public static constructMinimap: ((any) => BaseUIElement);
|
||||
private readonly _location: UIEventSource<Loc>;
|
||||
|
||||
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
private readonly value: UIEventSource<string>;
|
||||
private background;
|
||||
|
||||
constructor(value?: UIEventSource<string>) {
|
||||
constructor(mapBackground: UIEventSource<any>,
|
||||
location: UIEventSource<Loc>,
|
||||
value?: UIEventSource<string>) {
|
||||
super();
|
||||
this._location = location;
|
||||
this.value = value ?? new UIEventSource<string>(undefined);
|
||||
|
||||
this.background = mapBackground;
|
||||
}
|
||||
|
||||
GetValue(): UIEventSource<string> {
|
||||
|
@ -30,16 +39,23 @@ export default class DirectionInput extends InputElement<string> {
|
|||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
|
||||
let map: BaseUIElement = new FixedUiElement("")
|
||||
if (!Utils.runningFromConsole) {
|
||||
map = DirectionInput.constructMinimap({
|
||||
background: this.background,
|
||||
allowMoving: false,
|
||||
location: this._location
|
||||
})
|
||||
}
|
||||
|
||||
const element = new Combine([
|
||||
new FixedUiElement("").SetClass("w-full h-full absolute top-0 left-O rounded-full"),
|
||||
Svg.direction_svg().SetStyle(
|
||||
Svg.direction_stroke_svg().SetStyle(
|
||||
`position: absolute;top: 0;left: 0;width: 100%;height: 100%;transform:rotate(${this.value.data ?? 0}deg);`)
|
||||
.SetClass("direction-svg"),
|
||||
Svg.compass_svg().SetStyle(
|
||||
"position: absolute;top: 0;left: 0;width: 100%;height: 100%;")
|
||||
.SetClass("direction-svg relative")
|
||||
.SetStyle("z-index: 1000"),
|
||||
map.SetClass("w-full h-full absolute top-0 left-O rounded-full overflow-hidden"),
|
||||
])
|
||||
.SetStyle("position:relative;display:block;width: min(100%, 25em); padding-top: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em")
|
||||
.SetStyle("position:relative;display:block;width: min(100%, 25em); height: min(100% , 25em); background:white; border: 1px solid black; border-radius: 999em")
|
||||
.ConstructElement()
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {DropDown} from "./DropDown";
|
||||
import * as EmailValidator from "email-validator";
|
||||
import {parsePhoneNumberFromString} from "libphonenumber-js";
|
||||
import InputElementMap from "./InputElementMap";
|
||||
import {InputElement} from "./InputElement";
|
||||
import {TextField} from "./TextField";
|
||||
import {UIElement} from "../UIElement";
|
||||
|
@ -12,6 +11,7 @@ import OpeningHoursInput from "../OpeningHours/OpeningHoursInput";
|
|||
import DirectionInput from "./DirectionInput";
|
||||
import ColorPicker from "./ColorPicker";
|
||||
import {Utils} from "../../Utils";
|
||||
import Loc from "../../Models/Loc";
|
||||
|
||||
interface TextFieldDef {
|
||||
name: string,
|
||||
|
@ -19,7 +19,8 @@ interface TextFieldDef {
|
|||
isValid: ((s: string, country?: () => string) => boolean),
|
||||
reformat?: ((s: string, country?: () => string) => string),
|
||||
inputHelper?: (value: UIEventSource<string>, options?: {
|
||||
location: [number, number]
|
||||
location: [number, number],
|
||||
mapBackgroundLayer?: UIEventSource<any>
|
||||
}) => InputElement<string>,
|
||||
|
||||
inputmode?: string
|
||||
|
@ -118,8 +119,12 @@ export default class ValidatedTextField {
|
|||
str = "" + str;
|
||||
return str !== undefined && str.indexOf(".") < 0 && !isNaN(Number(str)) && Number(str) >= 0 && Number(str) <= 360
|
||||
}, str => str,
|
||||
(value) => {
|
||||
return new DirectionInput(value);
|
||||
(value, options) => {
|
||||
return new DirectionInput(options.mapBackgroundLayer , new UIEventSource<Loc>({
|
||||
lat: options.location[0],
|
||||
lon: options.location[1],
|
||||
zoom: 19
|
||||
}),value);
|
||||
},
|
||||
"numeric"
|
||||
),
|
||||
|
@ -216,16 +221,6 @@ export default class ValidatedTextField {
|
|||
* {string (typename) --> TextFieldDef}
|
||||
*/
|
||||
public static AllTypes = ValidatedTextField.allTypesDict();
|
||||
|
||||
public static TypeDropdown(): DropDown<string> {
|
||||
const values: { value: string, shown: string }[] = [];
|
||||
const expl = ValidatedTextField.tpList;
|
||||
for (const key in expl) {
|
||||
values.push({value: expl[key].name, shown: `${expl[key].name} - ${expl[key].explanation}`})
|
||||
}
|
||||
return new DropDown<string>("", values)
|
||||
}
|
||||
|
||||
public static InputForType(type: string, options?: {
|
||||
placeholder?: string | UIElement,
|
||||
value?: UIEventSource<string>,
|
||||
|
@ -235,7 +230,8 @@ export default class ValidatedTextField {
|
|||
textAreaRows?: number,
|
||||
isValid?: ((s: string, country: () => string) => boolean),
|
||||
country?: () => string,
|
||||
location?: [number /*lat*/, number /*lon*/]
|
||||
location?: [number /*lat*/, number /*lon*/],
|
||||
mapBackgroundLayer?: UIEventSource<any>
|
||||
}): InputElement<string> {
|
||||
options = options ?? {};
|
||||
options.placeholder = options.placeholder ?? type;
|
||||
|
@ -269,90 +265,16 @@ export default class ValidatedTextField {
|
|||
|
||||
if (tp.inputHelper) {
|
||||
input = new CombinedInputElement(input, tp.inputHelper(input.GetValue(), {
|
||||
location: options.location
|
||||
location: options.location,
|
||||
mapBackgroundLayer: options.mapBackgroundLayer
|
||||
|
||||
}),
|
||||
(a, b) => a, // We can ignore b, as they are linked earlier
|
||||
(a, _) => a, // We can ignore b, as they are linked earlier
|
||||
a => [a, a]
|
||||
);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public static NumberInput(type: string = "int", extraValidation: (number: Number) => boolean = undefined): InputElement<number> {
|
||||
const isValid = ValidatedTextField.AllTypes[type].isValid;
|
||||
extraValidation = extraValidation ?? (() => true)
|
||||
|
||||
const fromString = str => {
|
||||
if (!isValid(str)) {
|
||||
return undefined;
|
||||
}
|
||||
const n = Number(str);
|
||||
if (!extraValidation(n)) {
|
||||
return undefined;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
const toString = num => {
|
||||
if (num === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return "" + num;
|
||||
};
|
||||
const textField = ValidatedTextField.InputForType(type);
|
||||
return new InputElementMap(textField, (n0, n1) => n0 === n1, fromString, toString)
|
||||
}
|
||||
|
||||
public static KeyInput(allowEmpty: boolean = false): InputElement<string> {
|
||||
|
||||
function fromString(str) {
|
||||
if (str?.match(/^[a-zA-Z][a-zA-Z0-9:_-]*$/)) {
|
||||
return str;
|
||||
}
|
||||
if (str === "" && allowEmpty) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
const toString = str => str
|
||||
|
||||
function isSame(str0, str1) {
|
||||
return str0 === str1;
|
||||
}
|
||||
|
||||
const textfield = new TextField({
|
||||
placeholder: "key",
|
||||
isValid: str => fromString(str) !== undefined,
|
||||
value: new UIEventSource<string>("")
|
||||
});
|
||||
|
||||
return new InputElementMap(textfield, isSame, fromString, toString);
|
||||
}
|
||||
|
||||
static Mapped<T>(fromString: (str) => T, toString: (T) => string, options?: {
|
||||
placeholder?: string | UIElement,
|
||||
type?: string,
|
||||
value?: UIEventSource<string>,
|
||||
startValidated?: boolean,
|
||||
textArea?: boolean,
|
||||
textAreaRows?: number,
|
||||
isValid?: ((string: string) => boolean),
|
||||
country?: () => string
|
||||
}): InputElement<T> {
|
||||
let textField: InputElement<string>;
|
||||
if (options?.type) {
|
||||
textField = ValidatedTextField.InputForType(options.type, options);
|
||||
} else {
|
||||
textField = new TextField(options);
|
||||
}
|
||||
return new InputElementMap(
|
||||
textField, (a, b) => a === b,
|
||||
fromString, toString
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public static HelpText(): string {
|
||||
const explanations = ValidatedTextField.tpList.map(type => ["## " + type.name, "", type.explanation].join("\n")).join("\n\n")
|
||||
return "# Available types for text fields\n\nThe listed types here trigger a special input element. Use them in `tagrendering.freeform.type` of your tagrendering to activate them\n\n" + explanations
|
||||
|
@ -363,7 +285,8 @@ export default class ValidatedTextField {
|
|||
isValid?: ((s: string, country?: () => string) => boolean),
|
||||
reformat?: ((s: string, country?: () => string) => string),
|
||||
inputHelper?: (value: UIEventSource<string>, options?: {
|
||||
location: [number, number]
|
||||
location: [number, number],
|
||||
mapBackgroundLayer: UIEventSource<any>
|
||||
}) => InputElement<string>,
|
||||
inputmode?: string): TextFieldDef {
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue