Feature: allow to remove an answer (in most cases), fix #2008

This commit is contained in:
Pieter Vander Vennet 2024-09-04 02:15:10 +02:00
parent 7565f13e39
commit 55c89085a3
8 changed files with 181 additions and 86 deletions

View file

@ -12,11 +12,15 @@
</script>
{#if !onlyLink}
<Popup {shown} {bodyPadding} {fullscreen}/>
<Popup {shown} {bodyPadding} {fullscreen}>
<slot name="header" slot="header" />
<slot />
<slot name="footer" slot="footer" />
</Popup>
{:else}
<button class="as-link sidebar-button" on:click={() => shown.setData(true)}>
<slot name="link">
<slot name="header" />
<slot name="header" />
</slot>
</button>
{/if}
@ -27,9 +31,9 @@
align-items: center;
}
:global(.page-header svg) {
width: 2rem;
height: 2rem;
margin-right: 0.75rem;
}
:global(.page-header svg) {
width: 2rem;
height: 2rem;
margin-right: 0.75rem;
}
</style>

View file

@ -34,7 +34,7 @@
<Modal open={_shown} on:close={() => shown.set(false)} outsideclose
size="xl"
dismissable={false}
dismissable={false}P
{defaultClass} {bodyClass} {dialogClass} {headerClass}
color="none">
<h1 slot="header" class="page-header w-full">

View file

@ -33,6 +33,9 @@
import Markdown from "../../Base/Markdown.svelte"
import { Utils } from "../../../Utils"
import type { UploadableTag } from "../../../Logic/Tags/TagTypes"
import { Modal } from "flowbite-svelte"
import Popup from "../../Base/Popup.svelte"
import If from "../../Base/If.svelte"
export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>>
@ -48,6 +51,8 @@
let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
let unit: Unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
let isKnown = tags.mapD(tags => config.GetRenderValue(tags) !== undefined)
let matchesEmpty = config.GetRenderValue({}) !== undefined
// Will be bound if a freeform is available
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key])
@ -59,6 +64,12 @@
*/
let checkedMappings: boolean[]
/**
* IF set: we can remove the current answer by deleting all those keys
*/
let settableKeys = config.removeToSetUnknown()
let unknownModal = new UIEventSource(false)
let searchTerm: UIEventSource<string> = new UIEventSource("")
let dispatch = createEventDispatcher<{
@ -80,7 +91,7 @@
return !m.hideInAnswer.matchesProperties(tgs)
})
selectedMapping = mappings?.findIndex(
(mapping) => mapping.if.matchesProperties(tgs) || mapping.alsoShowIf?.matchesProperties(tgs)
(mapping) => mapping.if.matchesProperties(tgs) || mapping.alsoShowIf?.matchesProperties(tgs),
)
if (selectedMapping < 0) {
selectedMapping = undefined
@ -142,7 +153,6 @@
let usedKeys: string[] = Utils.Dedup(config.usedTags().flatMap((t) => t.usedKeys()))
let keysToDeleteOnUnknown = config.settableKeys()
/**
* The 'minimalTags' is a subset of the tags of the feature, only containing the values relevant for this object.
* The main goal is to be stable and only 'ping' when an actual change is relevant
@ -189,7 +199,7 @@
if (freeformValue?.length > 0) {
selectedMapping = config.mappings.length
}
})
}),
)
$: {
@ -207,7 +217,7 @@
$freeformInput,
selectedMapping,
checkedMappings,
tags.data
tags.data,
)
if (featureSwitchIsDebugging?.data) {
console.log(
@ -219,7 +229,7 @@
currentTags: tags.data,
},
" --> ",
selectedTags
selectedTags,
)
}
} catch (e) {
@ -241,7 +251,7 @@
selectedTags = new And([...selectedTags.and, ...extraTagsArray])
} else {
console.error(
"selectedTags is not of type Tag or And, it is a " + JSON.stringify(selectedTags)
"selectedTags is not of type Tag or And, it is a " + JSON.stringify(selectedTags),
)
}
}
@ -310,9 +320,24 @@
onDestroy(
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => {
numberOfCs = ud.csCount
})
}),
)
}
function clearAnswer() {
const tagsToSet = settableKeys.map(k => new Tag(k, ""))
const change = new ChangeTagAction(tags.data.id, new And(tagsToSet), tags.data, {
theme: tags.data["_orig_theme"] ?? state.layout.id,
changeType: "answer",
})
freeformInput.set(undefined)
selectedMapping = undefined
selectedTags = undefined
change
.CreateChangeDescriptions()
.then((changes) => state.changes.applyChanges(changes))
.catch(console.error)
}
</script>
{#if question !== undefined}
@ -491,36 +516,74 @@
<Tr t={$feedback} />
</div>
{/if}
<!--{#if keysToDeleteOnUnknown?.some(k => !! $tags[k])}
Mark as unknown (delete {keysToDeleteOnUnknown?.filter(k => !! $tags[k]).join(";")})
{/if}-->
<Popup shown={unknownModal}>
<h2 slot="header">
<Tr t={Translations.t.unknown.title} />
</h2>
<Tr t={Translations.t.unknown.explanation} />
<If condition={state.userRelatedState.showTags.map(v => v === "yes" || v === "full" || v === "always")}>
<div class="subtle">
<Tr t={Translations.t.unknown.removedKeys}/>
{#each settableKeys as key}
<code>
<del>
{key}
</del>
</code>
{/each}
</div>
</If>
<div class="flex justify-end w-full" slot="footer">
<button on:click={() => unknownModal.set(false)}>
<Tr t={Translations.t.unknown.keep} />
</button>
<button class="primary" on:click={() => {unknownModal.set(false); clearAnswer()}}>
<Tr t={Translations.t.unknown.clear} />
</button>
</div>
</Popup>
<div
class="sticky bottom-0 flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap"
class="sticky bottom-0 flex justify-between flex-wrap"
style="z-index: 11"
>
<!-- TagRenderingQuestion-buttons -->
<slot name="cancel" />
<slot name="save-button" {selectedTags}>
{#if config.freeform?.key && !checkedMappings?.some((m) => m) && !$freeformInput && !$freeformInputUnvalidated && $tags[config.freeform.key]}
<button
class="primary flex"
on:click|stopPropagation|preventDefault={() => onSave()}
>
<TrashIcon class="h-6 w-6 text-red-500" />
<Tr t={Translations.t.general.eraseValue} />
</button>
{:else}
<button
on:click={() => onSave()}
class={twJoin(
{#if settableKeys && $isKnown && !matchesEmpty }
<button class="as-link small text-sm" on:click={() => unknownModal.set(true)}>
<Tr t={Translations.t.unknown.markUnknown} />
</button>
{/if}
<div class="flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap self-end flex-grow">
<!-- TagRenderingQuestion-buttons -->
<slot name="cancel" />
<slot name="save-button" {selectedTags}>
{#if config.freeform?.key && !checkedMappings?.some((m) => m) && !$freeformInput && !$freeformInputUnvalidated && $tags[config.freeform.key]}
<button
class="primary flex"
on:click|stopPropagation|preventDefault={() => onSave()}
>
<TrashIcon class="h-6 w-6 text-red-500" />
<Tr t={Translations.t.general.eraseValue} />
</button>
{:else}
<button
on:click={() => onSave()}
class={twJoin(
selectedTags === undefined ? "disabled" : "button-shadow",
"primary"
)}
>
<Tr t={Translations.t.general.save} />
</button>
{/if}
</slot>
>
<Tr t={Translations.t.general.save} />
</button>
{/if}
</slot>
</div>
</div>
{#if UserRelatedState.SHOW_TAGS_VALUES.indexOf($showTags) >= 0 || ($showTags === "" && numberOfCs >= Constants.userJourney.tagsVisibleAt) || $featureSwitchIsTesting || $featureSwitchIsDebugging}
<span class="flex flex-wrap justify-between">