parent
d1aa751e18
commit
382f96596e
3 changed files with 132 additions and 78 deletions
|
@ -132,6 +132,9 @@ export class TagUtils {
|
|||
|
||||
/**
|
||||
* Given multiple tagsfilters which can be used as answer, will take the tags with the same keys together as set.
|
||||
*
|
||||
* @see MatchesMultiAnswer to do the reverse
|
||||
*
|
||||
* E.g:
|
||||
*
|
||||
* const tag = TagUtils.ParseUploadableTag({"and": [
|
||||
|
|
|
@ -243,7 +243,10 @@ export default class TagRenderingConfig {
|
|||
if (txt === "") {
|
||||
throw context + " Rendering for language " + ln + " is empty"
|
||||
}
|
||||
if (txt.indexOf("{" + this.freeform.key + "}") >= 0 || txt.indexOf("&LBRACE" + this.freeform.key + "&RBRACE") ) {
|
||||
if (
|
||||
txt.indexOf("{" + this.freeform.key + "}") >= 0 ||
|
||||
txt.indexOf("&LBRACE" + this.freeform.key + "&RBRACE")
|
||||
) {
|
||||
continue
|
||||
}
|
||||
if (txt.indexOf("{" + this.freeform.key + ":") >= 0) {
|
||||
|
@ -645,6 +648,16 @@ export default class TagRenderingConfig {
|
|||
/**
|
||||
* Given a value for the freeform key and an overview of the selected mappings, construct the correct tagsFilter to apply
|
||||
*
|
||||
* const config = new TagRenderingConfig({"id":"bookcase-booktypes","render":{"en":"This place mostly serves {books}" },
|
||||
* "question":{"en":"What kind of books can be found in this public bookcase?"},
|
||||
* "freeform":{"key":"books","addExtraTags":["fixme=Freeform tag `books` used, to be doublechecked"],
|
||||
* "inline":true},
|
||||
* "multiAnswer":true,
|
||||
* "mappings":[{"if":"books=children","then":"Mostly children books"},
|
||||
* {"if":"books=adults","then": "Mostly books for adults"}]}
|
||||
* , "testcase")
|
||||
* config.constructChangeSpecification(undefined, undefined, [false, true, false], {amenity: "public_bookcase"}) // => new And([new Tag("books","adult")])
|
||||
*
|
||||
* @param freeformValue The freeform value which will be applied as 'freeform.key'. Ignored if 'freeform.key' is not set
|
||||
*
|
||||
* @param singleSelectedMapping (Only used if multiAnswer == false): the single mapping to apply. Use (mappings.length) for the freeform
|
||||
|
|
|
@ -1,76 +1,114 @@
|
|||
<script lang="ts">
|
||||
import { Store, 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 { 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";
|
||||
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
export let selectedElement: Feature
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig
|
||||
export let config: TagRenderingConfig;
|
||||
export let tags: UIEventSource<Record<string, string>>;
|
||||
export let selectedElement: Feature;
|
||||
export let state: SpecialVisualizationState;
|
||||
export let layer: LayerConfig;
|
||||
|
||||
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[]
|
||||
$: {
|
||||
let tgs = $tags
|
||||
mappings = config.mappings?.filter((m) => {
|
||||
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;
|
||||
}
|
||||
return !m.hideInAnswer.matchesProperties(tgs)
|
||||
})
|
||||
return !m.hideInAnswer.matchesProperties(tgs);
|
||||
});
|
||||
// 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(confg.freeform?.key));
|
||||
|
||||
if (
|
||||
config.mappings?.length > 0 &&
|
||||
(checkedMappings === undefined || checkedMappings?.length + 1 < config.mappings.length)
|
||||
confg.mappings?.length > 0 &&
|
||||
(checkedMappings === undefined || checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0))
|
||||
) {
|
||||
const seenFreeforms = [];
|
||||
TagUtils.FlattenMultiAnswer()
|
||||
checkedMappings = [
|
||||
...config.mappings.map((_) => false),
|
||||
false /*One element extra in case a freeform value is added*/,
|
||||
]
|
||||
...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)
|
||||
}
|
||||
// TODO this has _to much_ values
|
||||
freeformInput.setData(unseenFreeformValues.join(";"))
|
||||
checkedMappings.push(unseenFreeformValues.length > 0)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (config.freeform?.key) {
|
||||
if (!config.multiAnswer) {
|
||||
// Somehow, setting multianswer freeform values is broken if this is not set
|
||||
freeformInput.setData(tgs[config.freeform.key])
|
||||
console.log("Inited 'checkMappings' to", checkedMappings);
|
||||
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)
|
||||
freeformInput.setData(undefined);
|
||||
}
|
||||
feedback.setData(undefined)
|
||||
feedback.setData(undefined);
|
||||
}
|
||||
export let selectedTags: TagsFilter = undefined
|
||||
|
||||
let mappings: Mapping[] = config?.mappings
|
||||
let searchTerm: UIEventSource<string> = new UIEventSource("")
|
||||
$: {
|
||||
// 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 {
|
||||
|
@ -79,10 +117,10 @@
|
|||
selectedMapping,
|
||||
checkedMappings,
|
||||
tags.data
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Could not calculate changeSpecification:", e)
|
||||
selectedTags = undefined
|
||||
console.error("Could not calculate changeSpecification:", e);
|
||||
selectedTags = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,53 +129,53 @@
|
|||
config: TagRenderingConfig
|
||||
applied: TagsFilter
|
||||
}
|
||||
}>()
|
||||
}>();
|
||||
|
||||
function onSave() {
|
||||
if (selectedTags === undefined) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (layer.source === null) {
|
||||
/**
|
||||
* This is a special, priviliged layer.
|
||||
* We simply apply the tags onto the records
|
||||
*/
|
||||
const kv = selectedTags.asChange(tags.data)
|
||||
const kv = selectedTags.asChange(tags.data);
|
||||
for (const { k, v } of kv) {
|
||||
if (v === undefined) {
|
||||
delete tags.data[k]
|
||||
delete tags.data[k];
|
||||
} else {
|
||||
tags.data[k] = v
|
||||
tags.data[k] = v;
|
||||
}
|
||||
}
|
||||
tags.ping()
|
||||
return
|
||||
tags.ping();
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch("saved", { config, applied: selectedTags })
|
||||
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
|
||||
changeType: "answer"
|
||||
});
|
||||
freeformInput.setData(undefined);
|
||||
selectedMapping = undefined;
|
||||
selectedTags = undefined;
|
||||
|
||||
change
|
||||
.CreateChangeDescriptions()
|
||||
.then((changes) => state.changes.applyChanges(changes))
|
||||
.catch(console.error)
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
let featureSwitchIsTesting = state.featureSwitchIsTesting
|
||||
let featureSwitchIsDebugging = state.featureSwitches.featureSwitchIsDebugging
|
||||
let showTags = state.userRelatedState.showTags
|
||||
let numberOfCs = state.osmConnection.userDetails.data.csCount
|
||||
let featureSwitchIsTesting = state.featureSwitchIsTesting;
|
||||
let featureSwitchIsDebugging = state.featureSwitches.featureSwitchIsDebugging;
|
||||
let showTags = state.userRelatedState.showTags;
|
||||
let numberOfCs = state.osmConnection.userDetails.data.csCount;
|
||||
onDestroy(
|
||||
state.osmConnection.userDetails.addCallbackAndRun((ud) => {
|
||||
numberOfCs = ud.csCount
|
||||
numberOfCs = ud.csCount;
|
||||
})
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
{#if config.question !== undefined}
|
||||
|
|
Loading…
Reference in a new issue