forked from MapComplete/MapComplete
Themes(postboxes): add 'points_in_time' input element, add 'collection_times' to posbox theme, fix #757
This commit is contained in:
parent
4c0a42d5b6
commit
602c51bcb9
15 changed files with 425 additions and 68 deletions
|
|
@ -0,0 +1,48 @@
|
|||
<script lang="ts">/**
|
||||
* Multiple 'SingleCollectionTime'-rules togehter
|
||||
*/
|
||||
import { Stores, UIEventSource } from "../../../../Logic/UIEventSource"
|
||||
import SingleCollectionTime from "./SingleCollectionTime.svelte"
|
||||
import { Utils } from "../../../../Utils"
|
||||
import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
|
||||
export let value: UIEventSource<string>
|
||||
|
||||
let initialRules: string[] = Utils.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(Utils.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 PlusCircle from "@rgossiaux/svelte-heroicons/solid/PlusCircle"
|
||||
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 { Utils } from "../../../../Utils"
|
||||
|
||||
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= Utils.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 = Utils.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 => Utils.NoNull(days.map((selected, i) => selected ? daysOfTheWeek[i] : undefined)))
|
||||
let valuesConcat: Store<string[]> = values.bindD(values => Stores.concat(values))
|
||||
.mapD(values => Utils.NoEmpty(values))
|
||||
valuesConcat.mapD(times => {
|
||||
console.log(times)
|
||||
times = Utils.NoNull(times)
|
||||
if (!times || times?.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
times?.sort(/*concatted, new array*/)
|
||||
return (Utils.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>
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
import WikidataInputHelper from "./Helpers/WikidataInputHelper.svelte"
|
||||
import DistanceInput from "./Helpers/DistanceInput.svelte"
|
||||
import TimeInput from "./Helpers/TimeInput.svelte"
|
||||
import CollectionTimes from "./Helpers/CollectionTimes/CollectionTimes.svelte"
|
||||
|
||||
export let type: ValidatorType
|
||||
export let value: UIEventSource<string | object>
|
||||
|
|
@ -42,6 +43,8 @@
|
|||
<DateInput {value} />
|
||||
{:else if type === "time"}
|
||||
<TimeInput {value} />
|
||||
{:else if type === "points_in_time"}
|
||||
<CollectionTimes {value} />
|
||||
{:else if type === "color"}
|
||||
<ColorInput {value} />
|
||||
{:else if type === "image"}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { UIEventSource } from "../../Logic/UIEventSource"
|
|||
import { MapProperties } from "../../Models/MapProperties"
|
||||
import { Feature } from "geojson"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import { ValidatorType } from "./Validators"
|
||||
|
||||
export interface InputHelperProperties {
|
||||
/**
|
||||
|
|
@ -25,11 +26,12 @@ export interface InputHelperProperties {
|
|||
}
|
||||
|
||||
export default class InputHelpers {
|
||||
public static hideInputField: string[] = ["translation", "simple_tag", "tag","time"]
|
||||
public static hideInputField: ValidatorType[] = ["translation", "simple_tag", "tag","time"]
|
||||
|
||||
/**
|
||||
* Constructs a mapProperties-object for the given properties.
|
||||
* Assumes that the first helper-args contains the desired zoom-level
|
||||
* Used for the 'direction' input helper
|
||||
* @param properties
|
||||
* @private
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import NameSuggestionIndexValidator from "./Validators/NameSuggestionIndexValida
|
|||
import CurrencyValidator from "./Validators/CurrencyValidator"
|
||||
import RegexValidator from "./Validators/RegexValidator"
|
||||
import { TimeValidator } from "./Validators/TimeValidator"
|
||||
import CollectionTimesValidator from "./Validators/CollectionTimesValidator"
|
||||
|
||||
export type ValidatorType = (typeof Validators.availableTypes)[number]
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ export default class Validators {
|
|||
"pfloat",
|
||||
"phone",
|
||||
"pnat",
|
||||
"points_in_time",
|
||||
"regex",
|
||||
"simple_tag",
|
||||
"slope",
|
||||
|
|
@ -97,6 +99,7 @@ export default class Validators {
|
|||
new NameSuggestionIndexValidator(),
|
||||
new CurrencyValidator(),
|
||||
new RegexValidator(),
|
||||
new CollectionTimesValidator()
|
||||
]
|
||||
|
||||
private static _byType = Validators._byTypeConstructor()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
import StringValidator from "./StringValidator"
|
||||
|
||||
export default class CollectionTimesValidator extends StringValidator{
|
||||
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")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue