forked from MapComplete/MapComplete
Refactoring: dismantle 'inputHelpers'
This commit is contained in:
parent
29dc7d1e03
commit
7c42758b42
26 changed files with 485 additions and 439 deletions
2
android
2
android
|
@ -1 +1 @@
|
|||
Subproject commit b7b29d20e40bde9144c719a2b59484c04cc79b9f
|
||||
Subproject commit a48aaffec4ca59a2129834207e72ee3df85d2cd6
|
|
@ -1,9 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="375px" height="375px" viewBox="0 0 375 375" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style="fill:none;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(97.254902%,100%,96.078432%);stroke-opacity:1;stroke-miterlimit:4;" d="M -177.48351 -16.993714 C -177.484166 101.48875 -226.288922 214.739751 -312.411923 296.10684 C -398.528411 377.467771 -514.363074 419.770216 -632.651731 413.060164 " transform="matrix(-0.316636,-0.299423,0.299423,-0.316636,0,0)"/>
|
||||
<path style="fill:none;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,100%,100%);stroke-opacity:1;stroke-miterlimit:4;" d="M 743.478328 134.561833 L 430.253662 430.253662 L 117.002105 134.508051 " transform="matrix(0.435789,0,0,0.435789,0,0)"/>
|
||||
<path style="fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M -177.48351 -16.993714 C -177.484166 101.48875 -226.288922 214.739751 -312.411923 296.10684 C -398.528411 377.467771 -514.363074 419.770216 -632.651731 413.060164 " transform="matrix(-0.316636,-0.299423,0.299423,-0.316636,0,0)"/>
|
||||
<path style="fill:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:4;" d="M 743.478328 134.561833 L 430.253662 430.253662 L 117.002105 134.508051 " transform="matrix(0.435789,0,0,0.435789,0,0)"/>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="375px"
|
||||
height="375px"
|
||||
viewBox="0 0 375 375"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="direction_stroke.svg"
|
||||
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="namedview4"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.87158493"
|
||||
inkscape:cx="-23.52037"
|
||||
inkscape:cy="19.504697"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1005"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<g
|
||||
id="surface1"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:1"
|
||||
transform="translate(0,4)">
|
||||
<path
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;-inkscape-stroke:none"
|
||||
d="m -177.48437,-20.435547 a 3.4420288,3.4420288 0 0 0 -3.44141,3.441406 c -6.5e-4,117.537141 -48.41377,229.881731 -133.84961,310.599611 -85.42934,80.71172 -200.33664,122.6741 -317.68164,116.01758 a 3.4420288,3.4420288 0 0 0 -3.63086,3.24218 3.4420288,3.4420288 0 0 0 3.24219,3.63086 c 119.23207,6.76357 235.9934,-35.87674 322.79687,-117.88672 86.80999,-82.01614 136.00715,-196.17596 136.00781,-315.603511 a 3.4420288,3.4420288 0 0 0 -3.44335,-3.441406 z"
|
||||
id="path1"
|
||||
transform="matrix(-0.316636,-0.299423,0.299423,-0.316636,0,0)" />
|
||||
<path
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-opacity:1;-inkscape-stroke:none"
|
||||
d="m 119.36523,132.00586 -4.72656,5.00586 315.61524,297.97461 315.58789,-297.92188 -4.72657,-5.00586 -310.86132,293.46094 z"
|
||||
id="path2"
|
||||
transform="scale(0.435789)" />
|
||||
<path
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;-inkscape-stroke:none"
|
||||
d="m -177.48437,-20.435547 a 3.4420288,3.4420288 0 0 0 -3.44141,3.441406 c -6.5e-4,117.537141 -48.41377,229.881731 -133.84961,310.599611 -85.42934,80.71172 -200.33664,122.6741 -317.68164,116.01758 a 3.4420288,3.4420288 0 0 0 -3.63086,3.24218 3.4420288,3.4420288 0 0 0 3.24219,3.63086 c 119.23207,6.76357 235.9934,-35.87674 322.79687,-117.88672 86.80999,-82.01614 136.00715,-196.17596 136.00781,-315.603511 a 3.4420288,3.4420288 0 0 0 -3.44335,-3.441406 z"
|
||||
id="path3"
|
||||
transform="matrix(-0.316636,-0.299423,0.299423,-0.316636,0,0)" />
|
||||
<path
|
||||
style="color:#000000;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-opacity:1;-inkscape-stroke:none"
|
||||
d="m 119.36523,132.00586 -4.72656,5.00586 315.61524,297.97461 315.58789,-297.92188 -4.72657,-5.00586 -310.86132,293.46094 z"
|
||||
id="path4"
|
||||
transform="scale(0.435789)" />
|
||||
</g>
|
||||
</svg>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 3.2 KiB |
|
@ -2209,6 +2209,10 @@ input[type="range"].range-lg::-moz-range-thumb {
|
|||
min-width: 8rem;
|
||||
}
|
||||
|
||||
.min-w-48 {
|
||||
min-width: 12rem;
|
||||
}
|
||||
|
||||
.min-w-6 {
|
||||
min-width: 1.5rem;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import { QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRender
|
|||
import MarkdownUtils from "../../Utils/MarkdownUtils"
|
||||
import { And } from "../../Logic/Tags/And"
|
||||
import OsmWiki from "../../Logic/Osm/OsmWiki"
|
||||
import { UnitUtils } from "../UnitUtils"
|
||||
|
||||
export default class LayerConfig extends WithContextLoader {
|
||||
public static readonly syncSelectionAllowed = ["no", "local", "theme-only", "global"] as const
|
||||
|
@ -312,7 +313,7 @@ export default class LayerConfig extends WithContextLoader {
|
|||
)
|
||||
}
|
||||
this.units = (json.units ?? []).flatMap((unitJson, i) =>
|
||||
Unit.fromJson(unitJson, this.tagRenderings, `${context}.unit[${i}]`)
|
||||
UnitUtils.fromJson()(unitJson, this.tagRenderings, `${context}.unit[${i}]`)
|
||||
)
|
||||
{
|
||||
let filter = json.filter
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import BaseUIElement from "../UI/BaseUIElement"
|
||||
import { Denomination } from "./Denomination"
|
||||
import UnitConfigJson from "./ThemeConfig/Json/UnitConfigJson"
|
||||
import unit from "../../assets/layers/unit/unit.json"
|
||||
import TagRenderingConfig from "./ThemeConfig/TagRenderingConfig"
|
||||
import Validators, { ValidatorType } from "../UI/InputElement/Validators"
|
||||
import { Validator } from "../UI/InputElement/Validator"
|
||||
import FloatValidator from "../UI/InputElement/Validators/FloatValidator"
|
||||
|
||||
export class Unit {
|
||||
private static allUnits = this.initUnits()
|
||||
public readonly appliesToKeys: Set<string>
|
||||
public readonly denominations: Denomination[]
|
||||
public readonly denominationsSorted: Denomination[]
|
||||
|
@ -85,226 +80,7 @@ export class Unit {
|
|||
}
|
||||
}
|
||||
|
||||
static fromJson(
|
||||
json:
|
||||
| UnitConfigJson
|
||||
| Record<
|
||||
string,
|
||||
string | { quantity: string; denominations: string[]; inverted?: boolean }
|
||||
>,
|
||||
tagRenderings: TagRenderingConfig[],
|
||||
ctx: string
|
||||
): Unit[] {
|
||||
const types: Record<string, ValidatorType> = {}
|
||||
for (const tagRendering of tagRenderings) {
|
||||
if (tagRendering.freeform?.type) {
|
||||
types[tagRendering.freeform.key] = tagRendering.freeform.type
|
||||
}
|
||||
}
|
||||
|
||||
if (!json.appliesToKey && !json.quantity) {
|
||||
return this.loadFromLibrary(<any>json, types, ctx)
|
||||
}
|
||||
return this.parse(<UnitConfigJson>json, types, ctx)
|
||||
}
|
||||
|
||||
private static parseDenomination(
|
||||
json: UnitConfigJson,
|
||||
validator: Validator,
|
||||
appliesToKey: string,
|
||||
ctx: string
|
||||
): Unit {
|
||||
const applicable = json.applicableUnits.map((u, i) =>
|
||||
Denomination.fromJson(u, validator, `${ctx}.units[${i}]`)
|
||||
)
|
||||
|
||||
if (
|
||||
json.defaultInput &&
|
||||
!applicable.some((denom) => denom.canonical.trim() === json.defaultInput)
|
||||
) {
|
||||
throw `${ctx}: no denomination has the specified default denomination. The default denomination is '${
|
||||
json.defaultInput
|
||||
}', but the available denominations are ${applicable
|
||||
.map((denom) => denom.canonical)
|
||||
.join(", ")}`
|
||||
}
|
||||
|
||||
return new Unit(
|
||||
json.quantity ?? "",
|
||||
appliesToKey === undefined ? undefined : [appliesToKey],
|
||||
applicable,
|
||||
json.eraseInvalidValues ?? false,
|
||||
validator
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* // Should detect invalid defaultInput
|
||||
* let threwError = false
|
||||
* try{
|
||||
* Unit.parse({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "m",
|
||||
* useIfNoUnitGiven: true,
|
||||
* human: "meter"
|
||||
* }
|
||||
* ]
|
||||
* },"test")
|
||||
* }catch(e){
|
||||
* threwError = true
|
||||
* }
|
||||
* threwError // => true
|
||||
*
|
||||
* // Should work
|
||||
* Unit.parse({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "m",
|
||||
* useIfNoUnitGiven: true,
|
||||
* humen: "meter"
|
||||
* },
|
||||
* {
|
||||
* canonicalDenomination: "cm",
|
||||
* human: "centimeter"
|
||||
* }
|
||||
* ]
|
||||
* }, "test")
|
||||
*/
|
||||
private static parse(
|
||||
json: UnitConfigJson,
|
||||
types: Record<string, ValidatorType>,
|
||||
ctx: string
|
||||
): Unit[] {
|
||||
const appliesTo = json.appliesToKey
|
||||
for (let i = 0; i < (appliesTo ?? []).length; i++) {
|
||||
const key = appliesTo[i]
|
||||
if (key.trim() !== key) {
|
||||
throw `${ctx}.appliesToKey[${i}] is invalid: it starts or ends with whitespace`
|
||||
}
|
||||
}
|
||||
|
||||
if ((json.applicableUnits ?? []).length === 0) {
|
||||
throw `${ctx}: define at least one applicable unit`
|
||||
}
|
||||
// Some keys do have unit handling
|
||||
|
||||
const units: Unit[] = []
|
||||
if (appliesTo === undefined) {
|
||||
units.push(this.parseDenomination(json, Validators.get("float"), undefined, ctx))
|
||||
}
|
||||
for (const key of appliesTo ?? []) {
|
||||
const validator = Validators.get(types[key] ?? "float")
|
||||
units.push(this.parseDenomination(json, validator, undefined, ctx))
|
||||
}
|
||||
return units
|
||||
}
|
||||
|
||||
private static initUnits(): Map<string, Unit> {
|
||||
const m = new Map<string, Unit>()
|
||||
const units = (<UnitConfigJson[]>unit.units).flatMap((json, i) =>
|
||||
this.parse(json, {}, "unit.json.units." + i)
|
||||
)
|
||||
|
||||
for (const unit of units) {
|
||||
m.set(unit.quantity, unit)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
private static getFromLibrary(name: string, ctx: string): Unit {
|
||||
const loaded = this.allUnits.get(name)
|
||||
if (loaded === undefined) {
|
||||
throw (
|
||||
"No unit with quantity name " +
|
||||
name +
|
||||
" found (at " +
|
||||
ctx +
|
||||
"). Try one of: " +
|
||||
Array.from(this.allUnits.keys()).join(", ")
|
||||
)
|
||||
}
|
||||
return loaded
|
||||
}
|
||||
|
||||
private static loadFromLibrary(
|
||||
spec: Record<
|
||||
string,
|
||||
| string
|
||||
| { quantity: string; denominations: string[]; canonical?: string; inverted?: boolean }
|
||||
>,
|
||||
types: Record<string, ValidatorType>,
|
||||
ctx: string
|
||||
): Unit[] {
|
||||
const units: Unit[] = []
|
||||
for (const key in spec) {
|
||||
const toLoad = spec[key]
|
||||
const validator = Validators.get(types[key] ?? "float")
|
||||
if (typeof toLoad === "string") {
|
||||
const loaded = this.getFromLibrary(toLoad, ctx)
|
||||
units.push(
|
||||
new Unit(
|
||||
loaded.quantity,
|
||||
[key],
|
||||
loaded.denominations,
|
||||
loaded.eraseInvalid,
|
||||
validator,
|
||||
toLoad["inverted"]
|
||||
)
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
const loaded = this.getFromLibrary(toLoad.quantity, ctx)
|
||||
const quantity = toLoad.quantity
|
||||
|
||||
const fetchDenom = (d: string): Denomination => {
|
||||
const found = loaded.denominations.find(
|
||||
(denom) => denom.canonical.toLowerCase() === d
|
||||
)
|
||||
if (!found) {
|
||||
throw (
|
||||
`Could not find a denomination \`${d}\`for quantity ${quantity} at ${ctx}. Perhaps you meant to use on of ` +
|
||||
loaded.denominations.map((d) => d.canonical).join(", ")
|
||||
)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
if (!Array.isArray(toLoad.denominations)) {
|
||||
throw (
|
||||
"toLoad is not an array. Did you forget the [ and ] around the denominations at " +
|
||||
ctx +
|
||||
"?"
|
||||
)
|
||||
}
|
||||
const denoms = toLoad.denominations
|
||||
.map((d) => d.toLowerCase())
|
||||
.map((d) => fetchDenom(d))
|
||||
.map((d) => d.withValidator(validator))
|
||||
|
||||
if (toLoad.canonical) {
|
||||
const canonical = fetchDenom(toLoad.canonical).withValidator(validator)
|
||||
denoms.unshift(canonical.withBlankCanonical())
|
||||
}
|
||||
units.push(
|
||||
new Unit(
|
||||
loaded.quantity,
|
||||
[key],
|
||||
denoms,
|
||||
loaded.eraseInvalid,
|
||||
validator,
|
||||
toLoad["inverted"]
|
||||
)
|
||||
)
|
||||
}
|
||||
return units
|
||||
}
|
||||
|
||||
isApplicableToKey(key: string | undefined): boolean {
|
||||
if (key === undefined) {
|
||||
|
|
232
src/Models/UnitUtils.ts
Normal file
232
src/Models/UnitUtils.ts
Normal file
|
@ -0,0 +1,232 @@
|
|||
import UnitConfigJson from "./ThemeConfig/Json/UnitConfigJson"
|
||||
import TagRenderingConfig from "./ThemeConfig/TagRenderingConfig"
|
||||
import Validators, { ValidatorType } from "../UI/InputElement/Validators"
|
||||
import { Validator } from "../UI/InputElement/Validator"
|
||||
import { Denomination } from "./Denomination"
|
||||
import unit from "../../assets/layers/unit/unit.json"
|
||||
import { Unit } from "./Unit"
|
||||
|
||||
export class UnitUtils {
|
||||
private static allUnits = this.initUnits()
|
||||
|
||||
static fromJson(
|
||||
json:
|
||||
| UnitConfigJson
|
||||
| Record<
|
||||
string,
|
||||
string | { quantity: string; denominations: string[]; inverted?: boolean }
|
||||
>,
|
||||
tagRenderings: TagRenderingConfig[],
|
||||
ctx: string,
|
||||
): Unit[] {
|
||||
const types: Record<string, ValidatorType> = {}
|
||||
for (const tagRendering of tagRenderings) {
|
||||
if (tagRendering.freeform?.type) {
|
||||
types[tagRendering.freeform.key] = tagRendering.freeform.type
|
||||
}
|
||||
}
|
||||
|
||||
if (!json.appliesToKey && !json.quantity) {
|
||||
return this.loadFromLibrary(<any>json, types, ctx)
|
||||
}
|
||||
return this.parse(<UnitConfigJson>json, types, ctx)
|
||||
}
|
||||
|
||||
private static parseDenomination(
|
||||
json: UnitConfigJson,
|
||||
validator: Validator,
|
||||
appliesToKey: string,
|
||||
ctx: string,
|
||||
): Unit {
|
||||
const applicable = json.applicableUnits.map((u, i) =>
|
||||
Denomination.fromJson(u, validator, `${ctx}.units[${i}]`),
|
||||
)
|
||||
|
||||
if (
|
||||
json.defaultInput &&
|
||||
!applicable.some((denom) => denom.canonical.trim() === json.defaultInput)
|
||||
) {
|
||||
throw `${ctx}: no denomination has the specified default denomination. The default denomination is '${
|
||||
json.defaultInput
|
||||
}', but the available denominations are ${applicable
|
||||
.map((denom) => denom.canonical)
|
||||
.join(", ")}`
|
||||
}
|
||||
|
||||
return new Unit(
|
||||
json.quantity ?? "",
|
||||
appliesToKey === undefined ? undefined : [appliesToKey],
|
||||
applicable,
|
||||
json.eraseInvalidValues ?? false,
|
||||
validator,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* // Should detect invalid defaultInput
|
||||
* let threwError = false
|
||||
* try{
|
||||
* Unit.parse({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "m",
|
||||
* useIfNoUnitGiven: true,
|
||||
* human: "meter"
|
||||
* }
|
||||
* ]
|
||||
* },"test")
|
||||
* }catch(e){
|
||||
* threwError = true
|
||||
* }
|
||||
* threwError // => true
|
||||
*
|
||||
* // Should work
|
||||
* Unit.parse({
|
||||
* appliesToKey: ["length"],
|
||||
* defaultInput: "xcm",
|
||||
* applicableUnits: [
|
||||
* {
|
||||
* canonicalDenomination: "m",
|
||||
* useIfNoUnitGiven: true,
|
||||
* humen: "meter"
|
||||
* },
|
||||
* {
|
||||
* canonicalDenomination: "cm",
|
||||
* human: "centimeter"
|
||||
* }
|
||||
* ]
|
||||
* }, "test")
|
||||
*/
|
||||
private static parse(
|
||||
json: UnitConfigJson,
|
||||
types: Record<string, ValidatorType>,
|
||||
ctx: string,
|
||||
): Unit[] {
|
||||
const appliesTo = json.appliesToKey
|
||||
for (let i = 0; i < (appliesTo ?? []).length; i++) {
|
||||
const key = appliesTo[i]
|
||||
if (key.trim() !== key) {
|
||||
throw `${ctx}.appliesToKey[${i}] is invalid: it starts or ends with whitespace`
|
||||
}
|
||||
}
|
||||
|
||||
if ((json.applicableUnits ?? []).length === 0) {
|
||||
throw `${ctx}: define at least one applicable unit`
|
||||
}
|
||||
// Some keys do have unit handling
|
||||
|
||||
const units: Unit[] = []
|
||||
if (appliesTo === undefined) {
|
||||
units.push(this.parseDenomination(json, Validators.get("float"), undefined, ctx))
|
||||
}
|
||||
for (const key of appliesTo ?? []) {
|
||||
const validator = Validators.get(types[key] ?? "float")
|
||||
units.push(this.parseDenomination(json, validator, undefined, ctx))
|
||||
}
|
||||
return units
|
||||
}
|
||||
|
||||
private static initUnits(): Map<string, Unit> {
|
||||
const m = new Map<string, Unit>()
|
||||
const units = (<UnitConfigJson[]>unit.units).flatMap((json, i) =>
|
||||
this.parse(json, {}, "unit.json.units." + i),
|
||||
)
|
||||
|
||||
for (const unit of units) {
|
||||
m.set(unit.quantity, unit)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
private static getFromLibrary(name: string, ctx: string): Unit {
|
||||
const loaded = this.allUnits.get(name)
|
||||
if (loaded === undefined) {
|
||||
throw (
|
||||
"No unit with quantity name " +
|
||||
name +
|
||||
" found (at " +
|
||||
ctx +
|
||||
"). Try one of: " +
|
||||
Array.from(this.allUnits.keys()).join(", ")
|
||||
)
|
||||
}
|
||||
return loaded
|
||||
}
|
||||
|
||||
private static loadFromLibrary(
|
||||
spec: Record<
|
||||
string,
|
||||
| string
|
||||
| { quantity: string; denominations: string[]; canonical?: string; inverted?: boolean }
|
||||
>,
|
||||
types: Record<string, ValidatorType>,
|
||||
ctx: string,
|
||||
): Unit[] {
|
||||
const units: Unit[] = []
|
||||
for (const key in spec) {
|
||||
const toLoad = spec[key]
|
||||
const validator = Validators.get(types[key] ?? "float")
|
||||
if (typeof toLoad === "string") {
|
||||
const loaded = this.getFromLibrary(toLoad, ctx)
|
||||
units.push(
|
||||
new Unit(
|
||||
loaded.quantity,
|
||||
[key],
|
||||
loaded.denominations,
|
||||
loaded.eraseInvalid,
|
||||
validator,
|
||||
toLoad["inverted"],
|
||||
),
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
const loaded = this.getFromLibrary(toLoad.quantity, ctx)
|
||||
const quantity = toLoad.quantity
|
||||
|
||||
const fetchDenom = (d: string): Denomination => {
|
||||
const found = loaded.denominations.find(
|
||||
(denom) => denom.canonical.toLowerCase() === d,
|
||||
)
|
||||
if (!found) {
|
||||
throw (
|
||||
`Could not find a denomination \`${d}\`for quantity ${quantity} at ${ctx}. Perhaps you meant to use on of ` +
|
||||
loaded.denominations.map((d) => d.canonical).join(", ")
|
||||
)
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
if (!Array.isArray(toLoad.denominations)) {
|
||||
throw (
|
||||
"toLoad is not an array. Did you forget the [ and ] around the denominations at " +
|
||||
ctx +
|
||||
"?"
|
||||
)
|
||||
}
|
||||
const denoms = toLoad.denominations
|
||||
.map((d) => d.toLowerCase())
|
||||
.map((d) => fetchDenom(d))
|
||||
.map((d) => d.withValidator(validator))
|
||||
|
||||
if (toLoad.canonical) {
|
||||
const canonical = fetchDenom(toLoad.canonical).withValidator(validator)
|
||||
denoms.unshift(canonical.withBlankCanonical())
|
||||
}
|
||||
units.push(
|
||||
new Unit(
|
||||
loaded.quantity,
|
||||
[key],
|
||||
denoms,
|
||||
loaded.eraseInvalid,
|
||||
validator,
|
||||
toLoad["inverted"],
|
||||
),
|
||||
)
|
||||
}
|
||||
return units
|
||||
}
|
||||
}
|
|
@ -6,25 +6,35 @@
|
|||
import MaplibreMap from "../../Map/MaplibreMap.svelte"
|
||||
import Direction_stroke from "../../../assets/svg/Direction_stroke.svelte"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
|
||||
import type Feature from "geojson"
|
||||
import { GeoOperations } from "../../../Logic/GeoOperations"
|
||||
/**
|
||||
* A visualisation to pick a direction on a map background.
|
||||
*/
|
||||
export let value: UIEventSource<undefined | string>
|
||||
export let state: SpecialVisualizationState = undefined
|
||||
|
||||
export let mapProperties: Partial<MapProperties> & {
|
||||
readonly location: UIEventSource<{ lon: number; lat: number }>
|
||||
export let args: any[] = []
|
||||
export let feature: Feature
|
||||
let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
||||
let mapProperties: MapProperties = {
|
||||
location: new UIEventSource({ lon, lat }),
|
||||
zoom: new UIEventSource(args[0] !== undefined ? Number(args[0]) : 17),
|
||||
rasterLayer: state.mapProperties.rasterLayer,
|
||||
rotation: state.mapProperties.rotation,
|
||||
}
|
||||
let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined)
|
||||
let mla = new MapLibreAdaptor(map, mapProperties)
|
||||
mla.allowMoving.setData(false)
|
||||
mla.allowZooming.setData(false)
|
||||
state?.mapProperties?.rasterLayer?.addCallbackAndRunD((l) => mla.rasterLayer.set(l))
|
||||
|
||||
let rotation = new UIEventSource(value.data)
|
||||
rotation.addCallbackD(rotation => {
|
||||
const r = (rotation + mapProperties.rotation.data + 360) % 360
|
||||
console.log("Setting value to", r)
|
||||
value.setData(""+Math.floor(r))
|
||||
}, [mapProperties.rotation])
|
||||
let directionElem: HTMLElement | undefined
|
||||
$: value.addCallbackAndRunD((degrees) => {
|
||||
if (directionElem === undefined) {
|
||||
$: rotation.addCallbackAndRunD((degrees) => {
|
||||
if (!directionElem?.style) {
|
||||
return
|
||||
}
|
||||
directionElem.style.rotate = degrees + "deg"
|
||||
|
@ -32,13 +42,14 @@
|
|||
|
||||
let mainElem: HTMLElement
|
||||
|
||||
|
||||
function onPosChange(x: number, y: number) {
|
||||
const rect = mainElem.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)
|
||||
value.setData("" + angleGeo)
|
||||
rotation.setData(angleGeo)
|
||||
}
|
||||
|
||||
let isDown = false
|
||||
|
@ -46,7 +57,7 @@
|
|||
|
||||
<div
|
||||
bind:this={mainElem}
|
||||
class="relative h-48 w-48 cursor-pointer overflow-hidden"
|
||||
class="relative h-48 min-w-48 w-full cursor-pointer overflow-hidden rounded-xl"
|
||||
on:click={(e) => onPosChange(e.x, e.y)}
|
||||
on:mousedown={(e) => {
|
||||
isDown = true
|
||||
|
@ -71,7 +82,7 @@
|
|||
<MaplibreMap mapProperties={mla} {map} />
|
||||
</div>
|
||||
|
||||
<div bind:this={directionElem} class="absolute left-0 top-0 h-full w-full">
|
||||
<div bind:this={directionElem} class="absolute left-0 top-0 h-full w-full p-1">
|
||||
<Direction_stroke />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,67 +1,25 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Constructs an input helper element for the given type.
|
||||
* Note that all values are stringified
|
||||
*/
|
||||
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { ValidatorType } from "./Validators"
|
||||
import InputHelpers from "./InputHelpers"
|
||||
import Validators from "./Validators"
|
||||
|
||||
import type { Feature } from "geojson"
|
||||
import ImageHelper from "./Helpers/ImageHelper.svelte"
|
||||
import TranslationInput from "./Helpers/TranslationInput.svelte"
|
||||
import TagInput from "./Helpers/TagInput.svelte"
|
||||
import SimpleTagInput from "./Helpers/SimpleTagInput.svelte"
|
||||
import DirectionInput from "./Helpers/DirectionInput.svelte"
|
||||
import DateInput from "./Helpers/DateInput.svelte"
|
||||
import ColorInput from "./Helpers/ColorInput.svelte"
|
||||
import OpeningHoursInput from "./Helpers/OpeningHoursInput.svelte"
|
||||
import SlopeInput from "./Helpers/SlopeInput.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import WikidataInputHelper from "./Helpers/WikidataInputHelper.svelte"
|
||||
import type { Validator } from "./Validator"
|
||||
import DistanceInput from "./Helpers/DistanceInput.svelte"
|
||||
import TimeInput from "./Helpers/TimeInput.svelte"
|
||||
import CollectionTimes from "./Helpers/CollectionTimes/CollectionTimes.svelte"
|
||||
|
||||
export let type: ValidatorType
|
||||
export let value: UIEventSource<string | object>
|
||||
|
||||
export let feature: Feature = undefined
|
||||
export let args: (string | number | boolean)[] | any = undefined
|
||||
export let state: SpecialVisualizationState = undefined
|
||||
let validator = Validators.get(type)
|
||||
let validatorHelper: Validator = validator.inputHelper
|
||||
</script>
|
||||
|
||||
{#if type === "translation"}
|
||||
<TranslationInput {value} on:submit {args} />
|
||||
{:else if type === "direction"}
|
||||
<DirectionInput
|
||||
{value}
|
||||
{state}
|
||||
mapProperties={InputHelpers.constructMapProperties({ feature, args: args ?? [] })}
|
||||
/>
|
||||
{:else if type === "date"}
|
||||
<DateInput {value} />
|
||||
{:else if type === "time"}
|
||||
<TimeInput {value} />
|
||||
{:else if type === "points_in_time"}
|
||||
<CollectionTimes {value} />
|
||||
{:else if type === "color"}
|
||||
<ColorInput {value} />
|
||||
{:else if type === "image"}
|
||||
<ImageHelper {value} />
|
||||
{:else if type === "tag"}
|
||||
<TagInput {value} on:submit />
|
||||
{:else if type === "simple_tag"}
|
||||
<SimpleTagInput {value} {args} on:submit />
|
||||
{:else if type === "opening_hours"}
|
||||
<OpeningHoursInput {value} {args} />
|
||||
{:else if type === "slope"}
|
||||
<SlopeInput {value} {feature} {state} />
|
||||
{:else if type === "wikidata"}
|
||||
<WikidataInputHelper {value} {feature} {state} {args} />
|
||||
{:else if type === "distance"}
|
||||
<DistanceInput {value} {state} {feature} {args} />
|
||||
|
||||
{:else}
|
||||
<slot name="fallback" />
|
||||
{#if type === "distance"}
|
||||
<DistanceInput {value} {feature} {state} {args} />
|
||||
{:else if validatorHelper !== undefined}
|
||||
<svelte:component this={validatorHelper} {value} {feature} {state} {args} on:submit />
|
||||
{/if}
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
|
||||
import { MapProperties } from "../../Models/MapProperties"
|
||||
import { Feature } from "geojson"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import { ValidatorType } from "./Validators"
|
||||
|
||||
export interface InputHelperProperties {
|
||||
/**
|
||||
* Extra arguments which might be used by the helper component
|
||||
*/
|
||||
args?: (string | number | boolean)[]
|
||||
|
||||
/**
|
||||
* Used for map-based helpers, such as 'direction'
|
||||
*/
|
||||
mapProperties?: Partial<MapProperties> & {
|
||||
readonly location: UIEventSource<{ lon: number; lat: number }>
|
||||
}
|
||||
/**
|
||||
* The feature that this question is about
|
||||
* Used by the wikidata-input to read properties, which in turn is used to read the name to pre-populate the text field.
|
||||
* Additionally, used for direction input to set the default location if no mapProperties with location are given
|
||||
*/
|
||||
feature?: Feature
|
||||
}
|
||||
|
||||
export default class InputHelpers {
|
||||
public static hideInputField: ValidatorType[] = ["translation", "simple_tag", "tag","time"]
|
||||
|
||||
/**
|
||||
* Constructs a mapProperties-object for the given properties.
|
||||
* Assumes that the first helper-args contains the desired zoom-level
|
||||
* Used for the 'direction' input helper
|
||||
* @param properties
|
||||
* @private
|
||||
*/
|
||||
public static constructMapProperties(
|
||||
properties: InputHelperProperties
|
||||
): Partial<MapProperties> {
|
||||
let location = properties?.mapProperties?.location
|
||||
if (!location) {
|
||||
const [lon, lat] = GeoOperations.centerpointCoordinates(properties.feature)
|
||||
location = new UIEventSource<{ lon: number; lat: number }>({ lon, lat })
|
||||
}
|
||||
let mapProperties: Partial<MapProperties> = properties?.mapProperties ?? { location }
|
||||
if (!mapProperties.location) {
|
||||
mapProperties = { ...mapProperties, location }
|
||||
}
|
||||
let zoom = 17
|
||||
if (properties?.args?.[0] !== undefined) {
|
||||
zoom = Number(properties.args[0])
|
||||
if (isNaN(zoom)) {
|
||||
throw "Invalid zoom level for argument at 'length'-input"
|
||||
}
|
||||
}
|
||||
if (!mapProperties.zoom) {
|
||||
mapProperties = { ...mapProperties, zoom: new UIEventSource<number>(zoom) }
|
||||
}
|
||||
if (!mapProperties.rasterLayer) {
|
||||
/* mapProperties = {
|
||||
...mapProperties, rasterLayer: properties?.mapProperties?.rasterLayer
|
||||
}*/
|
||||
}
|
||||
return mapProperties
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { Translation } from "../i18n/Translation"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { HTMLInputTypeAttribute } from "svelte/elements"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
|
||||
/**
|
||||
* A 'TextFieldValidator' contains various methods to check and cleanup an entered value or to give feedback.
|
||||
|
@ -21,6 +22,8 @@ export abstract class Validator {
|
|||
public readonly textArea: boolean
|
||||
|
||||
public readonly isMeta?: boolean
|
||||
public readonly inputHelper : ComponentType = undefined
|
||||
public readonly hideInputField: boolean = false
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
|
@ -80,4 +83,5 @@ export abstract class Validator {
|
|||
public validateArguments(args: string): undefined | string {
|
||||
return undefined
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,105 +1,114 @@
|
|||
import { Validator } from "./Validator"
|
||||
import FloatValidator from "./Validators/FloatValidator"
|
||||
import StringValidator from "./Validators/StringValidator"
|
||||
import PFloatValidator from "./Validators/PFloatValidator"
|
||||
import TextValidator from "./Validators/TextValidator"
|
||||
import DateValidator from "./Validators/DateValidator"
|
||||
import { TimeValidator } from "./Validators/TimeValidator"
|
||||
import NatValidator from "./Validators/NatValidator"
|
||||
import IntValidator from "./Validators/IntValidator"
|
||||
import DistanceValidator from "./Validators/DistanceValidator"
|
||||
import PNatValidator from "./Validators/PNatValidator"
|
||||
import DirectionValidator from "./Validators/DirectionValidator"
|
||||
import WikidataValidator from "./Validators/WikidataValidator"
|
||||
import PNatValidator from "./Validators/PNatValidator"
|
||||
import FloatValidator from "./Validators/FloatValidator"
|
||||
import PFloatValidator from "./Validators/PFloatValidator"
|
||||
import EmailValidator from "./Validators/EmailValidator"
|
||||
import UrlValidator from "./Validators/UrlValidator"
|
||||
import PhoneValidator from "./Validators/PhoneValidator"
|
||||
import OpeningHoursValidator from "./Validators/OpeningHoursValidator"
|
||||
import PhoneValidator from "./Validators/PhoneValidator"
|
||||
import ColorValidator from "./Validators/ColorValidator"
|
||||
import SimpleTagValidator from "./Validators/SimpleTagValidator"
|
||||
import ImageUrlValidator from "./Validators/ImageUrlValidator"
|
||||
import TagKeyValidator from "./Validators/TagKeyValidator"
|
||||
import TranslationValidator from "./Validators/TranslationValidator"
|
||||
import FediverseValidator from "./Validators/FediverseValidator"
|
||||
import IconValidator from "./Validators/IconValidator"
|
||||
import TagValidator from "./Validators/TagValidator"
|
||||
import IdValidator from "./Validators/IdValidator"
|
||||
import SlopeValidator from "./Validators/SlopeValidator"
|
||||
import CollectionTimesValidator from "./Validators/CollectionTimesValidator"
|
||||
import IdValidator from "./Validators/IdValidator"
|
||||
import FediverseValidator from "./Validators/FediverseValidator"
|
||||
import SimpleTagValidator from "./Validators/SimpleTagValidator"
|
||||
import VeloparkValidator from "./Validators/VeloparkValidator"
|
||||
import NameSuggestionIndexValidator from "./Validators/NameSuggestionIndexValidator"
|
||||
import CurrencyValidator from "./Validators/CurrencyValidator"
|
||||
import RegexValidator from "./Validators/RegexValidator"
|
||||
import { TimeValidator } from "./Validators/TimeValidator"
|
||||
import CollectionTimesValidator from "./Validators/CollectionTimesValidator"
|
||||
import CurrencyValidator from "./Validators/CurrencyValidator"
|
||||
import TagValidator from "./Validators/TagValidator"
|
||||
import TranslationValidator from "./Validators/TranslationValidator"
|
||||
import DistanceValidator from "./Validators/DistanceValidator"
|
||||
|
||||
export type ValidatorType = (typeof Validators.availableTypes)[number]
|
||||
export const availableValidators = [
|
||||
"color",
|
||||
"currency",
|
||||
"date",
|
||||
"time",
|
||||
"direction",
|
||||
"distance",
|
||||
"email",
|
||||
"fediverse",
|
||||
"float",
|
||||
"icon",
|
||||
"id",
|
||||
"image",
|
||||
"int",
|
||||
"key",
|
||||
"nat",
|
||||
"nsi",
|
||||
"opening_hours",
|
||||
"pfloat",
|
||||
"phone",
|
||||
"pnat",
|
||||
"points_in_time",
|
||||
"regex",
|
||||
"simple_tag",
|
||||
"slope",
|
||||
"string",
|
||||
"tag",
|
||||
"text",
|
||||
"translation",
|
||||
"url",
|
||||
"velopark",
|
||||
"wikidata",
|
||||
] as const
|
||||
export type ValidatorType = (typeof availableValidators)[number]
|
||||
|
||||
export default class Validators {
|
||||
public static readonly availableTypes = [
|
||||
"color",
|
||||
"currency",
|
||||
"date",
|
||||
"time",
|
||||
"direction",
|
||||
"distance",
|
||||
"email",
|
||||
"fediverse",
|
||||
"float",
|
||||
"icon",
|
||||
"id",
|
||||
"image",
|
||||
"int",
|
||||
"key",
|
||||
"nat",
|
||||
"nsi",
|
||||
"opening_hours",
|
||||
"pfloat",
|
||||
"phone",
|
||||
"pnat",
|
||||
"points_in_time",
|
||||
"regex",
|
||||
"simple_tag",
|
||||
"slope",
|
||||
"string",
|
||||
"tag",
|
||||
"text",
|
||||
"translation",
|
||||
"url",
|
||||
"velopark",
|
||||
"wikidata",
|
||||
] as const
|
||||
|
||||
public static readonly availableTypes = availableValidators
|
||||
public static readonly AllValidators: ReadonlyArray<Validator> = [
|
||||
new StringValidator(),
|
||||
new TextValidator(),
|
||||
new DateValidator(),
|
||||
new TimeValidator(),
|
||||
new NatValidator(),
|
||||
new IntValidator(),
|
||||
new DistanceValidator(),
|
||||
new DirectionValidator(),
|
||||
new WikidataValidator(),
|
||||
new PNatValidator(),
|
||||
new FloatValidator(),
|
||||
new PFloatValidator(),
|
||||
new EmailValidator(),
|
||||
new UrlValidator(),
|
||||
new PhoneValidator(),
|
||||
new OpeningHoursValidator(),
|
||||
new TextValidator(),
|
||||
new NatValidator(),
|
||||
new PNatValidator(),
|
||||
new IntValidator(),
|
||||
new DateValidator(),
|
||||
new TimeValidator(),
|
||||
new ColorValidator(),
|
||||
new ImageUrlValidator(),
|
||||
new SimpleTagValidator(),
|
||||
new TagValidator(),
|
||||
new TagKeyValidator(),
|
||||
new TranslationValidator(),
|
||||
new IconValidator(),
|
||||
new FediverseValidator(),
|
||||
new IdValidator(),
|
||||
|
||||
new DirectionValidator(),
|
||||
new SlopeValidator(),
|
||||
new VeloparkValidator(),
|
||||
new NameSuggestionIndexValidator(),
|
||||
|
||||
|
||||
new UrlValidator(),
|
||||
new EmailValidator(),
|
||||
new PhoneValidator(),
|
||||
new FediverseValidator(),
|
||||
new ImageUrlValidator(),
|
||||
|
||||
new OpeningHoursValidator(),
|
||||
new CollectionTimesValidator(),
|
||||
new CurrencyValidator(),
|
||||
|
||||
new WikidataValidator(),
|
||||
|
||||
new TagKeyValidator(),
|
||||
new IconValidator(),
|
||||
new VeloparkValidator(),
|
||||
|
||||
new IdValidator(),
|
||||
new RegexValidator(),
|
||||
new CollectionTimesValidator()
|
||||
new SimpleTagValidator(),
|
||||
new TranslationValidator(),
|
||||
new TagValidator(),
|
||||
|
||||
new NameSuggestionIndexValidator(),
|
||||
|
||||
new DistanceValidator(),
|
||||
]
|
||||
|
||||
private static _byType = Validators._byTypeConstructor()
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import StringValidator from "./StringValidator"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
import CollectionTimes from "../Helpers/CollectionTimes/CollectionTimes.svelte"
|
||||
|
||||
export default class CollectionTimesValidator extends StringValidator{
|
||||
public readonly inputHelper: ComponentType = CollectionTimes
|
||||
constructor() {
|
||||
super("points_in_time", "'Points in time' are points according to a fixed schedule, e.g. 'every monday at 10:00'. They are typically used for postbox collection times or times of mass at a place of worship")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { Validator } from "../Validator"
|
||||
import ColorInput from "../Helpers/ColorInput.svelte"
|
||||
|
||||
export default class ColorValidator extends Validator {
|
||||
inputHelper = ColorInput
|
||||
|
||||
constructor() {
|
||||
super("color", "Shows a color picker")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { Validator } from "../Validator"
|
||||
import DateInput from "../Helpers/DateInput.svelte"
|
||||
|
||||
export default class DateValidator extends Validator {
|
||||
public readonly inputHelper = DateInput
|
||||
public readonly hideInputField = true
|
||||
|
||||
constructor() {
|
||||
super("date", "A date with date picker")
|
||||
}
|
||||
|
@ -25,4 +29,5 @@ export default class DateValidator extends Validator {
|
|||
|
||||
return [year, month, day].join("-")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import IntValidator from "./IntValidator"
|
||||
import DirectionInput from "../Helpers/DirectionInput.svelte"
|
||||
|
||||
export default class DirectionValidator extends IntValidator {
|
||||
|
||||
public readonly inputHelper = DirectionInput
|
||||
constructor() {
|
||||
super(
|
||||
"direction",
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Utils } from "../../../Utils"
|
|||
import { eliCategory } from "../../../Models/RasterLayerProperties"
|
||||
|
||||
export default class DistanceValidator extends Validator {
|
||||
|
||||
private readonly docs: string = [
|
||||
"#### Helper-arguments",
|
||||
"Options are:",
|
||||
|
@ -58,4 +59,5 @@ export default class DistanceValidator extends Validator {
|
|||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import UrlValidator from "./UrlValidator"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import ImageHelper from "../Helpers/ImageHelper.svelte"
|
||||
|
||||
export default class ImageUrlValidator extends UrlValidator {
|
||||
private static readonly allowedExtensions = ["jpg", "jpeg", "svg", "png"]
|
||||
public readonly isMeta = true
|
||||
public readonly inputHelper = ImageHelper
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
|
@ -37,4 +39,6 @@ export default class ImageUrlValidator extends UrlValidator {
|
|||
}
|
||||
return ImageUrlValidator.hasValidExternsion(str)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { Validator } from "../Validator"
|
||||
import MarkdownUtils from "../../../Utils/MarkdownUtils"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
import OpeningHoursInput from "../Helpers/OpeningHoursInput.svelte"
|
||||
|
||||
export default class OpeningHoursValidator extends Validator {
|
||||
|
||||
public readonly inputHelper= OpeningHoursInput
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
"opening_hours",
|
||||
|
@ -39,4 +44,6 @@ export default class OpeningHoursValidator extends Validator {
|
|||
].join("\n")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -2,12 +2,15 @@ import { Validator } from "../Validator"
|
|||
import { Translation } from "../../i18n/Translation"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import TagKeyValidator from "./TagKeyValidator"
|
||||
import SimpleTagInput from "../Helpers/SimpleTagInput.svelte"
|
||||
|
||||
/**
|
||||
* Checks that the input conforms `key=value`, where `key` and `value` don't have too much weird characters
|
||||
*/
|
||||
export default class SimpleTagValidator extends Validator {
|
||||
private static readonly KeyValidator = new TagKeyValidator()
|
||||
public readonly inputHelper = SimpleTagInput
|
||||
public readonly hideInputField = true
|
||||
|
||||
public readonly isMeta = true
|
||||
constructor() {
|
||||
|
@ -50,4 +53,6 @@ export default class SimpleTagValidator extends Validator {
|
|||
isValid(tag: string, _): boolean {
|
||||
return this.getFeedback(tag, _) === undefined
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import FloatValidator from "./FloatValidator"
|
||||
import SlopeInput from "../Helpers/SlopeInput.svelte"
|
||||
|
||||
export default class SlopeValidator extends FloatValidator {
|
||||
public readonly inputHelper =SlopeInput
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
"slope",
|
||||
|
@ -40,4 +43,5 @@ export default class SlopeValidator extends FloatValidator {
|
|||
}
|
||||
return super.reformat(str) + lastChar
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { Validator } from "../Validator"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import TagInput from "../Helpers/TagInput.svelte"
|
||||
|
||||
/**
|
||||
* Checks that the input conforms a JSON-encoded tag expression or a simpleTag`key=value`,
|
||||
*/
|
||||
export default class TagValidator extends Validator {
|
||||
public readonly isMeta = true
|
||||
public readonly inputHelper = TagInput
|
||||
public readonly hideInputField = true
|
||||
|
||||
constructor() {
|
||||
super("tag", "A simple tag of the format `key=value` OR a tagExpression")
|
||||
|
@ -18,4 +21,5 @@ export default class TagValidator extends Validator {
|
|||
isValid(tag: string, _): boolean {
|
||||
return this.getFeedback(tag, _) === undefined
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import { Validator } from "../Validator"
|
||||
import TimeInput from "../Helpers/TimeInput.svelte"
|
||||
|
||||
export class TimeValidator extends Validator {
|
||||
|
||||
inputmode = "time"
|
||||
public readonly inputmode = "time"
|
||||
public readonly inputHelper = TimeInput
|
||||
public readonly hideInputField = true
|
||||
|
||||
constructor() {
|
||||
super("time", "A time picker")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { Validator } from "../Validator"
|
||||
import TranslationInput from "../Helpers/TranslationInput.svelte"
|
||||
|
||||
export default class TranslationValidator extends Validator {
|
||||
public readonly inputHelper = TranslationInput
|
||||
public readonly isMeta = true
|
||||
public readonly hideInputField = true
|
||||
constructor() {
|
||||
super("translation", "Makes sure the the string is of format `Record<string, string>` ")
|
||||
}
|
||||
|
@ -14,4 +17,5 @@ export default class TranslationValidator extends Validator {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@ import { Validator } from "../Validator"
|
|||
import { Translation } from "../../i18n/Translation"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import MarkdownUtils from "../../../Utils/MarkdownUtils"
|
||||
import WikidataInputHelper from "../Helpers/WikidataInputHelper.svelte"
|
||||
|
||||
export default class WikidataValidator extends Validator {
|
||||
public static readonly _searchCache = new Map<string, Promise<WikidataResponse[]>>()
|
||||
public readonly inputHelper = WikidataInputHelper
|
||||
|
||||
private static docs = [
|
||||
"#### Helper arguments",
|
||||
|
@ -182,4 +184,5 @@ Another example is to search for species and trees:
|
|||
}
|
||||
return clipped
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
import InputHelper from "../../InputElement/InputHelper.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import { Unit } from "../../../Models/Unit"
|
||||
import InputHelpers from "../../InputElement/InputHelpers"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import Validators from "../../InputElement/Validators"
|
||||
|
||||
export let value: UIEventSource<string>
|
||||
export let unvalidatedText: UIEventSource<string> = new UIEventSource<string>(value.data)
|
||||
|
@ -29,6 +29,7 @@
|
|||
}
|
||||
|
||||
const dispatch = createEventDispatcher<{ selected }>()
|
||||
let hideInput = Validators.get(config.freeform.type).hideInputField
|
||||
export let feedback: UIEventSource<Translation>
|
||||
onDestroy(
|
||||
value.addCallbackD(() => {
|
||||
|
@ -44,6 +45,16 @@
|
|||
<div class="inline-flex w-full flex-col">
|
||||
{#if inline}
|
||||
<Inline key={config.freeform.key} {tags} template={config.render}>
|
||||
{#if hideInput}
|
||||
<InputHelper
|
||||
args={config.freeform.args}
|
||||
{feature}
|
||||
type={config.freeform.type}
|
||||
{value}
|
||||
{state}
|
||||
on:submit
|
||||
/>
|
||||
{:else}
|
||||
<ValidatedInput
|
||||
{feedback}
|
||||
{getCountry}
|
||||
|
@ -55,8 +66,9 @@
|
|||
{value}
|
||||
range={config.freeform.range}
|
||||
/>
|
||||
{/if}
|
||||
</Inline>
|
||||
{:else if InputHelpers.hideInputField.indexOf(config.freeform.type) < 0}
|
||||
{:else if !hideInput}
|
||||
<ValidatedInput
|
||||
{feedback}
|
||||
{getCountry}
|
||||
|
@ -71,6 +83,7 @@
|
|||
/>
|
||||
{/if}
|
||||
|
||||
{#if !(inline && hideInput)}
|
||||
<InputHelper
|
||||
args={config.freeform.args}
|
||||
{feature}
|
||||
|
@ -79,4 +92,5 @@
|
|||
{state}
|
||||
on:submit
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<script>
|
||||
export let color = "#000000"
|
||||
</script>
|
||||
<svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown on:focus xmlns="http://www.w3.org/2000/svg" width="375px" height="375px" viewBox="0 0 375 375" version="1.1"> <g id="surface1"> <path style="fill: none !important;stroke-width:6;stroke-linecap:round;stroke-linejoin:round;stroke:rgb(97.254902%,100%,96.078432%);stroke-opacity:1;stroke-miterlimit:4;" d="M -177.48351 -16.993714 C -177.484166 101.48875 -226.288922 214.739751 -312.411923 296.10684 C -398.528411 377.467771 -514.363074 419.770216 -632.651731 413.060164 " transform="matrix(-0.316636,-0.299423,0.299423,-0.316636,0,0)"/> <path style="fill: none !important;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(100%,100%,100%);stroke-opacity:1;stroke-miterlimit:4;" d="M 743.478328 134.561833 L 430.253662 430.253662 L 117.002105 134.508051 " transform="matrix(0.435789,0,0,0.435789,0,0)"/> <path style="fill: none !important;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke:{color};stroke-opacity:1;stroke-miterlimit:4;" d="M -177.48351 -16.993714 C -177.484166 101.48875 -226.288922 214.739751 -312.411923 296.10684 C -398.528411 377.467771 -514.363074 419.770216 -632.651731 413.060164 " transform="matrix(-0.316636,-0.299423,0.299423,-0.316636,0,0)"/> <path style="fill: none !important;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke:{color};stroke-opacity:1;stroke-miterlimit:4;" d="M 743.478328 134.561833 L 430.253662 430.253662 L 117.002105 134.508051 " transform="matrix(0.435789,0,0,0.435789,0,0)"/> </g> </svg>
|
||||
<svg {...$$restProps} on:click on:mouseover on:mouseenter on:mouseleave on:keydown on:focus width="375px" height="375px" viewBox="0 0 375 375" version="1.1" id="svg4" sodipodi:docname="direction_stroke.svg" inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"> <defs id="defs4" /> <sodipodi:namedview id="namedview4" pagecolor="#ffffff" bordercolor="#999999" borderopacity="1" inkscape:showpageshadow="2" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:zoom="0.87158493" inkscape:cx="-23.52037" inkscape:cy="19.504697" inkscape:window-width="1920" inkscape:window-height="1005" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg4" /> <g id="surface1" style="fill:{color};fill-opacity:1;stroke:#ffffff;stroke-width:3;stroke-opacity:1" transform="translate(0,4)"> <path style="color:{color};fill:{color};fill-opacity:1;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;-inkscape-stroke:none" d="m -177.48437,-20.435547 a 3.4420288,3.4420288 0 0 0 -3.44141,3.441406 c -6.5e-4,117.537141 -48.41377,229.881731 -133.84961,310.599611 -85.42934,80.71172 -200.33664,122.6741 -317.68164,116.01758 a 3.4420288,3.4420288 0 0 0 -3.63086,3.24218 3.4420288,3.4420288 0 0 0 3.24219,3.63086 c 119.23207,6.76357 235.9934,-35.87674 322.79687,-117.88672 86.80999,-82.01614 136.00715,-196.17596 136.00781,-315.603511 a 3.4420288,3.4420288 0 0 0 -3.44335,-3.441406 z" id="path1" transform="matrix(-0.316636,-0.299423,0.299423,-0.316636,0,0)" /> <path style="color:{color};fill:{color};fill-opacity:1;stroke:#ffffff;stroke-opacity:1;-inkscape-stroke:none" d="m 119.36523,132.00586 -4.72656,5.00586 315.61524,297.97461 315.58789,-297.92188 -4.72657,-5.00586 -310.86132,293.46094 z" id="path2" transform="scale(0.435789)" /> <path style="color:{color};fill:{color};fill-opacity:1;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;-inkscape-stroke:none" d="m -177.48437,-20.435547 a 3.4420288,3.4420288 0 0 0 -3.44141,3.441406 c -6.5e-4,117.537141 -48.41377,229.881731 -133.84961,310.599611 -85.42934,80.71172 -200.33664,122.6741 -317.68164,116.01758 a 3.4420288,3.4420288 0 0 0 -3.63086,3.24218 3.4420288,3.4420288 0 0 0 3.24219,3.63086 c 119.23207,6.76357 235.9934,-35.87674 322.79687,-117.88672 86.80999,-82.01614 136.00715,-196.17596 136.00781,-315.603511 a 3.4420288,3.4420288 0 0 0 -3.44335,-3.441406 z" id="path3" transform="matrix(-0.316636,-0.299423,0.299423,-0.316636,0,0)" /> <path style="color:{color};fill:{color};fill-opacity:1;stroke:#ffffff;stroke-opacity:1;-inkscape-stroke:none" d="m 119.36523,132.00586 -4.72656,5.00586 315.61524,297.97461 315.58789,-297.92188 -4.72657,-5.00586 -310.86132,293.46094 z" id="path4" transform="scale(0.435789)" /> </g> </svg>
|
Loading…
Add table
Add a link
Reference in a new issue