Studio: WIP

This commit is contained in:
Pieter Vander Vennet 2023-08-23 11:11:53 +02:00
parent 04ecdad1bb
commit 903e168a89
62 changed files with 19152 additions and 123399 deletions

View file

@ -0,0 +1,20 @@
<script lang="ts">/**
* Input helper to create a tag. The tag is JSON-encoded
*/
import { UIEventSource } from "../../../Logic/UIEventSource";
import BasicTagInput from "../../Studio/TagInput/BasicTagInput.svelte";
export let value: UIEventSource<undefined | string>;
export let uploadableOnly: boolean;
export let overpassSupportNeeded: boolean;
/**
* Only show the taginfo-statistics if they are suspicious (thus: less then 250 entries)
*/
export let silent: boolean = false;
</script>
<BasicTagInput {overpassSupportNeeded} {silent} tag={value} {uploadableOnly} />

View file

@ -0,0 +1,33 @@
<script lang="ts">/**
* Input helper to create a tag. The tag is JSON-encoded
*/
import { UIEventSource } from "../../../Logic/UIEventSource";
import type { TagConfigJson } from "../../../Models/ThemeConfig/Json/TagConfigJson";
import FullTagInput from "../../Studio/TagInput/FullTagInput.svelte";
export let value: UIEventSource<undefined | string>;
export let uploadableOnly: boolean;
export let overpassSupportNeeded: boolean;
/**
* Only show the taginfo-statistics if they are suspicious (thus: less then 250 entries)
*/
export let silent: boolean = false;
let tag: UIEventSource<string | TagConfigJson> = value.sync(s => {
try {
return JSON.parse(s);
} catch (e) {
return s;
}
}, [], t => {
if(typeof t === "string"){
return t
}
return JSON.stringify(t);
});
</script>
<FullTagInput {overpassSupportNeeded} {silent} {tag} {uploadableOnly} />

View file

@ -15,6 +15,8 @@ import { Feature } from "geojson"
import { GeoOperations } from "../../Logic/GeoOperations"
import ImageHelper from "./Helpers/ImageHelper.svelte"
import TranslationInput from "./Helpers/TranslationInput.svelte"
import TagInput from "./Helpers/TagInput.svelte"
import SimpleTagInput from "./Helpers/SimpleTagInput.svelte"
export interface InputHelperProperties {
/**
@ -59,9 +61,11 @@ export default class InputHelpers {
wikidata: InputHelpers.constructWikidataHelper,
image: (value) => new SvelteUIElement(ImageHelper, { value }),
translation: (value) => new SvelteUIElement(TranslationInput, { value }),
tag: (value) => new SvelteUIElement(TagInput, { value }),
simple_tag: (value) => new SvelteUIElement(SimpleTagInput, { value }),
} as const
public static hideInputField : string[] = ["translation"]
public static hideInputField: string[] = ["translation", "simple_tag", "tag"]
/**
* Constructs a mapProperties-object for the given properties.

View file

@ -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,23 @@ export abstract class Validator {
/**
* What HTML-inputmode to use
*/
public readonly inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'
public readonly inputmode?:
| "none"
| "text"
| "tel"
| "url"
| "email"
| "numeric"
| "decimal"
| "search"
public readonly textArea: boolean
public readonly isMeta?: boolean
constructor(
name: string,
explanation: string | BaseUIElement,
inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search',
inputmode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search",
textArea?: false | boolean
) {
this.name = name

View file

@ -23,6 +23,8 @@ 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"
export type ValidatorType = (typeof Validators.availableTypes)[number]
@ -48,7 +50,9 @@ export default class Validators {
"simple_tag",
"key",
"translation",
"icon",
"fediverse",
"tag",
] as const
public static readonly AllValidators: ReadonlyArray<Validator> = [
@ -70,20 +74,15 @@ export default class Validators {
new ColorValidator(),
new ImageUrlValidator(),
new SimpleTagValidator(),
new TagValidator(),
new TagKeyValidator(),
new TranslationValidator(),
new IconValidator(),
new FediverseValidator(),
]
private static _byType = Validators._byTypeConstructor()
private static _byTypeConstructor(): Map<ValidatorType, Validator> {
const map = new Map<ValidatorType, Validator>()
for (const validator of Validators.AllValidators) {
map.set(<ValidatorType>validator.name, validator)
}
return map
}
public static HelpText(): BaseUIElement {
const explanations: BaseUIElement[] = Validators.AllValidators.map((type) =>
new Combine([new Title(type.name, 3), type.explanation]).SetClass("flex flex-col")
@ -95,6 +94,14 @@ export default class Validators {
]).SetClass("flex flex-col")
}
private static _byTypeConstructor(): Map<ValidatorType, Validator> {
const map = new Map<ValidatorType, Validator>()
for (const validator of Validators.AllValidators) {
map.set(<ValidatorType>validator.name, validator)
}
return map
}
static get(type: ValidatorType): Validator {
return Validators._byType.get(type)
}

View file

@ -0,0 +1,46 @@
import { Validator } from "../Validator"
import { Translation } from "../../i18n/Translation"
import licenses from "../../../assets/generated/license_info.json"
import { Utils } from "../../../Utils"
export default class IconValidator extends Validator {
private static allLicenses = new Set(licenses.map((l) => l.path))
private static allLicensesArr = Array.from(IconValidator.allLicenses)
public static readonly isMeta = true
constructor() {
super("icon", "Makes sure that a valid .svg-path is added")
}
getFeedback(s: string, getCountry, sloppy?: boolean): Translation | undefined {
if (!s.startsWith("http")) {
if (!IconValidator.allLicenses.has(s)) {
const close = sloppy
? []
: Utils.sortedByLevenshteinDistance(
s.substring(s.lastIndexOf("/")),
IconValidator.allLicensesArr,
(s) => s.substring(s.lastIndexOf("/"))
).slice(0, 5)
return new Translation(
[
`Unkown builtin icon ${s}, perhaps you meant one of: <ul>`,
...close.map(
(item) =>
`<li><span class="flex justify-start"> <img src='${item}' class="w-6 h-6"/>${item}</span></li>`
),
"</ul>",
].join("")
)
}
}
if (!s.endsWith(".svg")) {
return new Translation("An icon should end with `.svg`")
}
return undefined
}
isValid(key: string, getCountry?: () => string): boolean {
return this.getFeedback(key, getCountry, true) === undefined
}
}

View file

@ -3,6 +3,7 @@ import { Translation } from "../../i18n/Translation"
export default class ImageUrlValidator extends UrlValidator {
private static readonly allowedExtensions = ["jpg", "jpeg", "svg", "png"]
public readonly isMeta = true
constructor() {
super(

View file

@ -8,6 +8,8 @@ import TagKeyValidator from "./TagKeyValidator"
*/
export default class SimpleTagValidator extends Validator {
private static readonly KeyValidator = new TagKeyValidator()
public readonly isMeta = true
constructor() {
super(
"simple_tag",

View file

@ -3,6 +3,8 @@ import { Translation } from "../../i18n/Translation"
import Translations from "../../i18n/Translations"
export default class TagKeyValidator extends Validator {
public readonly isMeta = true
constructor() {
super("key", "Validates a key, mostly that no weird characters are used")
}

View file

@ -0,0 +1,24 @@
import { Validator } from "../Validator"
import { Translation } from "../../i18n/Translation"
import Translations from "../../i18n/Translations"
import TagKeyValidator from "./TagKeyValidator"
import SimpleTagValidator from "./SimpleTagValidator"
/**
* 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
constructor() {
super("tag", "A simple tag of the format `key=value` OR a tagExpression")
}
getFeedback(tag: string, _): Translation | undefined {
return undefined
}
isValid(tag: string, _): boolean {
return this.getFeedback(tag, _) === undefined
}
}

View file

@ -1,6 +1,8 @@
import { Validator } from "../Validator"
export default class TranslationValidator extends Validator {
public readonly isMeta = true
constructor() {
super("translation", "Makes sure the the string is of format `Record<string, string>` ")
}