diff --git a/assets/layers/climbing_gym/climbing_gym.json b/assets/layers/climbing_gym/climbing_gym.json index 7948a2b9e1..ae085eda41 100644 --- a/assets/layers/climbing_gym/climbing_gym.json +++ b/assets/layers/climbing_gym/climbing_gym.json @@ -135,7 +135,6 @@ "de": "Kletterschuhe können hier ausgeliehen werden" }, "addExtraTags": [ - "service:climbing_shoes:rental:fee=", "service:climbing_shoes:rental:charge=" ] }, diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index 1f56726803..3774c0a912 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -2154,20 +2154,23 @@ { "id": "survey_date", "question": { - "en": "When was this object last surveyed?" + "en": "When was this object last surveyed?", + "de": "Wann wurde dieses Objekt zuletzt geprüft?" }, "freeform": { "key": "survey:date", "type": "date" }, "render": { - "en": "This object was last surveyed on {survey:date}" + "en": "This object was last surveyed on {survey:date}", + "de": "Dieses Objekt wurde zuletzt geprüft am {survey:date}" }, "mappings": [ { "if": "survey:date:={_now:date}", "then": { - "en": "This object was last surveyed today" + "en": "This object was last surveyed today", + "de": "Dieses Objekt wurde heute zuletzt geprüft" } } ] diff --git a/assets/layers/usersettings/usersettings.json b/assets/layers/usersettings/usersettings.json index 2f4fa5671f..183ff57423 100644 --- a/assets/layers/usersettings/usersettings.json +++ b/assets/layers/usersettings/usersettings.json @@ -39,7 +39,8 @@ "if": "__url_parameter_initialized:language=yes", "icon": "./assets/layers/usersettings/translate_disabled.svg", "then": { - "en": "The language was set via an URL-parameter and cannot be set by the user.²" + "en": "The language was set via an URL-parameter and cannot be set by the user.²", + "de": "Die Sprache wurde über einen URL-Parameter gesetzt und kann nicht vom Benutzer eingestellt werden.²" } } ] diff --git a/assets/themes/campersite/campersite.json b/assets/themes/campersite/campersite.json index 64dc9a0206..40eecd5715 100644 --- a/assets/themes/campersite/campersite.json +++ b/assets/themes/campersite/campersite.json @@ -231,12 +231,7 @@ } }, { - "if": { - "and": [ - "fee=no", - "charge=" - ] - }, + "if": "fee=no", "then": { "en": "Can be used for free", "id": "Boleh digunakan tanpa bayaran", @@ -1562,4 +1557,4 @@ ] }, "credits": "joost schouppe" -} \ No newline at end of file +} diff --git a/langs/de.json b/langs/de.json index 2995ce4dd2..ff399a2cdc 100644 --- a/langs/de.json +++ b/langs/de.json @@ -14,7 +14,7 @@ "available": "Diese Gemeinschaft spricht {native}", "intro": "Treten Sie mit anderen Menschen in Kontakt, um sie kennen zu lernen, von ihnen zu lernen, …", "notAvailable": "Diese Gemeinschaft spricht nicht {native}", - "title": "Index der Community" + "title": "Mit anderen in Kontakt treten" }, "delete": { "cancel": "Abbrechen", diff --git a/langs/layers/de.json b/langs/layers/de.json index 4d9dab1380..3f2b2918f2 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -5163,13 +5163,13 @@ "hs-club-mate": { "mappings": { "0": { - "then": "In diesem Hackerspace gibt es Club Mate" + "then": "In diesem Hackerspace gibt es Club-Mate" }, "1": { - "then": "In diesem Hackerspace gibt es kein Club Mate" + "then": "In diesem Hackerspace gibt es kein Club-Mate" } }, - "question": "Gibt es in diesem Hackerspace Club Mate?" + "question": "Gibt es in diesem Hackerspace Club-Mate?" }, "is_makerspace": { "mappings": { @@ -7318,6 +7318,15 @@ }, "question": "Ist das Rauchen in {title()} erlaubt?" }, + "survey_date": { + "mappings": { + "0": { + "then": "Dieses Objekt wurde heute zuletzt geprüft" + } + }, + "question": "Wann wurde dieses Objekt zuletzt geprüft?", + "render": "Dieses Objekt wurde zuletzt geprüft am {survey:date}" + }, "website": { "question": "Wie lautet die Webseite von {title()}?" }, @@ -9406,6 +9415,13 @@ } } }, + "language_picker": { + "mappings": { + "0": { + "then": "Die Sprache wurde über einen URL-Parameter gesetzt und kann nicht vom Benutzer eingestellt werden.²" + } + } + }, "mangrove-keys": { "render": "Laden Sie den privaten Schlüssel für Ihr Mangrove-Konto herunter

Jeder, der diese Datei besitzt, kann mit Ihrer Identität Rezensionen vornehmen

" }, diff --git a/langs/layers/en.json b/langs/layers/en.json index 4959af6b1c..bbbdb9367d 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -5163,13 +5163,13 @@ "hs-club-mate": { "mappings": { "0": { - "then": "This hackerspace serves club mate" + "then": "This hackerspace serves Club-Mate" }, "1": { - "then": "This hackerspace does not serve club mate" + "then": "This hackerspace does not serve Club-Mate" } }, - "question": "Does this hackerspace serve Club Mate?" + "question": "Does this hackerspace serve Club-Mate?" }, "is_makerspace": { "mappings": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index ff66c6600e..477ddd2c39 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -4939,13 +4939,13 @@ "hs-club-mate": { "mappings": { "0": { - "then": "Deze hackerspace biedt clube-mate aan" + "then": "Deze hackerspace biedt Club-Mate aan" }, "1": { - "then": "Deze hackerspace biedt geen club-mate aan" + "then": "Deze hackerspace biedt geen Club-Mate aan" } }, - "question": "Biedt deze hackerspace club-mate aan?" + "question": "Biedt deze hackerspace Club-Mate aan?" }, "is_makerspace": { "mappings": { diff --git a/langs/nb_NO.json b/langs/nb_NO.json index de0ede216a..7856c11fbe 100644 --- a/langs/nb_NO.json +++ b/langs/nb_NO.json @@ -1,4 +1,7 @@ { + "advanced": { + "title": "Avanserte funksjoner" + }, "centerMessage": { "loadingData": "Laster inn data …", "ready": "Ferdig", diff --git a/scripts/generateTranslations.ts b/scripts/generateTranslations.ts index 21460c6b5e..0e8a3036d0 100644 --- a/scripts/generateTranslations.ts +++ b/scripts/generateTranslations.ts @@ -557,7 +557,7 @@ function MergeTranslation(source: any, target: any, language: string, context: s if (context.endsWith(".tagRenderings")) { keyRemapping = new Map() for (const key in target) { - keyRemapping.set(target[key].id, key) + keyRemapping.set(target[key].id ?? target[key].builtin, key) } } diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index 6588e5edfd..89fe06697a 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -1,21 +1,22 @@ -import { DesugaringStep, Each, Fuse, On } from "./Conversion" -import { LayerConfigJson } from "../Json/LayerConfigJson" +import {DesugaringStep, Each, Fuse, On} from "./Conversion" +import {LayerConfigJson} from "../Json/LayerConfigJson" import LayerConfig from "../LayerConfig" -import { Utils } from "../../../Utils" +import {Utils} from "../../../Utils" import Constants from "../../Constants" -import { Translation } from "../../../UI/i18n/Translation" -import { LayoutConfigJson } from "../Json/LayoutConfigJson" +import {Translation} from "../../../UI/i18n/Translation" +import {LayoutConfigJson} from "../Json/LayoutConfigJson" import LayoutConfig from "../LayoutConfig" -import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" -import { TagUtils } from "../../../Logic/Tags/TagUtils" -import { ExtractImages } from "./FixImages" -import { And } from "../../../Logic/Tags/And" +import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson" +import {TagUtils} from "../../../Logic/Tags/TagUtils" +import {ExtractImages} from "./FixImages" +import {And} from "../../../Logic/Tags/And" import Translations from "../../../UI/i18n/Translations" import Svg from "../../../Svg" import FilterConfigJson from "../Json/FilterConfigJson" import DeleteConfig from "../DeleteConfig" -import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" +import {QuestionableTagRenderingConfigJson} from "../Json/QuestionableTagRenderingConfigJson" import Validators from "../../../UI/InputElement/Validators" +import TagRenderingConfig from "../TagRenderingConfig"; class ValidateLanguageCompleteness extends DesugaringStep { private readonly _languages: string[] @@ -46,12 +47,12 @@ class ValidateLanguageCompleteness extends DesugaringStep { .forEach((missing) => { errors.push( context + - "A theme should be translation-complete for " + - neededLanguage + - ", but it lacks a translation for " + - missing.context + - ".\n\tThe known translation is " + - missing.tr.textFor("en") + "A theme should be translation-complete for " + + neededLanguage + + ", but it lacks a translation for " + + missing.context + + ".\n\tThe known translation is " + + missing.tr.textFor("en") ) }) } @@ -85,7 +86,7 @@ export class DoesImageExist extends DesugaringStep { context: string ): { result: string; errors?: string[]; warnings?: string[]; information?: string[] } { if (this._ignore?.has(image)) { - return { result: image } + return {result: image} } const errors = [] @@ -93,22 +94,22 @@ export class DoesImageExist extends DesugaringStep { const information = [] if (image.indexOf("{") >= 0) { information.push("Ignoring image with { in the path: " + image) - return { result: image } + return {result: image} } if (image === "assets/SocialImage.png") { - return { result: image } + return {result: image} } if (image.match(/[a-z]*/)) { if (Svg.All[image + ".svg"] !== undefined) { // This is a builtin img, e.g. 'checkmark' or 'crosshair' - return { result: image } + return {result: image} } } if (image.startsWith("<") && image.endsWith(">")) { // This is probably HTML, you're on your own here - return { result: image } + return {result: image} } if (!this._knownImagePaths.has(image)) { @@ -177,15 +178,15 @@ class ValidateTheme extends DesugaringStep { if (json["units"] !== undefined) { errors.push( "The theme " + - json.id + - " has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) " + json.id + + " has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) " ) } if (json["roamingRenderings"] !== undefined) { errors.push( "Theme " + - json.id + - " contains an old 'roamingRenderings'. Use an 'overrideAll' instead" + json.id + + " contains an old 'roamingRenderings'. Use an 'overrideAll' instead" ) } } @@ -197,10 +198,10 @@ class ValidateTheme extends DesugaringStep { for (const remoteImage of remoteImages) { errors.push( "Found a remote image: " + - remoteImage + - " in theme " + - json.id + - ", please download it." + remoteImage + + " in theme " + + json.id + + ", please download it." ) } for (const image of images) { @@ -227,12 +228,12 @@ class ValidateTheme extends DesugaringStep { if (theme.id !== filename) { errors.push( "Theme ids should be the same as the name.json, but we got id: " + - theme.id + - " and filename " + - filename + - " (" + - this._path + - ")" + theme.id + + " and filename " + + filename + + " (" + + this._path + + ")" ) } this._validateImage.convertJoin( @@ -312,7 +313,7 @@ class OverrideShadowingCheck extends DesugaringStep { ): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } { const overrideAll = json.overrideAll if (overrideAll === undefined) { - return { result: json } + return {result: json} } const errors = [] @@ -339,7 +340,7 @@ class OverrideShadowingCheck extends DesugaringStep { } } - return { result: json, errors } + return {result: json, errors} } } @@ -383,6 +384,51 @@ export class PrevalidateTheme extends Fuse { } } +export class DetectConflictingAddExtraTags extends DesugaringStep { + constructor() { + super("The `if`-part in a mapping might set some keys. Those key are not allowed to be set in the `addExtraTags`, as this might result in conflicting values", [], "DetectConflictingAddExtraTags"); + } + + convert(json: TagRenderingConfigJson, context: string): { + result: TagRenderingConfigJson; + errors?: string[]; + warnings?: string[]; + information?: string[] + } { + + if (!(json.mappings?.length > 0)) { + return {result: json} + } + + const tagRendering = new TagRenderingConfig(json) + + const errors = [] + for (let i = 0; i < tagRendering.mappings.length; i++) { + const mapping = tagRendering.mappings[i]; + if (!mapping.addExtraTags) { + continue + } + const keysInMapping = new Set(mapping.if.usedKeys()) + + const keysInAddExtraTags = mapping.addExtraTags.map(t => t.key) + + const duplicateKeys = keysInAddExtraTags.filter(k => keysInMapping.has(k)) + if (duplicateKeys.length > 0) { + errors.push( + "At " + context + ".mappings[" + i + "]: AddExtraTags overrides a key that is set in the `if`-clause of this mapping. Selecting this answer might thus first set one value (needed to match as answer) and then override it with a different value, resulting in an unsaveable question. The offending `addExtraTags` is " + duplicateKeys.join(", ") + ) + } + } + + + return { + result: json, + errors + }; + } +} + + export class DetectShadowedMappings extends DesugaringStep { private readonly _calculatedTagNames: string[] @@ -449,7 +495,7 @@ export class DetectShadowedMappings extends DesugaringStep { + keyValues.forEach(({k, v}) => { properties[k] = v }) for (let j = 0; j < i; j++) { @@ -492,10 +538,10 @@ export class DetectShadowedMappings extends DesugaringStep { if (json["special"] !== undefined) { errors.push( "At " + - context + - ': detected `special` on the top level. Did you mean `{"render":{ "special": ... }}`' + context + + ': detected `special` on the top level. Did you mean `{"render":{ "special": ... }}`' ) } if (json["group"]) { errors.push( "At " + - context + - ': groups are deprecated, use `"label": ["' + - json["group"] + - '"]` instead' + context + + ': groups are deprecated, use `"label": ["' + + json["group"] + + '"]` instead' ) } const freeformType = json["freeform"]?.["type"] @@ -669,6 +715,7 @@ export class ValidateTagRenderings extends Fuse { super( "Various validation on tagRenderingConfigs", new DetectShadowedMappings(layerConfig), + new DetectConflictingAddExtraTags(), new DetectMappingsWithImages(doesImageExist), new MiscTagRenderingChecks(options) ) @@ -711,9 +758,9 @@ export class ValidateLayer extends DesugaringStep { if (!Constants.priviliged_layers.find((x) => x == json.id)) { errors.push( context + - ": layer " + - json.id + - " uses 'special' as source.osmTags. However, this layer is not a priviliged layer" + ": layer " + + json.id + + " uses 'special' as source.osmTags. However, this layer is not a priviliged layer" ) } } @@ -722,13 +769,13 @@ export class ValidateLayer extends DesugaringStep { if (json.title === undefined && json.source !== "special:library") { errors.push( context + - ": this layer does not have a title defined but it does have tagRenderings. Not having a title will disable the popups, resulting in an unclickable element. Please add a title. If not having a popup is intended and the tagrenderings need to be kept (e.g. in a library layer), set `title: null` to disable this error." + ": this layer does not have a title defined but it does have tagRenderings. Not having a title will disable the popups, resulting in an unclickable element. Please add a title. If not having a popup is intended and the tagrenderings need to be kept (e.g. in a library layer), set `title: null` to disable this error." ) } if (json.title === null) { information.push( context + - ": title is `null`. This results in an element that cannot be clicked - even though tagRenderings is set." + ": title is `null`. This results in an element that cannot be clicked - even though tagRenderings is set." ) } } @@ -755,9 +802,9 @@ export class ValidateLayer extends DesugaringStep { console.log(json.tagRenderings) errors.push( "At " + - context + - ": some tagrenderings have a duplicate id: " + - duplicates.join(", ") + context + + ": some tagrenderings have a duplicate id: " + + duplicates.join(", ") ) } } @@ -775,8 +822,8 @@ export class ValidateLayer extends DesugaringStep { if (json["overpassTags"] !== undefined) { errors.push( "Layer " + - json.id + - 'still uses the old \'overpassTags\'-format. Please use "source": {"osmTags": }\' instead of "overpassTags": (note: this isn\'t your fault, the custom theme generator still spits out the old format)' + json.id + + 'still uses the old \'overpassTags\'-format. Please use "source": {"osmTags": }\' instead of "overpassTags": (note: this isn\'t your fault, the custom theme generator still spits out the old format)' ) } const forbiddenTopLevel = [ @@ -794,18 +841,18 @@ export class ValidateLayer extends DesugaringStep { if (json[forbiddenKey] !== undefined) errors.push( context + - ": layer " + - json.id + - " still has a forbidden key " + - forbiddenKey + ": layer " + + json.id + + " still has a forbidden key " + + forbiddenKey ) } if (json["hideUnderlayingFeaturesMinPercentage"] !== undefined) { errors.push( context + - ": layer " + - json.id + - " contains an old 'hideUnderlayingFeaturesMinPercentage'" + ": layer " + + json.id + + " contains an old 'hideUnderlayingFeaturesMinPercentage'" ) } @@ -822,9 +869,9 @@ export class ValidateLayer extends DesugaringStep { if (this._path != undefined && this._path.indexOf(expected) < 0) { errors.push( "Layer is in an incorrect place. The path is " + - this._path + - ", but expected " + - expected + this._path + + ", but expected " + + expected ) } } @@ -865,6 +912,13 @@ export class ValidateLayer extends DesugaringStep { } } + if (json.filter) { + const r = new On("filter", new Each( new ValidateFilter())).convert(json, context) + warnings.push(...(r.warnings ?? [])) + errors.push(...(r.errors ?? [])) + information.push(...(r.information ?? [])) + } + if (json.tagRenderings !== undefined) { const r = new On( "tagRenderings", @@ -886,9 +940,9 @@ export class ValidateLayer extends DesugaringStep { if (hasCondition?.length > 0) { errors.push( "At " + - context + - ":\n One or more icons in the mapRenderings have a condition set. Don't do this, as this will result in an invisible but clickable element. Use extra filters in the source instead. The offending mapRenderings are:\n" + - JSON.stringify(hasCondition, null, " ") + context + + ":\n One or more icons in the mapRenderings have a condition set. Don't do this, as this will result in an invisible but clickable element. Use extra filters in the source instead. The offending mapRenderings are:\n" + + JSON.stringify(hasCondition, null, " ") ) } } @@ -903,7 +957,7 @@ export class ValidateLayer extends DesugaringStep { const preset = json.presets[i] const tags: { k: string; v: string }[] = new And( preset.tags.map((t) => TagUtils.Tag(t)) - ).asChange({ id: "node/-1" }) + ).asChange({id: "node/-1"}) const properties = {} for (const tag of tags) { properties[tag.k] = tag.v @@ -912,12 +966,12 @@ export class ValidateLayer extends DesugaringStep { if (!doMatch) { errors.push( context + - ".presets[" + - i + - "]: This preset does not match the required tags of this layer. This implies that a newly added point will not show up.\n A newly created point will have properties: " + - JSON.stringify(properties) + - "\n The required tags are: " + - baseTags.asHumanString(false, false, {}) + ".presets[" + + i + + "]: This preset does not match the required tags of this layer. This implies that a newly added point will not show up.\n A newly created point will have properties: " + + JSON.stringify(properties) + + "\n The required tags are: " + + baseTags.asHumanString(false, false, {}) ) } } @@ -949,9 +1003,14 @@ export class ValidateFilter extends DesugaringStep { warnings?: string[] information?: string[] } { + if (typeof filter === "string") { + // Calling another filter, we skip + return {result: filter} + } const errors = [] for (const option of filter.options) { - for (let i = 0; i < option.fields.length; i++) { + + for (let i = 0; i < option.fields?.length ?? 0; i++) { const field = option.fields[i] const type = field.type ?? "string" if (Validators.availableTypes.find((t) => t === type) === undefined) { @@ -962,7 +1021,7 @@ export class ValidateFilter extends DesugaringStep { } } } - return { result: filter, errors } + return {result: filter, errors} } } @@ -991,7 +1050,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{ const warnings: string[] = [] const information: string[] = [] - const { layers, themes } = json + const {layers, themes} = json const perOsmTag = new Map< string, { @@ -1027,7 +1086,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{ return } let msg = "Possible duplicate filter: " + key - for (const { filter, layer, layout } of value) { + for (const {filter, layer, layout} of value) { let id = "" if (layout !== undefined) { id = layout.id + ":" diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index dbc0ba2f3f..6cd5dc4dc8 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -58,7 +58,7 @@ export default class TagRenderingConfig { public readonly freeform?: { readonly key: string - readonly type: string + readonly type: ValidatorType readonly placeholder: Translation readonly addExtraTags: UploadableTag[] readonly inline: boolean @@ -133,7 +133,17 @@ export default class TagRenderingConfig { ) { throw `Freeform.addExtraTags should be a list of strings - not a single string (at ${context})` } - const type = json.freeform.type ?? "string" + if ( + json.freeform.type && + Validators.availableTypes.indexOf(json.freeform.type) < 0 + ) { + throw `At ${context}: invalid type, perhaps you meant ${Utils.sortedByLevenshteinDistance( + json.freeform.key, + Validators.availableTypes, + (s) => s + )}` + } + const type: ValidatorType = json.freeform.type ?? "string" let placeholder: Translation = Translations.T(json.freeform.placeholder) if (placeholder === undefined) { @@ -622,7 +632,7 @@ export default class TagRenderingConfig { * * @param singleSelectedMapping (Only used if multiAnswer == false): the single mapping to apply. Use (mappings.length) for the freeform * @param multiSelectedMapping (Only used if multiAnswer == true): all the mappings that must be applied. Set multiSelectedMapping[mappings.length] to use the freeform as well - * @param currentProperties: The current properties of the object for which the question should be answered + * @param currentProperties The current properties of the object for which the question should be answered */ public constructChangeSpecification( freeformValue: string | undefined, @@ -685,38 +695,42 @@ export default class TagRenderingConfig { return undefined } return and - } else { - // Is at least one mapping shown in the answer? - const someMappingIsShown = this.mappings.some((m) => { - if (typeof m.hideInAnswer === "boolean") { - return !m.hideInAnswer - } - const isHidden = m.hideInAnswer.matchesProperties(currentProperties) - return !isHidden - }) - // If all mappings are hidden for the current tags, we can safely assume that we should use the freeform key - const useFreeform = - freeformValue !== undefined && - (singleSelectedMapping === this.mappings.length || !someMappingIsShown) - if (useFreeform) { - return new And([ - new Tag(this.freeform.key, freeformValue), - ...(this.freeform.addExtraTags ?? []), - ]) - } else if (singleSelectedMapping !== undefined) { - return new And([ - this.mappings[singleSelectedMapping].if, - ...(this.mappings[singleSelectedMapping].addExtraTags ?? []), - ]) - } else { - console.warn("TagRenderingConfig.ConstructSpecification has a weird fallback for", { - freeformValue, - singleSelectedMapping, - multiSelectedMapping, - currentProperties, - }) - return undefined + } + + // Is at least one mapping shown in the answer? + const someMappingIsShown = this.mappings.some((m) => { + if (typeof m.hideInAnswer === "boolean") { + return !m.hideInAnswer } + const isHidden = m.hideInAnswer.matchesProperties(currentProperties) + return !isHidden + }) + // If all mappings are hidden for the current tags, we can safely assume that we should use the freeform key + const useFreeform = + freeformValue !== undefined && + (singleSelectedMapping === this.mappings.length || + !someMappingIsShown || + singleSelectedMapping === undefined) + if (useFreeform) { + return new And([ + new Tag(this.freeform.key, freeformValue), + ...(this.freeform.addExtraTags ?? []), + ]) + } else if (singleSelectedMapping !== undefined) { + return new And([ + this.mappings[singleSelectedMapping].if, + ...(this.mappings[singleSelectedMapping].addExtraTags ?? []), + ]) + } else { + console.error("TagRenderingConfig.ConstructSpecification has a weird fallback for", { + freeformValue, + singleSelectedMapping, + multiSelectedMapping, + currentProperties, + useFreeform, + }) + + return undefined } } diff --git a/src/UI/InputElement/ValidatedInput.svelte b/src/UI/InputElement/ValidatedInput.svelte index 3656e819f1..57b702e1d5 100644 --- a/src/UI/InputElement/ValidatedInput.svelte +++ b/src/UI/InputElement/ValidatedInput.svelte @@ -9,7 +9,7 @@ import { Unit } from "../../Models/Unit" import UnitInput from "../Popup/UnitInput.svelte" - export let type: ValidatorType + export let type: ValidatorType export let feedback: UIEventSource | undefined = undefined export let getCountry: () => string | undefined export let placeholder: string | Translation | undefined @@ -63,6 +63,7 @@ } if (unit && isNaN(Number(v))) { + console.debug("Not a number, but a unit is required") value.setData(undefined) return } diff --git a/src/UI/InputElement/Validator.ts b/src/UI/InputElement/Validator.ts index 5172b2433d..e63a98f94a 100644 --- a/src/UI/InputElement/Validator.ts +++ b/src/UI/InputElement/Validator.ts @@ -1,6 +1,6 @@ -import BaseUIElement from "../BaseUIElement" -import { Translation } from "../i18n/Translation" -import Translations from "../i18n/Translations" +import BaseUIElement from "../BaseUIElement"; +import { Translation } from "../i18n/Translation"; +import Translations from "../i18n/Translations"; /** * A 'TextFieldValidator' contains various methods to check and cleanup an entered value or to give feedback. @@ -16,13 +16,13 @@ export abstract class Validator { /** * What HTML-inputmode to use */ - public readonly inputmode?: string + public readonly inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search' public readonly textArea: boolean constructor( name: string, explanation: string | BaseUIElement, - inputmode?: string, + inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search', textArea?: false | boolean ) { this.name = name diff --git a/src/UI/InputElement/Validators/FloatValidator.ts b/src/UI/InputElement/Validators/FloatValidator.ts index f984a9fc07..2c3f6adbbc 100644 --- a/src/UI/InputElement/Validators/FloatValidator.ts +++ b/src/UI/InputElement/Validators/FloatValidator.ts @@ -1,11 +1,12 @@ import { Translation } from "../../i18n/Translation" import Translations from "../../i18n/Translations" import { Validator } from "../Validator" +import { ValidatorType } from "../Validators"; export default class FloatValidator extends Validator { - inputmode = "decimal" + inputmode: "decimal" = "decimal" - constructor(name?: string, explanation?: string) { + constructor(name?: ValidatorType, explanation?: string) { super(name ?? "float", explanation ?? "A decimal number", "decimal") } diff --git a/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte b/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte index b8bd989e0b..8f0979dad1 100644 --- a/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte +++ b/src/UI/Popup/TagRendering/TagRenderingQuestion.svelte @@ -70,7 +70,7 @@ export let selectedTags: TagsFilter = undefined let mappings: Mapping[] = config?.mappings - let searchTerm: Store = new UIEventSource("") + let searchTerm: UIEventSource = new UIEventSource("") $: { try {