Merge develop

This commit is contained in:
Pieter Vander Vennet 2025-08-01 15:18:48 +02:00
commit 827d9ae685
664 changed files with 33303 additions and 29790 deletions

View file

@ -0,0 +1,48 @@
<script lang="ts">/**
* Multiple 'SingleCollectionTime'-rules togehter
*/
import { Stores, UIEventSource } from "../../../../Logic/UIEventSource"
import SingleCollectionTime from "./SingleCollectionTime.svelte"
import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
import { Lists } from "../../../../Utils/Lists"
export let value: UIEventSource<string>
let initialRules: string[] = Lists.noEmpty(value.data?.split(";")?.map(v => v.trim()))
let singleRules: UIEventSource<UIEventSource<string>[]> = new UIEventSource(
initialRules?.map(v => new UIEventSource(v)) ?? []
)
if(singleRules.data.length === 0){
singleRules.data.push(new UIEventSource(undefined))
}
singleRules.bindD(stores => Stores.concat(stores)).addCallbackAndRunD(subrules => {
console.log("Setting subrules", subrules)
value.set(Lists.noEmpty(subrules).join("; "))
})
function rm(rule: UIEventSource){
const index = singleRules.data.indexOf(rule)
singleRules.data.splice(index, 1)
singleRules.ping()
}
</script>
<div class="interactive">
{#each $singleRules as rule}
<SingleCollectionTime value={rule}>
<svelte:fragment slot="right">
{#if $singleRules.length > 1}
<button on:click={() => { rm(rule) }} class="as-link">
<TrashIcon class="w-6 h-6" />
</button>
{/if}
</svelte:fragment>
</SingleCollectionTime>
{/each}
<button on:click={() => {singleRules.data.push(new UIEventSource(undefined)); singleRules.ping()}}>Add schedule
</button>
</div>

View file

@ -0,0 +1,122 @@
<script lang="ts">
import TimeInput from "../TimeInput.svelte"
import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
import Checkbox from "../../../Base/Checkbox.svelte"
import Tr from "../../../Base/Tr.svelte"
import { Stores, UIEventSource } from "../../../../Logic/UIEventSource"
import Translations from "../../../i18n/Translations"
import { OH } from "../../../OpeningHours/OpeningHours"
import { Lists } from "../../../../Utils/Lists"
import { Translation } from "../../../i18n/Translation"
export let value: UIEventSource<string>
const wt = Translations.t.general.weekdays.abbreviations
/*
Single rule for collection times, e.g. "Mo-Fr 10:00, 17:00"
*/
let weekdays: Translation[] = [wt.monday, wt.tuesday, wt.wednesday, wt.thursday, wt.friday, wt.saturday, wt.sunday, Translations.t.general.opening_hours.ph]
let initialTimes= Lists.noEmpty(value.data?.split(" ")?.[1]?.split(",")?.map(s => s.trim()) ?? [])
let values = new UIEventSource(initialTimes.map(t => new UIEventSource(t)))
if(values.data.length === 0){
values.data.push(new UIEventSource(""))
}
let daysOfTheWeek = [...OH.days, "PH"]
let selectedDays = daysOfTheWeek.map(() => new UIEventSource(false))
let initialDays = Lists.noEmpty(value.data?.split(" ")?.[0]?.split(",") ?? [])
for (const initialDay of initialDays) {
if (initialDay.indexOf("-") > 0) {
let [start, end] = initialDay.split("-")
let startindex = daysOfTheWeek.indexOf(start)
let stopindex = daysOfTheWeek.indexOf(end)
for (let i = startindex; i <= stopindex; i++) {
selectedDays[i]?.set(true)
}
} else {
let index = daysOfTheWeek.indexOf(initialDay)
if (index >= 0) {
selectedDays[index]?.set(true)
}
}
}
let selectedDaysBound = Stores.concat(selectedDays)
.mapD(days => Lists.noNull(days.map((selected, i) => selected ? daysOfTheWeek[i] : undefined)))
let valuesConcat: Store<string[]> = values.bindD(values => Stores.concat(values))
.mapD(values => Lists.noEmpty(values))
valuesConcat.mapD(times => {
console.log(times)
times = Lists.noNull(times)
if (!times || times?.length === 0) {
return undefined
}
times?.sort(/*concatted, new array*/)
return (Lists.noEmpty(selectedDaysBound.data).join(",") + " " + times).trim()
}, [selectedDaysBound]).addCallbackAndRunD(v => value.set(v))
function selectWeekdays() {
for (let i = 0; i < 5; i++) {
selectedDays[i].set(true)
}
for (let i = 5; i < selectedDays.length; i++) {
selectedDays[i].set(false)
}
}
function clearDays() {
for (let i = 0; i < selectedDays.length; i++) {
selectedDays[i].set(false)
}
}
</script>
<div class="rounded-xl my-2 p-2 low-interaction flex w-full justify-between flex-wrap">
<div class="flex flex-col">
<div class="flex flex-wrap">
{#each $values as value, i}
<div class="flex mx-4 gap-x-1 items-center">
<TimeInput {value} />
{#if $values.length > 1}
<button class="as-link">
<TrashIcon class="w-6 h-6" />
</button>
{/if}
</div>
{/each}
<button on:click={() => {values.data.push(new UIEventSource(undefined)); values.ping()}}>
<PlusCircle class="w-6 h-6" />
Add time
</button>
</div>
<div class="flex w-fit flex-wrap">
{#each daysOfTheWeek as day, i}
<div class="w-fit">
<Checkbox selected={selectedDays[i]}>
<Tr t={weekdays[i]} />
</Checkbox>
</div>
{/each}
</div>
</div>
<div class="flex flex-wrap justify-between w-full">
<div class="flex flex-wrap gap-x-4">
<button class="as-link text-sm" on:click={() => selectWeekdays()}>Select weekdays</button>
<button class="as-link text-sm" on:click={() => clearDays()}>Clear days</button>
</div>
<slot name="right" />
</div>
</div>

View file

@ -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>

View file

@ -20,6 +20,7 @@
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import Translations from "../../i18n/Translations"
import Tr from "../../Base/Tr.svelte"
import { onDestroy } from "svelte"
export let value: UIEventSource<number>
export let feature: Feature
@ -84,7 +85,7 @@
},
]
},
[mapLocation]
[mapLocation], onDestroy
)
new ShowDataLayer(map, {

View file

@ -79,7 +79,7 @@
forceIndex = floors.data.indexOf(level)
})
Stores.Chronic(50).addCallback(() => stabilize())
Stores.chronic(50).addCallback(() => stabilize())
floors.addCallback((floors) => {
forceIndex = floors.findIndex((s) => s === value.data)
})

View file

@ -29,7 +29,7 @@
let element: HTMLTableElement
function range(n: number) {
return Utils.TimesT(n, (n) => n)
return Utils.timesT(n, (n) => n)
}
function clearSelection() {

View file

@ -7,6 +7,7 @@
import { GeoOperations } from "../../../Logic/GeoOperations"
import If from "../../Base/If.svelte"
import type { SpecialVisualizationState } from "../../SpecialVisualization"
import { onDestroy } from "svelte"
/**
* The value exported to outside, saved only when the button is pressed
@ -61,7 +62,7 @@
} else {
return -1
}
})
}, onDestroy)
beta.map(
(beta) => {
@ -77,7 +78,7 @@
previewDegrees.setData(beta + "°")
previewPercentage.setData(degreesToPercentage(beta))
},
[valuesign, beta]
[valuesign, beta], onDestroy
)
function onSave() {

View file

@ -0,0 +1,10 @@
<script lang="ts">
/**
* Simple wrapper around the HTML-time field.
*/
import { UIEventSource } from "../../../Logic/UIEventSource"
export let value: UIEventSource<undefined | string>
</script>
<input bind:value={$value} type="time" />

View file

@ -5,7 +5,7 @@
import Translations from "../../i18n/Translations"
import Tr from "../../Base/Tr.svelte"
import { ImmutableStore, Store, Stores, UIEventSource } from "../../../Logic/UIEventSource"
import { ImmutableStore, Store, UIEventSource } from "../../../Logic/UIEventSource"
import Wikidata, { WikidataResponse } from "../../../Logic/Web/Wikidata"
import Locale from "../../i18n/Locale"
import Loading from "../../Base/Loading.svelte"
@ -13,6 +13,7 @@
import { Utils } from "../../../Utils"
import WikidataValidator from "../Validators/WikidataValidator"
import Searchbar from "../../Base/Searchbar.svelte"
import { Lists } from "../../../Utils/Lists"
const t = Translations.t.general.wikipedia
@ -64,7 +65,7 @@
})
WikidataValidator._searchCache.set(key, promise)
}
return Stores.FromPromiseWithErr(promise)
return UIEventSource.fromPromiseWithErr(promise)
}
)
@ -80,7 +81,7 @@
seen.push(item)
}
}
return Utils.NoNull(seen).filter((i) => !knownIds.has(i.id))
return Lists.noNull(seen).filter((i) => !knownIds.has(i.id))
})
</script>

View file

@ -4,14 +4,15 @@
Wrapper around 'WikidataInput.svelte' which handles the arguments
*/
import { UIEventSource } from "../../Logic/UIEventSource"
import Locale from "../i18n/Locale"
import { Utils } from "../../Utils"
import Wikidata from "../../Logic/Web/Wikidata"
import WikidataInput from "./Helpers/WikidataInput.svelte"
import { UIEventSource } from "../../../Logic/UIEventSource"
import Locale from "../../i18n/Locale"
import { Utils } from "../../../Utils"
import Wikidata from "../../../Logic/Web/Wikidata"
import WikidataInput from "../Helpers/WikidataInput.svelte"
import type { Feature } from "geojson"
import { onDestroy } from "svelte"
import WikidataValidator from "./Validators/WikidataValidator"
import WikidataValidator from "../Validators/WikidataValidator"
import { Lists } from "../../../Utils/Lists"
export let args: (string | number | boolean)[] = []
export let feature: Feature
@ -43,12 +44,8 @@
})
)
let instanceOf: number[] = Utils.NoNull(
(options?.instanceOf ?? []).map((i) => Wikidata.QIdToNumber(i))
)
let notInstanceOf: number[] = Utils.NoNull(
(options?.notInstanceOf ?? []).map((i) => Wikidata.QIdToNumber(i))
)
let instanceOf: number[] = Lists.noNull((options?.instanceOf ?? []).map((i) => Wikidata.QIdToNumber(i)))
let notInstanceOf: number[] = Lists.noNull((options?.notInstanceOf ?? []).map((i) => Wikidata.QIdToNumber(i)))
let allowMultipleArg = options?.["multiple"] ?? "yes"
let allowMultiple = allowMultipleArg === "yes" || "" + allowMultipleArg === "true"

View file

@ -1,60 +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 "./WikidataInputHelper.svelte"
import type { Validator } from "./Validator"
import DistanceInput from "./Helpers/DistanceInput.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 === "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}

View file

@ -1,65 +0,0 @@
import { UIEventSource } from "../../Logic/UIEventSource"
import { MapProperties } from "../../Models/MapProperties"
import { Feature } from "geojson"
import { GeoOperations } from "../../Logic/GeoOperations"
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: string[] = ["translation", "simple_tag", "tag"]
/**
* Constructs a mapProperties-object for the given properties.
* Assumes that the first helper-args contains the desired zoom-level
* @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
}
}

View file

@ -173,7 +173,7 @@
)
}
const isValid = unvalidatedText.map((v) => validator?.isValid(v, getCountry) ?? true)
const isValid = unvalidatedText.map((v) => validator?.isValid(v, getCountry) ?? true, onDestroy)
let htmlElem: HTMLInputElement | HTMLTextAreaElement

View file

@ -1,5 +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.
@ -16,18 +18,12 @@ export abstract class Validator {
* What HTML-inputmode to use?
* Note: some inputHelpers will completely hide the default text field. This is kept in InputHelpers.hideInputField
*/
public readonly inputmode?:
| "none"
| "text"
| "tel"
| "url"
| "email"
| "numeric"
| "decimal"
| "search"
public readonly inputmode?: HTMLInputTypeAttribute
public readonly textArea: boolean
public readonly isMeta?: boolean
public readonly inputHelper : ComponentType = undefined
public readonly hideInputField: boolean = false
constructor(
name: string,
@ -87,4 +83,5 @@ export abstract class Validator {
public validateArguments(args: string): undefined | string {
return undefined
}
}

View file

@ -1,99 +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 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",
"direction",
"distance",
"email",
"fediverse",
"float",
"icon",
"id",
"image",
"int",
"key",
"nat",
"nsi",
"opening_hours",
"pfloat",
"phone",
"pnat",
"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 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 SimpleTagValidator(),
new TranslationValidator(),
new TagValidator(),
new NameSuggestionIndexValidator(),
new DistanceValidator(),
]
private static _byType = Validators._byTypeConstructor()

View file

@ -0,0 +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")
}
}

View file

@ -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")
}
}

View file

@ -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("-")
}
}

View file

@ -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",

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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")
)
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -0,0 +1,15 @@
import { Validator } from "../Validator"
import TimeInput from "../Helpers/TimeInput.svelte"
export class TimeValidator extends Validator {
public readonly inputmode = "time"
public readonly inputHelper = TimeInput
public readonly hideInputField = true
constructor() {
super("time", "A time picker")
}
}

View file

@ -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
}
}
}

View file

@ -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",
@ -104,6 +106,10 @@ Another example is to search for species and trees:
if (str === undefined) {
return false
}
if (str.length === 0) {
// Don't show an exclamation mark if empty, the other code will prevent saving
return true
}
if (str.length == 1) {
return false
}
@ -182,4 +188,5 @@ Another example is to search for species and trees:
}
return clipped
}
}