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;
}
.mb-4 {
margin-bottom: 1rem;
}
.mt-4 {
margin-top: 1rem;
}
@ -877,10 +881,6 @@ video {
margin-right: 0.25rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.ml-1 {
margin-left: 0.25rem;
}
@ -1784,6 +1784,10 @@ video {
padding-bottom: 0.5rem;
}
.pt-1 {
padding-top: 0.25rem;
}
.text-center {
text-align: center;
}

View file

@ -1,202 +1,200 @@
<script lang="ts">
import { ImmutableStore, UIEventSource } from "../../../Logic/UIEventSource"
import type { SpecialVisualizationState } from "../../SpecialVisualization"
import Tr from "../../Base/Tr.svelte"
import type { Feature } from "geojson"
import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig"
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
import FreeformInput from "./FreeformInput.svelte"
import Translations from "../../i18n/Translations.js"
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"
import { createEventDispatcher, onDestroy } from "svelte"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import SpecialTranslation from "./SpecialTranslation.svelte"
import TagHint from "../TagHint.svelte"
import LoginToggle from "../../Base/LoginToggle.svelte"
import SubtleButton from "../../Base/SubtleButton.svelte"
import Loading from "../../Base/Loading.svelte"
import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte"
import { Translation } from "../../i18n/Translation"
import Constants from "../../../Models/Constants"
import { Unit } from "../../../Models/Unit"
import UserRelatedState from "../../../Logic/State/UserRelatedState"
import { twJoin } from "tailwind-merge"
import { TagUtils } from "../../../Logic/Tags/TagUtils"
import Search from "../../../assets/svg/Search.svelte";
import Login from "../../../assets/svg/Login.svelte";
import { ImmutableStore, UIEventSource } from "../../../Logic/UIEventSource"
import type { SpecialVisualizationState } from "../../SpecialVisualization"
import Tr from "../../Base/Tr.svelte"
import type { Feature } from "geojson"
import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig"
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
import FreeformInput from "./FreeformInput.svelte"
import Translations from "../../i18n/Translations.js"
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"
import { createEventDispatcher, onDestroy } from "svelte"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import SpecialTranslation from "./SpecialTranslation.svelte"
import TagHint from "../TagHint.svelte"
import LoginToggle from "../../Base/LoginToggle.svelte"
import SubtleButton from "../../Base/SubtleButton.svelte"
import Loading from "../../Base/Loading.svelte"
import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte"
import { Translation } from "../../i18n/Translation"
import Constants from "../../../Models/Constants"
import { Unit } from "../../../Models/Unit"
import UserRelatedState from "../../../Logic/State/UserRelatedState"
import { twJoin } from "tailwind-merge"
import { TagUtils } from "../../../Logic/Tags/TagUtils"
import Search from "../../../assets/svg/Search.svelte"
import Login from "../../../assets/svg/Login.svelte"
export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>>
export let selectedElement: Feature
export let state: SpecialVisualizationState
export let layer: LayerConfig | undefined
export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>>
export let selectedElement: Feature
export let state: SpecialVisualizationState
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
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key])
let selectedMapping: number = undefined
let checkedMappings: boolean[]
// Will be bound if a freeform is available
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key])
let selectedMapping: number = undefined
let checkedMappings: boolean[]
/**
* Prepares and fills the checkedMappings
*/
function initialize(tgs: Record<string, string>, confg: TagRenderingConfig) {
mappings = confg.mappings?.filter((m) => {
if (typeof m.hideInAnswer === "boolean") {
return !m.hideInAnswer
}
return !m.hideInAnswer.matchesProperties(tgs)
})
// We received a new config -> reinit
unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
/**
* Prepares and fills the checkedMappings
*/
function initialize(tgs: Record<string, string>, confg: TagRenderingConfig) {
mappings = confg.mappings?.filter((m) => {
if (typeof m.hideInAnswer === "boolean") {
return !m.hideInAnswer
}
return !m.hideInAnswer.matchesProperties(tgs)
})
// We received a new config -> reinit
unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
if (
confg.mappings?.length > 0 &&
confg.multiAnswer &&
(checkedMappings === undefined ||
checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0))
) {
const seenFreeforms = []
TagUtils.FlattenMultiAnswer()
checkedMappings = [
...confg.mappings.map((mapping) => {
const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs)
if (matches && confg.freeform) {
const newProps = TagUtils.changeAsProperties(mapping.if.asChange())
seenFreeforms.push(newProps[confg.freeform.key])
}
return matches
}),
]
if (
confg.mappings?.length > 0 &&
confg.multiAnswer &&
(checkedMappings === undefined ||
checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0))
) {
const seenFreeforms = []
TagUtils.FlattenMultiAnswer()
checkedMappings = [
...confg.mappings.map((mapping) => {
const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs)
if (matches && confg.freeform) {
const newProps = TagUtils.changeAsProperties(mapping.if.asChange())
seenFreeforms.push(newProps[confg.freeform.key])
}
return matches
}),
]
if (tgs !== undefined && confg.freeform) {
const unseenFreeformValues = tgs[confg.freeform.key]?.split(";") ?? []
for (const seenFreeform of seenFreeforms) {
if (!seenFreeform) {
continue
}
const index = unseenFreeformValues.indexOf(seenFreeform)
if (index < 0) {
continue
}
unseenFreeformValues.splice(index, 1)
if (tgs !== undefined && confg.freeform) {
const unseenFreeformValues = tgs[confg.freeform.key]?.split(";") ?? []
for (const seenFreeform of seenFreeforms) {
if (!seenFreeform) {
continue
}
const index = unseenFreeformValues.indexOf(seenFreeform)
if (index < 0) {
continue
}
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
freeformInput.setData(unseenFreeformValues.join(";"))
checkedMappings.push(unseenFreeformValues.length > 0)
}
}
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]
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 {
tags.data[k] = v
freeformInput.setData(undefined)
}
}
tags.ping()
return
feedback.setData(undefined)
}
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
$: {
// 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
change
.CreateChangeDescriptions()
.then((changes) => state.changes.applyChanges(changes))
.catch(console.error)
}
let mappings: Mapping[] = config?.mappings
let searchTerm: UIEventSource<string> = new UIEventSource("")
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
})
)
}
$: {
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 {
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>
{#if question !== undefined}
<div
class="interactive border-interactive relative flex flex-col overflow-y-auto p-1 px-2"
style="max-height: 85vh"
class="interactive border-interactive relative flex flex-col overflow-y-auto px-2"
style="max-height: 75vh"
>
<div class="sticky top-0" style="z-index: 11">
<div class="interactive sticky top-0 flex justify-between">
<div class="sticky top-0 interactive pt-1 flex justify-between" style="z-index: 11">
<span class="font-bold">
<SpecialTranslation t={question} {tags} {state} {layer} feature={selectedElement} />
</span>
<slot name="upper-right" />
</div>
<slot name="upper-right" />
</div>
{#if config.questionhint}
@ -213,7 +211,7 @@
{#if config.mappings?.length >= 8}
<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" />
</div>
{/if}