This commit is contained in:
Pieter Vander Vennet 2023-12-01 15:31:29 +01:00
parent 8d694e30d2
commit 48216c1a7f
2 changed files with 183 additions and 181 deletions

View file

@ -841,6 +841,10 @@ video {
margin-right: 3rem; margin-right: 3rem;
} }
.mb-4 {
margin-bottom: 1rem;
}
.mt-4 { .mt-4 {
margin-top: 1rem; margin-top: 1rem;
} }
@ -877,10 +881,6 @@ video {
margin-right: 0.25rem; margin-right: 0.25rem;
} }
.mb-4 {
margin-bottom: 1rem;
}
.ml-1 { .ml-1 {
margin-left: 0.25rem; margin-left: 0.25rem;
} }
@ -1784,6 +1784,10 @@ video {
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }
.pt-1 {
padding-top: 0.25rem;
}
.text-center { .text-center {
text-align: center; text-align: center;
} }

View file

@ -1,202 +1,200 @@
<script lang="ts"> <script lang="ts">
import { ImmutableStore, UIEventSource } from "../../../Logic/UIEventSource" import { ImmutableStore, UIEventSource } from "../../../Logic/UIEventSource"
import type { SpecialVisualizationState } from "../../SpecialVisualization" import type { SpecialVisualizationState } from "../../SpecialVisualization"
import Tr from "../../Base/Tr.svelte" import Tr from "../../Base/Tr.svelte"
import type { Feature } from "geojson" import type { Feature } from "geojson"
import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig" import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig"
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig" import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
import { TagsFilter } from "../../../Logic/Tags/TagsFilter" import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
import FreeformInput from "./FreeformInput.svelte" import FreeformInput from "./FreeformInput.svelte"
import Translations from "../../i18n/Translations.js" import Translations from "../../i18n/Translations.js"
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction" import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"
import { createEventDispatcher, onDestroy } from "svelte" import { createEventDispatcher, onDestroy } from "svelte"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import SpecialTranslation from "./SpecialTranslation.svelte" import SpecialTranslation from "./SpecialTranslation.svelte"
import TagHint from "../TagHint.svelte" import TagHint from "../TagHint.svelte"
import LoginToggle from "../../Base/LoginToggle.svelte" import LoginToggle from "../../Base/LoginToggle.svelte"
import SubtleButton from "../../Base/SubtleButton.svelte" import SubtleButton from "../../Base/SubtleButton.svelte"
import Loading from "../../Base/Loading.svelte" import Loading from "../../Base/Loading.svelte"
import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte" import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte"
import { Translation } from "../../i18n/Translation" import { Translation } from "../../i18n/Translation"
import Constants from "../../../Models/Constants" import Constants from "../../../Models/Constants"
import { Unit } from "../../../Models/Unit" import { Unit } from "../../../Models/Unit"
import UserRelatedState from "../../../Logic/State/UserRelatedState" import UserRelatedState from "../../../Logic/State/UserRelatedState"
import { twJoin } from "tailwind-merge" import { twJoin } from "tailwind-merge"
import { TagUtils } from "../../../Logic/Tags/TagUtils" import { TagUtils } from "../../../Logic/Tags/TagUtils"
import Search from "../../../assets/svg/Search.svelte"; import Search from "../../../assets/svg/Search.svelte"
import Login from "../../../assets/svg/Login.svelte"; import Login from "../../../assets/svg/Login.svelte"
export let config: TagRenderingConfig export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>> export let tags: UIEventSource<Record<string, string>>
export let selectedElement: Feature export let selectedElement: Feature
export let state: SpecialVisualizationState export let state: SpecialVisualizationState
export let layer: LayerConfig | undefined export let layer: LayerConfig | undefined
let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined) let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
let unit: Unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key)) let unit: Unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
// Will be bound if a freeform is available // Will be bound if a freeform is available
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key]) let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key])
let selectedMapping: number = undefined let selectedMapping: number = undefined
let checkedMappings: boolean[] let checkedMappings: boolean[]
/** /**
* Prepares and fills the checkedMappings * Prepares and fills the checkedMappings
*/ */
function initialize(tgs: Record<string, string>, confg: TagRenderingConfig) { function initialize(tgs: Record<string, string>, confg: TagRenderingConfig) {
mappings = confg.mappings?.filter((m) => { mappings = confg.mappings?.filter((m) => {
if (typeof m.hideInAnswer === "boolean") { if (typeof m.hideInAnswer === "boolean") {
return !m.hideInAnswer return !m.hideInAnswer
} }
return !m.hideInAnswer.matchesProperties(tgs) return !m.hideInAnswer.matchesProperties(tgs)
}) })
// We received a new config -> reinit // We received a new config -> reinit
unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key)) unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
if ( if (
confg.mappings?.length > 0 && confg.mappings?.length > 0 &&
confg.multiAnswer && confg.multiAnswer &&
(checkedMappings === undefined || (checkedMappings === undefined ||
checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0)) checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0))
) { ) {
const seenFreeforms = [] const seenFreeforms = []
TagUtils.FlattenMultiAnswer() TagUtils.FlattenMultiAnswer()
checkedMappings = [ checkedMappings = [
...confg.mappings.map((mapping) => { ...confg.mappings.map((mapping) => {
const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs) const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs)
if (matches && confg.freeform) { if (matches && confg.freeform) {
const newProps = TagUtils.changeAsProperties(mapping.if.asChange()) const newProps = TagUtils.changeAsProperties(mapping.if.asChange())
seenFreeforms.push(newProps[confg.freeform.key]) seenFreeforms.push(newProps[confg.freeform.key])
} }
return matches return matches
}), }),
] ]
if (tgs !== undefined && confg.freeform) { if (tgs !== undefined && confg.freeform) {
const unseenFreeformValues = tgs[confg.freeform.key]?.split(";") ?? [] const unseenFreeformValues = tgs[confg.freeform.key]?.split(";") ?? []
for (const seenFreeform of seenFreeforms) { for (const seenFreeform of seenFreeforms) {
if (!seenFreeform) { if (!seenFreeform) {
continue continue
} }
const index = unseenFreeformValues.indexOf(seenFreeform) const index = unseenFreeformValues.indexOf(seenFreeform)
if (index < 0) { if (index < 0) {
continue continue
} }
unseenFreeformValues.splice(index, 1) unseenFreeformValues.splice(index, 1)
}
// TODO this has _to much_ values
freeformInput.setData(unseenFreeformValues.join(";"))
checkedMappings.push(unseenFreeformValues.length > 0)
}
} }
// TODO this has _to much_ values if (confg.freeform?.key) {
freeformInput.setData(unseenFreeformValues.join(";")) if (!confg.multiAnswer) {
checkedMappings.push(unseenFreeformValues.length > 0) // Somehow, setting multi-answer freeform values is broken if this is not set
} freeformInput.setData(tgs[confg.freeform.key])
} }
if (confg.freeform?.key) {
if (!confg.multiAnswer) {
// Somehow, setting multi-answer freeform values is broken if this is not set
freeformInput.setData(tgs[confg.freeform.key])
}
} else {
freeformInput.setData(undefined)
}
feedback.setData(undefined)
}
$: {
// Even though 'config' is not declared as a store, Svelte uses it as one to update the component
// We want to (re)-initialize whenever the 'tags' or 'config' change - but not when 'checkedConfig' changes
initialize($tags, config)
}
export let selectedTags: TagsFilter = undefined
let mappings: Mapping[] = config?.mappings
let searchTerm: UIEventSource<string> = new UIEventSource("")
$: {
try {
selectedTags = config?.constructChangeSpecification(
$freeformInput,
selectedMapping,
checkedMappings,
tags.data
)
} catch (e) {
console.error("Could not calculate changeSpecification:", e)
selectedTags = undefined
}
}
let dispatch = createEventDispatcher<{
saved: {
config: TagRenderingConfig
applied: TagsFilter
}
}>()
function onSave() {
if (selectedTags === undefined) {
console.log("SelectedTags is undefined, ignoring 'onSave'-event")
return
}
if (layer === undefined || layer?.source === null) {
/**
* This is a special, priviliged layer.
* We simply apply the tags onto the records
*/
const kv = selectedTags.asChange(tags.data)
for (const { k, v } of kv) {
if (v === undefined || v === "") {
delete tags.data[k]
} else { } else {
tags.data[k] = v freeformInput.setData(undefined)
} }
} feedback.setData(undefined)
tags.ping()
return
} }
dispatch("saved", { config, applied: selectedTags }) $: {
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, { // Even though 'config' is not declared as a store, Svelte uses it as one to update the component
theme: state.layout.id, // We want to (re)-initialize whenever the 'tags' or 'config' change - but not when 'checkedConfig' changes
changeType: "answer", initialize($tags, config)
}) }
freeformInput.setData(undefined) export let selectedTags: TagsFilter = undefined
selectedMapping = undefined
selectedTags = undefined
change let mappings: Mapping[] = config?.mappings
.CreateChangeDescriptions() let searchTerm: UIEventSource<string> = new UIEventSource("")
.then((changes) => state.changes.applyChanges(changes))
.catch(console.error)
}
let featureSwitchIsTesting = state?.featureSwitchIsTesting ?? new ImmutableStore(false) $: {
let featureSwitchIsDebugging = try {
state?.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false) selectedTags = config?.constructChangeSpecification(
let showTags = state?.userRelatedState?.showTags ?? new ImmutableStore(undefined) $freeformInput,
let numberOfCs = state?.osmConnection?.userDetails?.data?.csCount ?? 0 selectedMapping,
let question = config.question checkedMappings,
$: question = config.question tags.data,
if (state?.osmConnection) { )
onDestroy( } catch (e) {
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => { console.error("Could not calculate changeSpecification:", e)
numberOfCs = ud.csCount selectedTags = undefined
}) }
) }
}
let dispatch = createEventDispatcher<{
saved: {
config: TagRenderingConfig
applied: TagsFilter
}
}>()
function onSave() {
if (selectedTags === undefined) {
console.log("SelectedTags is undefined, ignoring 'onSave'-event")
return
}
if (layer === undefined || layer?.source === null) {
/**
* This is a special, priviliged layer.
* We simply apply the tags onto the records
*/
const kv = selectedTags.asChange(tags.data)
for (const { k, v } of kv) {
if (v === undefined || v === "") {
delete tags.data[k]
} else {
tags.data[k] = v
}
}
tags.ping()
return
}
dispatch("saved", { config, applied: selectedTags })
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, {
theme: state.layout.id,
changeType: "answer",
})
freeformInput.setData(undefined)
selectedMapping = undefined
selectedTags = undefined
change
.CreateChangeDescriptions()
.then((changes) => state.changes.applyChanges(changes))
.catch(console.error)
}
let featureSwitchIsTesting = state?.featureSwitchIsTesting ?? new ImmutableStore(false)
let featureSwitchIsDebugging =
state?.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false)
let showTags = state?.userRelatedState?.showTags ?? new ImmutableStore(undefined)
let numberOfCs = state?.osmConnection?.userDetails?.data?.csCount ?? 0
let question = config.question
$: question = config.question
if (state?.osmConnection) {
onDestroy(
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => {
numberOfCs = ud.csCount
}),
)
}
</script> </script>
{#if question !== undefined} {#if question !== undefined}
<div <div
class="interactive border-interactive relative flex flex-col overflow-y-auto p-1 px-2" class="interactive border-interactive relative flex flex-col overflow-y-auto px-2"
style="max-height: 85vh" style="max-height: 75vh"
> >
<div class="sticky top-0" style="z-index: 11"> <div class="sticky top-0 interactive pt-1 flex justify-between" style="z-index: 11">
<div class="interactive sticky top-0 flex justify-between">
<span class="font-bold"> <span class="font-bold">
<SpecialTranslation t={question} {tags} {state} {layer} feature={selectedElement} /> <SpecialTranslation t={question} {tags} {state} {layer} feature={selectedElement} />
</span> </span>
<slot name="upper-right" /> <slot name="upper-right" />
</div>
</div> </div>
{#if config.questionhint} {#if config.questionhint}
@ -213,7 +211,7 @@
{#if config.mappings?.length >= 8} {#if config.mappings?.length >= 8}
<div class="sticky flex w-full"> <div class="sticky flex w-full">
<Search class="h-6 w-6"/> <Search class="h-6 w-6" />
<input type="text" bind:value={$searchTerm} class="w-full" /> <input type="text" bind:value={$searchTerm} class="w-full" />
</div> </div>
{/if} {/if}