Merge branch 'master' into develop
This commit is contained in:
commit
e1c831884a
16 changed files with 242 additions and 150 deletions
|
@ -135,7 +135,6 @@
|
||||||
"de": "Kletterschuhe können hier ausgeliehen werden"
|
"de": "Kletterschuhe können hier ausgeliehen werden"
|
||||||
},
|
},
|
||||||
"addExtraTags": [
|
"addExtraTags": [
|
||||||
"service:climbing_shoes:rental:fee=",
|
|
||||||
"service:climbing_shoes:rental:charge="
|
"service:climbing_shoes:rental:charge="
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -2154,20 +2154,23 @@
|
||||||
{
|
{
|
||||||
"id": "survey_date",
|
"id": "survey_date",
|
||||||
"question": {
|
"question": {
|
||||||
"en": "When was this object last surveyed?"
|
"en": "When was this object last surveyed?",
|
||||||
|
"de": "Wann wurde dieses Objekt zuletzt geprüft?"
|
||||||
},
|
},
|
||||||
"freeform": {
|
"freeform": {
|
||||||
"key": "survey:date",
|
"key": "survey:date",
|
||||||
"type": "date"
|
"type": "date"
|
||||||
},
|
},
|
||||||
"render": {
|
"render": {
|
||||||
"en": "This object was last surveyed on <b>{survey:date}</b>"
|
"en": "This object was last surveyed on <b>{survey:date}</b>",
|
||||||
|
"de": "Dieses Objekt wurde zuletzt geprüft am <b>{survey:date}</b>"
|
||||||
},
|
},
|
||||||
"mappings": [
|
"mappings": [
|
||||||
{
|
{
|
||||||
"if": "survey:date:={_now:date}",
|
"if": "survey:date:={_now:date}",
|
||||||
"then": {
|
"then": {
|
||||||
"en": "This object was last surveyed today"
|
"en": "This object was last surveyed today",
|
||||||
|
"de": "Dieses Objekt wurde heute zuletzt geprüft"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -39,7 +39,8 @@
|
||||||
"if": "__url_parameter_initialized:language=yes",
|
"if": "__url_parameter_initialized:language=yes",
|
||||||
"icon": "./assets/layers/usersettings/translate_disabled.svg",
|
"icon": "./assets/layers/usersettings/translate_disabled.svg",
|
||||||
"then": {
|
"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.²"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -231,12 +231,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"if": {
|
"if": "fee=no",
|
||||||
"and": [
|
|
||||||
"fee=no",
|
|
||||||
"charge="
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"then": {
|
"then": {
|
||||||
"en": "Can be used for free",
|
"en": "Can be used for free",
|
||||||
"id": "Boleh digunakan tanpa bayaran",
|
"id": "Boleh digunakan tanpa bayaran",
|
||||||
|
@ -1562,4 +1557,4 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"credits": "joost schouppe"
|
"credits": "joost schouppe"
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"available": "Diese Gemeinschaft spricht {native}",
|
"available": "Diese Gemeinschaft spricht {native}",
|
||||||
"intro": "Treten Sie mit anderen Menschen in Kontakt, um sie kennen zu lernen, von ihnen zu lernen, …",
|
"intro": "Treten Sie mit anderen Menschen in Kontakt, um sie kennen zu lernen, von ihnen zu lernen, …",
|
||||||
"notAvailable": "Diese Gemeinschaft spricht nicht {native}",
|
"notAvailable": "Diese Gemeinschaft spricht nicht {native}",
|
||||||
"title": "Index der Community"
|
"title": "Mit anderen in Kontakt treten"
|
||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
|
|
|
@ -5163,13 +5163,13 @@
|
||||||
"hs-club-mate": {
|
"hs-club-mate": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
"then": "In diesem Hackerspace gibt es Club Mate"
|
"then": "In diesem Hackerspace gibt es Club-Mate"
|
||||||
},
|
},
|
||||||
"1": {
|
"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": {
|
"is_makerspace": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
@ -7318,6 +7318,15 @@
|
||||||
},
|
},
|
||||||
"question": "Ist das Rauchen in {title()} erlaubt?"
|
"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 <b>{survey:date}</b>"
|
||||||
|
},
|
||||||
"website": {
|
"website": {
|
||||||
"question": "Wie lautet die Webseite von {title()}?"
|
"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": {
|
"mangrove-keys": {
|
||||||
"render": "<a href='data:application/json,{mangroveidentity}' download='mangrove_private_key_{_name}'>Laden Sie den privaten Schlüssel für Ihr Mangrove-Konto herunter</a> <p>Jeder, der diese Datei besitzt, kann mit Ihrer Identität Rezensionen vornehmen</p>"
|
"render": "<a href='data:application/json,{mangroveidentity}' download='mangrove_private_key_{_name}'>Laden Sie den privaten Schlüssel für Ihr Mangrove-Konto herunter</a> <p>Jeder, der diese Datei besitzt, kann mit Ihrer Identität Rezensionen vornehmen</p>"
|
||||||
},
|
},
|
||||||
|
|
|
@ -5163,13 +5163,13 @@
|
||||||
"hs-club-mate": {
|
"hs-club-mate": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
"then": "This hackerspace serves club mate"
|
"then": "This hackerspace serves Club-Mate"
|
||||||
},
|
},
|
||||||
"1": {
|
"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": {
|
"is_makerspace": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
|
|
@ -4939,13 +4939,13 @@
|
||||||
"hs-club-mate": {
|
"hs-club-mate": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
"0": {
|
"0": {
|
||||||
"then": "Deze hackerspace biedt clube-mate aan"
|
"then": "Deze hackerspace biedt Club-Mate aan"
|
||||||
},
|
},
|
||||||
"1": {
|
"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": {
|
"is_makerspace": {
|
||||||
"mappings": {
|
"mappings": {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
{
|
{
|
||||||
|
"advanced": {
|
||||||
|
"title": "Avanserte funksjoner"
|
||||||
|
},
|
||||||
"centerMessage": {
|
"centerMessage": {
|
||||||
"loadingData": "Laster inn data …",
|
"loadingData": "Laster inn data …",
|
||||||
"ready": "Ferdig",
|
"ready": "Ferdig",
|
||||||
|
|
|
@ -557,7 +557,7 @@ function MergeTranslation(source: any, target: any, language: string, context: s
|
||||||
if (context.endsWith(".tagRenderings")) {
|
if (context.endsWith(".tagRenderings")) {
|
||||||
keyRemapping = new Map<string, string>()
|
keyRemapping = new Map<string, string>()
|
||||||
for (const key in target) {
|
for (const key in target) {
|
||||||
keyRemapping.set(target[key].id, key)
|
keyRemapping.set(target[key].id ?? target[key].builtin, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
import { DesugaringStep, Each, Fuse, On } from "./Conversion"
|
import {DesugaringStep, Each, Fuse, On} from "./Conversion"
|
||||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
import {LayerConfigJson} from "../Json/LayerConfigJson"
|
||||||
import LayerConfig from "../LayerConfig"
|
import LayerConfig from "../LayerConfig"
|
||||||
import { Utils } from "../../../Utils"
|
import {Utils} from "../../../Utils"
|
||||||
import Constants from "../../Constants"
|
import Constants from "../../Constants"
|
||||||
import { Translation } from "../../../UI/i18n/Translation"
|
import {Translation} from "../../../UI/i18n/Translation"
|
||||||
import { LayoutConfigJson } from "../Json/LayoutConfigJson"
|
import {LayoutConfigJson} from "../Json/LayoutConfigJson"
|
||||||
import LayoutConfig from "../LayoutConfig"
|
import LayoutConfig from "../LayoutConfig"
|
||||||
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
|
import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson"
|
||||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
import {TagUtils} from "../../../Logic/Tags/TagUtils"
|
||||||
import { ExtractImages } from "./FixImages"
|
import {ExtractImages} from "./FixImages"
|
||||||
import { And } from "../../../Logic/Tags/And"
|
import {And} from "../../../Logic/Tags/And"
|
||||||
import Translations from "../../../UI/i18n/Translations"
|
import Translations from "../../../UI/i18n/Translations"
|
||||||
import Svg from "../../../Svg"
|
import Svg from "../../../Svg"
|
||||||
import FilterConfigJson from "../Json/FilterConfigJson"
|
import FilterConfigJson from "../Json/FilterConfigJson"
|
||||||
import DeleteConfig from "../DeleteConfig"
|
import DeleteConfig from "../DeleteConfig"
|
||||||
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
|
import {QuestionableTagRenderingConfigJson} from "../Json/QuestionableTagRenderingConfigJson"
|
||||||
import Validators from "../../../UI/InputElement/Validators"
|
import Validators from "../../../UI/InputElement/Validators"
|
||||||
|
import TagRenderingConfig from "../TagRenderingConfig";
|
||||||
|
|
||||||
class ValidateLanguageCompleteness extends DesugaringStep<any> {
|
class ValidateLanguageCompleteness extends DesugaringStep<any> {
|
||||||
private readonly _languages: string[]
|
private readonly _languages: string[]
|
||||||
|
@ -46,12 +47,12 @@ class ValidateLanguageCompleteness extends DesugaringStep<any> {
|
||||||
.forEach((missing) => {
|
.forEach((missing) => {
|
||||||
errors.push(
|
errors.push(
|
||||||
context +
|
context +
|
||||||
"A theme should be translation-complete for " +
|
"A theme should be translation-complete for " +
|
||||||
neededLanguage +
|
neededLanguage +
|
||||||
", but it lacks a translation for " +
|
", but it lacks a translation for " +
|
||||||
missing.context +
|
missing.context +
|
||||||
".\n\tThe known translation is " +
|
".\n\tThe known translation is " +
|
||||||
missing.tr.textFor("en")
|
missing.tr.textFor("en")
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -85,7 +86,7 @@ export class DoesImageExist extends DesugaringStep<string> {
|
||||||
context: string
|
context: string
|
||||||
): { result: string; errors?: string[]; warnings?: string[]; information?: string[] } {
|
): { result: string; errors?: string[]; warnings?: string[]; information?: string[] } {
|
||||||
if (this._ignore?.has(image)) {
|
if (this._ignore?.has(image)) {
|
||||||
return { result: image }
|
return {result: image}
|
||||||
}
|
}
|
||||||
|
|
||||||
const errors = []
|
const errors = []
|
||||||
|
@ -93,22 +94,22 @@ export class DoesImageExist extends DesugaringStep<string> {
|
||||||
const information = []
|
const information = []
|
||||||
if (image.indexOf("{") >= 0) {
|
if (image.indexOf("{") >= 0) {
|
||||||
information.push("Ignoring image with { in the path: " + image)
|
information.push("Ignoring image with { in the path: " + image)
|
||||||
return { result: image }
|
return {result: image}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image === "assets/SocialImage.png") {
|
if (image === "assets/SocialImage.png") {
|
||||||
return { result: image }
|
return {result: image}
|
||||||
}
|
}
|
||||||
if (image.match(/[a-z]*/)) {
|
if (image.match(/[a-z]*/)) {
|
||||||
if (Svg.All[image + ".svg"] !== undefined) {
|
if (Svg.All[image + ".svg"] !== undefined) {
|
||||||
// This is a builtin img, e.g. 'checkmark' or 'crosshair'
|
// This is a builtin img, e.g. 'checkmark' or 'crosshair'
|
||||||
return { result: image }
|
return {result: image}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image.startsWith("<") && image.endsWith(">")) {
|
if (image.startsWith("<") && image.endsWith(">")) {
|
||||||
// This is probably HTML, you're on your own here
|
// This is probably HTML, you're on your own here
|
||||||
return { result: image }
|
return {result: image}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._knownImagePaths.has(image)) {
|
if (!this._knownImagePaths.has(image)) {
|
||||||
|
@ -177,15 +178,15 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
||||||
if (json["units"] !== undefined) {
|
if (json["units"] !== undefined) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"The theme " +
|
"The theme " +
|
||||||
json.id +
|
json.id +
|
||||||
" has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) "
|
" has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) "
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (json["roamingRenderings"] !== undefined) {
|
if (json["roamingRenderings"] !== undefined) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"Theme " +
|
"Theme " +
|
||||||
json.id +
|
json.id +
|
||||||
" contains an old 'roamingRenderings'. Use an 'overrideAll' instead"
|
" contains an old 'roamingRenderings'. Use an 'overrideAll' instead"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,10 +198,10 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
||||||
for (const remoteImage of remoteImages) {
|
for (const remoteImage of remoteImages) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"Found a remote image: " +
|
"Found a remote image: " +
|
||||||
remoteImage +
|
remoteImage +
|
||||||
" in theme " +
|
" in theme " +
|
||||||
json.id +
|
json.id +
|
||||||
", please download it."
|
", please download it."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
for (const image of images) {
|
for (const image of images) {
|
||||||
|
@ -227,12 +228,12 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
||||||
if (theme.id !== filename) {
|
if (theme.id !== filename) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"Theme ids should be the same as the name.json, but we got id: " +
|
"Theme ids should be the same as the name.json, but we got id: " +
|
||||||
theme.id +
|
theme.id +
|
||||||
" and filename " +
|
" and filename " +
|
||||||
filename +
|
filename +
|
||||||
" (" +
|
" (" +
|
||||||
this._path +
|
this._path +
|
||||||
")"
|
")"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
this._validateImage.convertJoin(
|
this._validateImage.convertJoin(
|
||||||
|
@ -312,7 +313,7 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> {
|
||||||
): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } {
|
): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } {
|
||||||
const overrideAll = json.overrideAll
|
const overrideAll = json.overrideAll
|
||||||
if (overrideAll === undefined) {
|
if (overrideAll === undefined) {
|
||||||
return { result: json }
|
return {result: json}
|
||||||
}
|
}
|
||||||
|
|
||||||
const errors = []
|
const errors = []
|
||||||
|
@ -339,7 +340,7 @@ class OverrideShadowingCheck extends DesugaringStep<LayoutConfigJson> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { result: json, errors }
|
return {result: json, errors}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,6 +384,51 @@ export class PrevalidateTheme extends Fuse<LayoutConfigJson> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class DetectConflictingAddExtraTags extends DesugaringStep<TagRenderingConfigJson> {
|
||||||
|
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<TagRenderingConfigJson> {
|
export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJson> {
|
||||||
private readonly _calculatedTagNames: string[]
|
private readonly _calculatedTagNames: string[]
|
||||||
|
|
||||||
|
@ -449,7 +495,7 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
|
||||||
const errors = []
|
const errors = []
|
||||||
const warnings = []
|
const warnings = []
|
||||||
if (json.mappings === undefined || json.mappings.length === 0) {
|
if (json.mappings === undefined || json.mappings.length === 0) {
|
||||||
return { result: json }
|
return {result: json}
|
||||||
}
|
}
|
||||||
const defaultProperties = {}
|
const defaultProperties = {}
|
||||||
for (const calculatedTagName of this._calculatedTagNames) {
|
for (const calculatedTagName of this._calculatedTagNames) {
|
||||||
|
@ -475,7 +521,7 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
|
||||||
}
|
}
|
||||||
const keyValues = parsedConditions[i].asChange(defaultProperties)
|
const keyValues = parsedConditions[i].asChange(defaultProperties)
|
||||||
const properties = {}
|
const properties = {}
|
||||||
keyValues.forEach(({ k, v }) => {
|
keyValues.forEach(({k, v}) => {
|
||||||
properties[k] = v
|
properties[k] = v
|
||||||
})
|
})
|
||||||
for (let j = 0; j < i; j++) {
|
for (let j = 0; j < i; j++) {
|
||||||
|
@ -492,10 +538,10 @@ export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJso
|
||||||
// The current mapping is shadowed!
|
// The current mapping is shadowed!
|
||||||
errors.push(`At ${context}: Mapping ${i} is shadowed by mapping ${j} and will thus never be shown:
|
errors.push(`At ${context}: Mapping ${i} is shadowed by mapping ${j} and will thus never be shown:
|
||||||
The mapping ${parsedConditions[i].asHumanString(
|
The mapping ${parsedConditions[i].asHumanString(
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
{}
|
{}
|
||||||
)} is fully matched by a previous mapping (namely ${j}), which matches:
|
)} is fully matched by a previous mapping (namely ${j}), which matches:
|
||||||
${parsedConditions[j].asHumanString(false, false, {})}.
|
${parsedConditions[j].asHumanString(false, false, {})}.
|
||||||
|
|
||||||
To fix this problem, you can try to:
|
To fix this problem, you can try to:
|
||||||
|
@ -564,7 +610,7 @@ export class DetectMappingsWithImages extends DesugaringStep<TagRenderingConfigJ
|
||||||
const warnings: string[] = []
|
const warnings: string[] = []
|
||||||
const information: string[] = []
|
const information: string[] = []
|
||||||
if (json.mappings === undefined || json.mappings.length === 0) {
|
if (json.mappings === undefined || json.mappings.length === 0) {
|
||||||
return { result: json }
|
return {result: json}
|
||||||
}
|
}
|
||||||
const ignoreToken = "ignore-image-in-then"
|
const ignoreToken = "ignore-image-in-then"
|
||||||
for (let i = 0; i < json.mappings.length; i++) {
|
for (let i = 0; i < json.mappings.length; i++) {
|
||||||
|
@ -626,17 +672,17 @@ class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJson> {
|
||||||
if (json["special"] !== undefined) {
|
if (json["special"] !== undefined) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"At " +
|
"At " +
|
||||||
context +
|
context +
|
||||||
': detected `special` on the top level. Did you mean `{"render":{ "special": ... }}`'
|
': detected `special` on the top level. Did you mean `{"render":{ "special": ... }}`'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (json["group"]) {
|
if (json["group"]) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"At " +
|
"At " +
|
||||||
context +
|
context +
|
||||||
': groups are deprecated, use `"label": ["' +
|
': groups are deprecated, use `"label": ["' +
|
||||||
json["group"] +
|
json["group"] +
|
||||||
'"]` instead'
|
'"]` instead'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const freeformType = json["freeform"]?.["type"]
|
const freeformType = json["freeform"]?.["type"]
|
||||||
|
@ -669,6 +715,7 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> {
|
||||||
super(
|
super(
|
||||||
"Various validation on tagRenderingConfigs",
|
"Various validation on tagRenderingConfigs",
|
||||||
new DetectShadowedMappings(layerConfig),
|
new DetectShadowedMappings(layerConfig),
|
||||||
|
new DetectConflictingAddExtraTags(),
|
||||||
new DetectMappingsWithImages(doesImageExist),
|
new DetectMappingsWithImages(doesImageExist),
|
||||||
new MiscTagRenderingChecks(options)
|
new MiscTagRenderingChecks(options)
|
||||||
)
|
)
|
||||||
|
@ -711,9 +758,9 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
if (!Constants.priviliged_layers.find((x) => x == json.id)) {
|
if (!Constants.priviliged_layers.find((x) => x == json.id)) {
|
||||||
errors.push(
|
errors.push(
|
||||||
context +
|
context +
|
||||||
": layer " +
|
": layer " +
|
||||||
json.id +
|
json.id +
|
||||||
" uses 'special' as source.osmTags. However, this layer is not a priviliged layer"
|
" uses 'special' as source.osmTags. However, this layer is not a priviliged layer"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -722,13 +769,13 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
if (json.title === undefined && json.source !== "special:library") {
|
if (json.title === undefined && json.source !== "special:library") {
|
||||||
errors.push(
|
errors.push(
|
||||||
context +
|
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) {
|
if (json.title === null) {
|
||||||
information.push(
|
information.push(
|
||||||
context +
|
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<LayerConfigJson> {
|
||||||
console.log(json.tagRenderings)
|
console.log(json.tagRenderings)
|
||||||
errors.push(
|
errors.push(
|
||||||
"At " +
|
"At " +
|
||||||
context +
|
context +
|
||||||
": some tagrenderings have a duplicate id: " +
|
": some tagrenderings have a duplicate id: " +
|
||||||
duplicates.join(", ")
|
duplicates.join(", ")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -775,8 +822,8 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
if (json["overpassTags"] !== undefined) {
|
if (json["overpassTags"] !== undefined) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"Layer " +
|
"Layer " +
|
||||||
json.id +
|
json.id +
|
||||||
'still uses the old \'overpassTags\'-format. Please use "source": {"osmTags": <tags>}\' instead of "overpassTags": <tags> (note: this isn\'t your fault, the custom theme generator still spits out the old format)'
|
'still uses the old \'overpassTags\'-format. Please use "source": {"osmTags": <tags>}\' instead of "overpassTags": <tags> (note: this isn\'t your fault, the custom theme generator still spits out the old format)'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const forbiddenTopLevel = [
|
const forbiddenTopLevel = [
|
||||||
|
@ -794,18 +841,18 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
if (json[forbiddenKey] !== undefined)
|
if (json[forbiddenKey] !== undefined)
|
||||||
errors.push(
|
errors.push(
|
||||||
context +
|
context +
|
||||||
": layer " +
|
": layer " +
|
||||||
json.id +
|
json.id +
|
||||||
" still has a forbidden key " +
|
" still has a forbidden key " +
|
||||||
forbiddenKey
|
forbiddenKey
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (json["hideUnderlayingFeaturesMinPercentage"] !== undefined) {
|
if (json["hideUnderlayingFeaturesMinPercentage"] !== undefined) {
|
||||||
errors.push(
|
errors.push(
|
||||||
context +
|
context +
|
||||||
": layer " +
|
": layer " +
|
||||||
json.id +
|
json.id +
|
||||||
" contains an old 'hideUnderlayingFeaturesMinPercentage'"
|
" contains an old 'hideUnderlayingFeaturesMinPercentage'"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,9 +869,9 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
if (this._path != undefined && this._path.indexOf(expected) < 0) {
|
if (this._path != undefined && this._path.indexOf(expected) < 0) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"Layer is in an incorrect place. The path is " +
|
"Layer is in an incorrect place. The path is " +
|
||||||
this._path +
|
this._path +
|
||||||
", but expected " +
|
", but expected " +
|
||||||
expected
|
expected
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -865,6 +912,13 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
if (json.tagRenderings !== undefined) {
|
||||||
const r = new On(
|
const r = new On(
|
||||||
"tagRenderings",
|
"tagRenderings",
|
||||||
|
@ -886,9 +940,9 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
if (hasCondition?.length > 0) {
|
if (hasCondition?.length > 0) {
|
||||||
errors.push(
|
errors.push(
|
||||||
"At " +
|
"At " +
|
||||||
context +
|
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" +
|
":\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, " ")
|
JSON.stringify(hasCondition, null, " ")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -903,7 +957,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
const preset = json.presets[i]
|
const preset = json.presets[i]
|
||||||
const tags: { k: string; v: string }[] = new And(
|
const tags: { k: string; v: string }[] = new And(
|
||||||
preset.tags.map((t) => TagUtils.Tag(t))
|
preset.tags.map((t) => TagUtils.Tag(t))
|
||||||
).asChange({ id: "node/-1" })
|
).asChange({id: "node/-1"})
|
||||||
const properties = {}
|
const properties = {}
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
properties[tag.k] = tag.v
|
properties[tag.k] = tag.v
|
||||||
|
@ -912,12 +966,12 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
if (!doMatch) {
|
if (!doMatch) {
|
||||||
errors.push(
|
errors.push(
|
||||||
context +
|
context +
|
||||||
".presets[" +
|
".presets[" +
|
||||||
i +
|
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: " +
|
"]: 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) +
|
JSON.stringify(properties) +
|
||||||
"\n The required tags are: " +
|
"\n The required tags are: " +
|
||||||
baseTags.asHumanString(false, false, {})
|
baseTags.asHumanString(false, false, {})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -949,9 +1003,14 @@ export class ValidateFilter extends DesugaringStep<FilterConfigJson> {
|
||||||
warnings?: string[]
|
warnings?: string[]
|
||||||
information?: string[]
|
information?: string[]
|
||||||
} {
|
} {
|
||||||
|
if (typeof filter === "string") {
|
||||||
|
// Calling another filter, we skip
|
||||||
|
return {result: filter}
|
||||||
|
}
|
||||||
const errors = []
|
const errors = []
|
||||||
for (const option of filter.options) {
|
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 field = option.fields[i]
|
||||||
const type = field.type ?? "string"
|
const type = field.type ?? "string"
|
||||||
if (Validators.availableTypes.find((t) => t === type) === undefined) {
|
if (Validators.availableTypes.find((t) => t === type) === undefined) {
|
||||||
|
@ -962,7 +1021,7 @@ export class ValidateFilter extends DesugaringStep<FilterConfigJson> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { result: filter, errors }
|
return {result: filter, errors}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -991,7 +1050,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{
|
||||||
const warnings: string[] = []
|
const warnings: string[] = []
|
||||||
const information: string[] = []
|
const information: string[] = []
|
||||||
|
|
||||||
const { layers, themes } = json
|
const {layers, themes} = json
|
||||||
const perOsmTag = new Map<
|
const perOsmTag = new Map<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
|
@ -1027,7 +1086,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let msg = "Possible duplicate filter: " + key
|
let msg = "Possible duplicate filter: " + key
|
||||||
for (const { filter, layer, layout } of value) {
|
for (const {filter, layer, layout} of value) {
|
||||||
let id = ""
|
let id = ""
|
||||||
if (layout !== undefined) {
|
if (layout !== undefined) {
|
||||||
id = layout.id + ":"
|
id = layout.id + ":"
|
||||||
|
|
|
@ -58,7 +58,7 @@ export default class TagRenderingConfig {
|
||||||
|
|
||||||
public readonly freeform?: {
|
public readonly freeform?: {
|
||||||
readonly key: string
|
readonly key: string
|
||||||
readonly type: string
|
readonly type: ValidatorType
|
||||||
readonly placeholder: Translation
|
readonly placeholder: Translation
|
||||||
readonly addExtraTags: UploadableTag[]
|
readonly addExtraTags: UploadableTag[]
|
||||||
readonly inline: boolean
|
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})`
|
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(<any>json.freeform.type) < 0
|
||||||
|
) {
|
||||||
|
throw `At ${context}: invalid type, perhaps you meant ${Utils.sortedByLevenshteinDistance(
|
||||||
|
json.freeform.key,
|
||||||
|
<any>Validators.availableTypes,
|
||||||
|
(s) => <any>s
|
||||||
|
)}`
|
||||||
|
}
|
||||||
|
const type: ValidatorType = <any>json.freeform.type ?? "string"
|
||||||
|
|
||||||
let placeholder: Translation = Translations.T(json.freeform.placeholder)
|
let placeholder: Translation = Translations.T(json.freeform.placeholder)
|
||||||
if (placeholder === undefined) {
|
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 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 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(
|
public constructChangeSpecification(
|
||||||
freeformValue: string | undefined,
|
freeformValue: string | undefined,
|
||||||
|
@ -685,38 +695,42 @@ export default class TagRenderingConfig {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return and
|
return and
|
||||||
} else {
|
}
|
||||||
// Is at least one mapping shown in the answer?
|
|
||||||
const someMappingIsShown = this.mappings.some((m) => {
|
// Is at least one mapping shown in the answer?
|
||||||
if (typeof m.hideInAnswer === "boolean") {
|
const someMappingIsShown = this.mappings.some((m) => {
|
||||||
return !m.hideInAnswer
|
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
|
|
||||||
}
|
}
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import { Unit } from "../../Models/Unit"
|
import { Unit } from "../../Models/Unit"
|
||||||
import UnitInput from "../Popup/UnitInput.svelte"
|
import UnitInput from "../Popup/UnitInput.svelte"
|
||||||
|
|
||||||
export let type: ValidatorType
|
export let type: ValidatorType
|
||||||
export let feedback: UIEventSource<Translation> | undefined = undefined
|
export let feedback: UIEventSource<Translation> | undefined = undefined
|
||||||
export let getCountry: () => string | undefined
|
export let getCountry: () => string | undefined
|
||||||
export let placeholder: string | Translation | undefined
|
export let placeholder: string | Translation | undefined
|
||||||
|
@ -63,6 +63,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unit && isNaN(Number(v))) {
|
if (unit && isNaN(Number(v))) {
|
||||||
|
console.debug("Not a number, but a unit is required")
|
||||||
value.setData(undefined)
|
value.setData(undefined)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import BaseUIElement from "../BaseUIElement"
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import { Translation } from "../i18n/Translation"
|
import { Translation } from "../i18n/Translation";
|
||||||
import Translations from "../i18n/Translations"
|
import Translations from "../i18n/Translations";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A 'TextFieldValidator' contains various methods to check and cleanup an entered value or to give feedback.
|
* 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
|
* What HTML-inputmode to use
|
||||||
*/
|
*/
|
||||||
public readonly inputmode?: string
|
public readonly inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'
|
||||||
public readonly textArea: boolean
|
public readonly textArea: boolean
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
name: string,
|
name: string,
|
||||||
explanation: string | BaseUIElement,
|
explanation: string | BaseUIElement,
|
||||||
inputmode?: string,
|
inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search',
|
||||||
textArea?: false | boolean
|
textArea?: false | boolean
|
||||||
) {
|
) {
|
||||||
this.name = name
|
this.name = name
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { Translation } from "../../i18n/Translation"
|
import { Translation } from "../../i18n/Translation"
|
||||||
import Translations from "../../i18n/Translations"
|
import Translations from "../../i18n/Translations"
|
||||||
import { Validator } from "../Validator"
|
import { Validator } from "../Validator"
|
||||||
|
import { ValidatorType } from "../Validators";
|
||||||
|
|
||||||
export default class FloatValidator extends Validator {
|
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")
|
super(name ?? "float", explanation ?? "A decimal number", "decimal")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
export let selectedTags: TagsFilter = undefined
|
export let selectedTags: TagsFilter = undefined
|
||||||
|
|
||||||
let mappings: Mapping[] = config?.mappings
|
let mappings: Mapping[] = config?.mappings
|
||||||
let searchTerm: Store<string> = new UIEventSource("")
|
let searchTerm: UIEventSource<string> = new UIEventSource("")
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Reference in a new issue