Merge develop

This commit is contained in:
Pieter Vander Vennet 2024-09-17 02:55:01 +02:00
commit fec3608ca4
57 changed files with 1160 additions and 594 deletions

View file

@ -8,6 +8,8 @@
import { Utils } from "../../Utils"
import type { ValidatorType } from "../InputElement/Validators"
import InputHelper from "../InputElement/InputHelper.svelte"
import { Translation } from "../i18n/Translation"
import Tr from "../Base/Tr.svelte"
export let filteredLayer: FilteredLayer
export let option: FilterConfigOption
@ -36,7 +38,7 @@
appliedFilter?.setData(FilteredLayer.fieldsToString(properties))
}
let firstValue : UIEventSource<string>
let firstValue: UIEventSource<string>
for (const field of option.fields) {
// A bit of cheating: the 'parts' will have '}' suffixed for fields
const src = new UIEventSource<string>(initialState[field.name] ?? "")
@ -47,9 +49,10 @@
onDestroy(
src.stabilized(200).addCallback(() => {
setFields()
}),
})
)
}
let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
</script>
<div class="low-interaction p-1 rounded-2xl px-3" class:interactive={$firstValue?.length > 0}>
@ -58,11 +61,15 @@
<!-- This is a field! -->
<span class="mx-1">
<InputHelper value={fieldValues[part["subs"]]} type={fieldTypes[part["subs"]]}>
<ValidatedInput slot="fallback" value={fieldValues[part["subs"]]} type={fieldTypes[part["subs"]]} />
<ValidatedInput slot="fallback" value={fieldValues[part["subs"]]} type={fieldTypes[part["subs"]]}
{feedback} />
</InputHelper>
</span>
{:else}
{@html part["message"]}
{/if}
{/each}
{#if $feedback}
<Tr cls="alert" t={$feedback}/>
{/if}
</div>

View file

@ -90,13 +90,13 @@
<LoginButton osmConnection={state.osmConnection} slot="not-logged-in" />
<div class="flex items-center gap-x-4">
{#if $userdetails.img}
<img src={$userdetails.img} class="h-14 w-14 rounded-full" />
<img alt="avatar" src={$userdetails.img} class="h-14 w-14 rounded-full" />
{/if}
<b>{$userdetails.name}</b>
</div>
</LoginToggle>
<Page {onlyLink} shown={pg.usersettings} bodyPadding="p-0">
<Page {onlyLink} shown={pg.usersettings} bodyPadding="p-0 pb-4">
<svelte:fragment slot="header">
<CogIcon />
<Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})} />

View file

@ -220,8 +220,8 @@
<td/>
{/if}
{#each range(7) as wd}
<OHCell type="half" {h} {wd} on:start={() => startSelection(wd, h)} on:end={() => endSelection(wd, h)}
on:move={() => moved(wd, h)} on:clear={() => clearSelection()} />
<OHCell type="half" {h} {wd} on:start={() => startSelection(wd, h + 0.5)} on:end={() => endSelection(wd, h + 0.5)}
on:move={() => moved(wd, h + 0.5)} on:clear={() => clearSelection()} />
{/each}
</tr>

View file

@ -15,9 +15,6 @@ import UrlValidator from "./Validators/UrlValidator"
import PhoneValidator from "./Validators/PhoneValidator"
import OpeningHoursValidator from "./Validators/OpeningHoursValidator"
import ColorValidator from "./Validators/ColorValidator"
import BaseUIElement from "../BaseUIElement"
import Combine from "../Base/Combine"
import Title from "../Base/Title"
import SimpleTagValidator from "./Validators/SimpleTagValidator"
import ImageUrlValidator from "./Validators/ImageUrlValidator"
import TagKeyValidator from "./Validators/TagKeyValidator"
@ -30,6 +27,7 @@ import SlopeValidator from "./Validators/SlopeValidator"
import VeloparkValidator from "./Validators/VeloparkValidator"
import NameSuggestionIndexValidator from "./Validators/NameSuggestionIndexValidator"
import CurrencyValidator from "./Validators/CurrencyValidator"
import RegexValidator from "./Validators/RegexValidator"
export type ValidatorType = (typeof Validators.availableTypes)[number]
@ -64,6 +62,7 @@ export default class Validators {
"velopark",
"nsi",
"currency",
"regex"
] as const
public static readonly AllValidators: ReadonlyArray<Validator> = [
@ -95,6 +94,7 @@ export default class Validators {
new VeloparkValidator(),
new NameSuggestionIndexValidator(),
new CurrencyValidator(),
new RegexValidator()
]
private static _byType = Validators._byTypeConstructor()

View file

@ -0,0 +1,22 @@
import StringValidator from "./StringValidator"
import { s } from "vitest/dist/env-afee91f0"
import { Translation } from "../../i18n/Translation"
import Translations from "../../i18n/Translations"
export default class RegexValidator extends StringValidator{
constructor() {
super("regex", "Validates a regex")
}
getFeedback(s: string): Translation | undefined {
try{
new RegExp(s)
}catch (e) {
return Translations.T("Not a valid Regex: "+e)
}
}
isValid(s: string): boolean {
return this.getFeedback(s) === undefined
}
}

View file

@ -9,8 +9,8 @@
import BackButton from "../Base/BackButton.svelte"
import NextButton from "../Base/NextButton.svelte"
import WikipediaPanel from "../Wikipedia/WikipediaPanel.svelte"
import { createEventDispatcher } from "svelte"
import Plantnet_logo from "../../assets/svg/Plantnet_logo.svelte"
import ArrowPath from "@babeard/svelte-heroicons/mini/ArrowPath"
/**
* The main entry point for the plantnet wizard
@ -23,7 +23,6 @@
*/
export let imageUrls: Store<string[]>
export let onConfirm: (wikidataId: string) => void
const dispatch = createEventDispatcher<{ selected: string }>()
let collapsedMode = true
let options: UIEventSource<PlantNetSpeciesMatch[]> = new UIEventSource<PlantNetSpeciesMatch[]>(
undefined
@ -38,18 +37,20 @@
let done = false
function speciesSelected(species: PlantNetSpeciesMatch) {
function speciesSelected(species: string) {
console.log("Selected:", species)
selectedOption = species
}
async function detectSpecies() {
error = undefined
collapsedMode = false
try {
const result = await PlantNet.query(imageUrls.data.slice(0, 5))
options.set(result.results.filter((r) => r.score > 0.005).slice(0, 8))
} catch (e) {
console.error("Caught", e)
error = e
}
}
@ -60,8 +61,12 @@
<button class="w-full" on:click={detectSpecies}>
<Tr t={t.button} />
</button>
{:else if $error !== undefined}
{:else if error !== undefined}
<Tr cls="alert" t={t.error.Subs({ error })} />
<button on:click={() => detectSpecies()}>
<ArrowPath class="w-6 h-6"/>
<Tr t={Translations.t.general.retry}/>
</button>
{:else if $imageUrls.length === 0}
<!-- No urls are available, show the explanation instead-->
<div class=" border-region relative mb-1 p-2">

View file

@ -4,6 +4,7 @@
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import Searchbar from "../Base/Searchbar.svelte"
import Translations from "../i18n/Translations"
import { Utils } from "../../Utils"
export let tags: UIEventSource<Record<string, any>>
export let tagKeys = tags.map((tgs) => (tgs === undefined ? [] : Object.keys(tgs)))
@ -34,10 +35,19 @@
const metaKeys: string[] = [].concat(...SimpleMetaTaggers.metatags.map((k) => k.keys))
let allCalculatedTags = new Set<string>([...calculatedTags, ...metaKeys])
let search = new UIEventSource<string>("")
function downloadAsJson(){
Utils.offerContentsAsDownloadableFile(
JSON.stringify(tags.data, null, " "), "tags-"+(tags.data.id ?? layer?.id ?? "")+".json"
)
}
</script>
<section>
<Searchbar value={search} placeholder={Translations.T("Search a key")}></Searchbar>
<button class="as-link" on:click={() => downloadAsJson()}>
Download as JSON
</button>
<table class="zebra-table break-all">
<tr>
<th>Key</th>

View file

@ -30,7 +30,7 @@
.getSchemaStartingWith(schema.path)
.filter((part) => part.path.length - 1 === schema.path.length)
let usesOverride = value["builtin"] !== undefined
let usesOverride = value?.["builtin"] !== undefined
function schemaForMultitype() {
const sch = { ...schema }
@ -138,7 +138,7 @@
</div>
{:else if typeof value === "string"}
Builtin: <b>{value}</b>
{:else if value["builtin"]}
{:else if value?.["builtin"]}
reused tagrendering <span class="font-bold">{JSON.stringify(value["builtin"])}</span>
{:else}
<Tr cls="font-bold" t={Translations.T(value?.question ?? value?.render)} />

View file

@ -159,6 +159,9 @@ export abstract class EditJsonState<T> {
}
public getSchemaStartingWith(path: string[]) {
if(path === undefined){
return undefined
}
return this.schema.filter(
(sch) =>
!path.some((part, i) => !(sch.path.length > path.length && sch.path[i] === part))

View file

@ -5,18 +5,23 @@
import { TrashIcon } from "@babeard/svelte-heroicons/mini"
import ShowConversionMessage from "./ShowConversionMessage.svelte"
import Markdown from "../Base/Markdown.svelte"
import type { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import type {
QuestionableTagRenderingConfigJson
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import CollapsedTagRenderingPreview from "./CollapsedTagRenderingPreview.svelte"
import { Accordion } from "flowbite-svelte"
import { Utils } from "../../Utils"
export let state: EditJsonState<any>
export let path: (string | number)[] = []
let schema: ConfigMeta = state.getSchema(path)[0]
console.log("SBA got schema", schema, "for path", path)
let title = schema.path.at(-1)
let title = schema?.path?.at(-1)
let singular = title
if (title?.endsWith("s")) {
singular = title.slice(0, title.length - 1)
singular = title?.slice(0, title.length - 1)
}
let article = "a"
if (singular?.match(/^[aeoui]/)) {
@ -25,18 +30,20 @@
const isTagRenderingBlock = path.length === 1 && path[0] === "tagRenderings"
if (isTagRenderingBlock) {
if (isTagRenderingBlock && schema !== undefined) {
schema = { ...schema }
schema.description = undefined
}
const subparts: ConfigMeta[] = state
.getSchemaStartingWith(schema.path)
.filter((part) => part.path.length - 1 === schema.path.length)
.getSchemaStartingWith(schema?.path)
?.filter((part) => part.path.length - 1 === schema?.path?.length)
let messages = state.messagesFor(path)
let datapath = path
const currentValue = state.getStoreFor<(string | QuestionableTagRenderingConfigJson)[]>(datapath)
currentValue.set(Utils.DedupT(currentValue.data))
console.log("Current value is", currentValue.data)
if (currentValue.data === undefined) {
currentValue.setData([])
}
@ -62,68 +69,69 @@
currentValue.ping()
}
</script>
{#if schema !== undefined}
<div class="pl-2">
<h3>{schema.path.at(-1)}</h3>
<div class="pl-2">
<h3>{schema.path.at(-1)}</h3>
{#if subparts.length > 0}
<Markdown src={schema.description} />
{/if}
{#if $currentValue === undefined}
No array defined
{:else if !Array.isArray($currentValue)}
Not an array: {typeof $currentValue}
{JSON.stringify(path)}
{JSON.stringify($currentValue).slice(0, 120)}
{:else if $currentValue?.length === 0}
No values are defined
{#if $messages.length > 0}
{#each $messages as message}
<ShowConversionMessage {message} />
{/each}
{#if subparts.length > 0}
<Markdown src={schema.description} />
{/if}
{:else if subparts.length === 0}
<!-- We need an array of values, so we use the typehint of the _parent_ element as field -->
{#each $currentValue as value, i}
<div class="flex w-full">
<SchemaBasedField {state} {schema} path={fusePath(i)} />
<button
class="h-fit w-fit rounded-full border border-black p-1"
on:click={() => {
{#if $currentValue === undefined}
No array defined
{:else if !Array.isArray($currentValue)}
Not an array: {typeof $currentValue}
{JSON.stringify(path)}
{JSON.stringify($currentValue).slice(0, 120)}
{:else if $currentValue?.length === 0}
No values are defined
{#if $messages.length > 0}
{#each $messages as message}
<ShowConversionMessage {message} />
{/each}
{/if}
{:else if subparts.length === 0}
<!-- We need an array of values, so we use the typehint of the _parent_ element as field -->
{#each $currentValue as value, i}
<div class="flex w-full">
<SchemaBasedField {state} {schema} path={fusePath(i)} />
<button
class="h-fit w-fit rounded-full border border-black p-1"
on:click={() => {
del(i)
}}
>
<TrashIcon class="h-4 w-4" />
</button>
</div>
{/each}
{:else}
<Accordion>
{#each $currentValue as value, i (value)}
<CollapsedTagRenderingPreview
{state}
{isTagRenderingBlock}
{schema}
{currentValue}
{value}
{i}
{singular}
path={fusePath(i)}
/>
>
<TrashIcon class="h-4 w-4" />
</button>
</div>
{/each}
</Accordion>
{/if}
<div class="flex">
<button on:click={() => createItem()}>Add {article} {singular}</button>
{#if path.length === 1 && path[0] === "tagRenderings"}
<button
on:click={() => {
{:else}
<Accordion>
{#each $currentValue as value, i}
<CollapsedTagRenderingPreview
{state}
{isTagRenderingBlock}
{schema}
{currentValue}
{value}
{i}
{singular}
path={fusePath(i)}
/>
{/each}
</Accordion>
{/if}
<div class="flex">
<button on:click={() => createItem()}>Add {article} {singular}</button>
{#if path.length === 1 && path[0] === "tagRenderings"}
<button
on:click={() => {
createItem("images")
}}
>
Add a builtin tagRendering
</button>
{/if}
<slot name="extra-button" />
>
Add a builtin tagRendering
</button>
{/if}
<slot name="extra-button" />
</div>
</div>
</div>
{/if}

View file

@ -48,7 +48,7 @@
import ChevronRight from "@babeard/svelte-heroicons/mini/ChevronRight"
import ChevronLeft from "@babeard/svelte-heroicons/solid/ChevronLeft"
import { Drawer } from "flowbite-svelte"
import { linear, sineIn } from "svelte/easing"
import { linear } from "svelte/easing"
export let state: ThemeViewState
@ -440,7 +440,6 @@
<MenuDrawer onlyLink={true} {state} />
</div>
</DrawerLeft>
<MenuDrawer onlyLink={false} {state} />
{#if $selectedElement !== undefined && $selectedLayer !== undefined && !$selectedLayer.popupInFloatover}
<!-- right modal with the selected element view -->
@ -494,4 +493,6 @@
{/if}
{/if}
<MenuDrawer onlyLink={false} {state} />
</main>