forked from MapComplete/MapComplete
Merge develop
This commit is contained in:
commit
827d9ae685
664 changed files with 33303 additions and 29790 deletions
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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, {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
10
src/UI/InputElement/Helpers/TimeInput.svelte
Normal file
10
src/UI/InputElement/Helpers/TimeInput.svelte
Normal 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" />
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
12
src/UI/InputElement/Validators/CollectionTimesValidator.ts
Normal file
12
src/UI/InputElement/Validators/CollectionTimesValidator.ts
Normal 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")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
import { Validator } from "../Validator"
|
||||
import ColorInput from "../Helpers/ColorInput.svelte"
|
||||
|
||||
export default class ColorValidator extends Validator {
|
||||
inputHelper = ColorInput
|
||||
|
||||
constructor() {
|
||||
super("color", "Shows a color picker")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import { Validator } from "../Validator"
|
||||
import DateInput from "../Helpers/DateInput.svelte"
|
||||
|
||||
export default class DateValidator extends Validator {
|
||||
public readonly inputHelper = DateInput
|
||||
public readonly hideInputField = true
|
||||
|
||||
constructor() {
|
||||
super("date", "A date with date picker")
|
||||
}
|
||||
|
|
@ -25,4 +29,5 @@ export default class DateValidator extends Validator {
|
|||
|
||||
return [year, month, day].join("-")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import IntValidator from "./IntValidator"
|
||||
import DirectionInput from "../Helpers/DirectionInput.svelte"
|
||||
|
||||
export default class DirectionValidator extends IntValidator {
|
||||
|
||||
public readonly inputHelper = DirectionInput
|
||||
constructor() {
|
||||
super(
|
||||
"direction",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Utils } from "../../../Utils"
|
|||
import { eliCategory } from "../../../Models/RasterLayerProperties"
|
||||
|
||||
export default class DistanceValidator extends Validator {
|
||||
|
||||
private readonly docs: string = [
|
||||
"#### Helper-arguments",
|
||||
"Options are:",
|
||||
|
|
@ -58,4 +59,5 @@ export default class DistanceValidator extends Validator {
|
|||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import UrlValidator from "./UrlValidator"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import ImageHelper from "../Helpers/ImageHelper.svelte"
|
||||
|
||||
export default class ImageUrlValidator extends UrlValidator {
|
||||
private static readonly allowedExtensions = ["jpg", "jpeg", "svg", "png"]
|
||||
public readonly isMeta = true
|
||||
public readonly inputHelper = ImageHelper
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
|
|
@ -37,4 +39,6 @@ export default class ImageUrlValidator extends UrlValidator {
|
|||
}
|
||||
return ImageUrlValidator.hasValidExternsion(str)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import { Validator } from "../Validator"
|
||||
import MarkdownUtils from "../../../Utils/MarkdownUtils"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
import OpeningHoursInput from "../Helpers/OpeningHoursInput.svelte"
|
||||
|
||||
export default class OpeningHoursValidator extends Validator {
|
||||
|
||||
public readonly inputHelper= OpeningHoursInput
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
"opening_hours",
|
||||
|
|
@ -39,4 +44,6 @@ export default class OpeningHoursValidator extends Validator {
|
|||
].join("\n")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@ import { Validator } from "../Validator"
|
|||
import { Translation } from "../../i18n/Translation"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import TagKeyValidator from "./TagKeyValidator"
|
||||
import SimpleTagInput from "../Helpers/SimpleTagInput.svelte"
|
||||
|
||||
/**
|
||||
* Checks that the input conforms `key=value`, where `key` and `value` don't have too much weird characters
|
||||
*/
|
||||
export default class SimpleTagValidator extends Validator {
|
||||
private static readonly KeyValidator = new TagKeyValidator()
|
||||
public readonly inputHelper = SimpleTagInput
|
||||
public readonly hideInputField = true
|
||||
|
||||
public readonly isMeta = true
|
||||
constructor() {
|
||||
|
|
@ -50,4 +53,6 @@ export default class SimpleTagValidator extends Validator {
|
|||
isValid(tag: string, _): boolean {
|
||||
return this.getFeedback(tag, _) === undefined
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import FloatValidator from "./FloatValidator"
|
||||
import SlopeInput from "../Helpers/SlopeInput.svelte"
|
||||
|
||||
export default class SlopeValidator extends FloatValidator {
|
||||
public readonly inputHelper =SlopeInput
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
"slope",
|
||||
|
|
@ -40,4 +43,5 @@ export default class SlopeValidator extends FloatValidator {
|
|||
}
|
||||
return super.reformat(str) + lastChar
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
import { Validator } from "../Validator"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import TagInput from "../Helpers/TagInput.svelte"
|
||||
|
||||
/**
|
||||
* Checks that the input conforms a JSON-encoded tag expression or a simpleTag`key=value`,
|
||||
*/
|
||||
export default class TagValidator extends Validator {
|
||||
public readonly isMeta = true
|
||||
public readonly inputHelper = TagInput
|
||||
public readonly hideInputField = true
|
||||
|
||||
constructor() {
|
||||
super("tag", "A simple tag of the format `key=value` OR a tagExpression")
|
||||
|
|
@ -18,4 +21,5 @@ export default class TagValidator extends Validator {
|
|||
isValid(tag: string, _): boolean {
|
||||
return this.getFeedback(tag, _) === undefined
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
15
src/UI/InputElement/Validators/TimeValidator.ts
Normal file
15
src/UI/InputElement/Validators/TimeValidator.ts
Normal 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")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
import { Validator } from "../Validator"
|
||||
import TranslationInput from "../Helpers/TranslationInput.svelte"
|
||||
|
||||
export default class TranslationValidator extends Validator {
|
||||
public readonly inputHelper = TranslationInput
|
||||
public readonly isMeta = true
|
||||
public readonly hideInputField = true
|
||||
constructor() {
|
||||
super("translation", "Makes sure the the string is of format `Record<string, string>` ")
|
||||
}
|
||||
|
|
@ -14,4 +17,5 @@ export default class TranslationValidator extends Validator {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ import { Validator } from "../Validator"
|
|||
import { Translation } from "../../i18n/Translation"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import MarkdownUtils from "../../../Utils/MarkdownUtils"
|
||||
import WikidataInputHelper from "../Helpers/WikidataInputHelper.svelte"
|
||||
|
||||
export default class WikidataValidator extends Validator {
|
||||
public static readonly _searchCache = new Map<string, Promise<WikidataResponse[]>>()
|
||||
public readonly inputHelper = WikidataInputHelper
|
||||
|
||||
private static docs = [
|
||||
"#### Helper arguments",
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue