From abae813606e6262df96d7f0cfdaa2d5b9e2bdbbc Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sun, 3 Oct 2021 21:44:43 +0200 Subject: [PATCH] Fix dynamism in questions with new VariableInputElement --- UI/Base/VariableUIElement.ts | 1 - UI/Input/VariableInputElement.ts | 35 +++++++++++++++ UI/Popup/EditableTagRendering.ts | 10 ++--- UI/Popup/QuestionBox.ts | 6 ++- UI/Popup/TagRenderingQuestion.ts | 75 +++++++++++++------------------- test.ts | 32 +++++++++++--- 6 files changed, 98 insertions(+), 61 deletions(-) create mode 100644 UI/Input/VariableInputElement.ts diff --git a/UI/Base/VariableUIElement.ts b/UI/Base/VariableUIElement.ts index 7c895b9cb..6ee720627 100644 --- a/UI/Base/VariableUIElement.ts +++ b/UI/Base/VariableUIElement.ts @@ -7,7 +7,6 @@ export class VariableUiElement extends BaseUIElement { constructor(contents: UIEventSource) { super(); this._contents = contents; - } protected InnerConstructElement(): HTMLElement { diff --git a/UI/Input/VariableInputElement.ts b/UI/Input/VariableInputElement.ts new file mode 100644 index 000000000..1918dfe9a --- /dev/null +++ b/UI/Input/VariableInputElement.ts @@ -0,0 +1,35 @@ +import {InputElement} from "./InputElement"; +import {UIEventSource} from "../../Logic/UIEventSource"; +import BaseUIElement from "../BaseUIElement"; +import {VariableUiElement} from "../Base/VariableUIElement"; + +export default class VariableInputElement extends InputElement { + + private readonly value: UIEventSource; + private readonly element: BaseUIElement + public readonly IsSelected: UIEventSource; + private readonly upstream: UIEventSource>; + + constructor(upstream: UIEventSource>) { + + super() + this.upstream = upstream; + this.value = upstream.bind(v => v.GetValue()) + this.element = new VariableUiElement(upstream) + this.IsSelected = upstream.bind(v => v.IsSelected) + } + + GetValue(): UIEventSource { + return this.value; + } + + protected InnerConstructElement(): HTMLElement { + return this.element.ConstructElement(); + } + + + IsValid(t: T): boolean { + return this.upstream.data.IsValid(t); + } + +} \ No newline at end of file diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index 585a75fee..2be62b01b 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -25,6 +25,7 @@ export default class EditableTagRendering extends Toggle { const renderingIsShown = tags.map(tags => configuration.IsKnown(tags) && (configuration?.condition?.matchesProperties(tags) ?? true)) + super( new Lazy(() => EditableTagRendering.CreateRendering(tags, configuration, units, editMode)), undefined, @@ -49,8 +50,8 @@ export default class EditableTagRendering extends Toggle { ]).SetClass("flex justify-between w-full") - const question = new Lazy(() => { - return new TagRenderingQuestion(tags, configuration, + const question = new Lazy(() => + new TagRenderingQuestion(tags, configuration, { units: units, cancelButton: Translations.t.general.cancel.Clone() @@ -61,10 +62,7 @@ export default class EditableTagRendering extends Toggle { afterSave: () => { editMode.setData(false) } - }) - - - }) + })) rendering = new Toggle( diff --git a/UI/Popup/QuestionBox.ts b/UI/Popup/QuestionBox.ts index 2b7fdfd7a..1192ff8ee 100644 --- a/UI/Popup/QuestionBox.ts +++ b/UI/Popup/QuestionBox.ts @@ -7,6 +7,7 @@ import BaseUIElement from "../BaseUIElement"; import {VariableUiElement} from "../Base/VariableUIElement"; import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; import {Unit} from "../../Models/Unit"; +import Lazy from "../Base/Lazy"; /** @@ -27,7 +28,8 @@ export default class QuestionBox extends VariableUiElement { } const tagRenderingQuestions = tagRenderings - .map((tagRendering, i) => new TagRenderingQuestion(tagsSource, tagRendering, + .map((tagRendering, i) => + new Lazy(() => new TagRenderingQuestion(tagsSource, tagRendering, { units: units, afterSave: () => { @@ -41,7 +43,7 @@ export default class QuestionBox extends VariableUiElement { skippedQuestions.ping(); }) } - )); + ))); const skippedQuestionsButton = Translations.t.general.skippedQuestions.Clone() .onClick(() => { diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index e95d3abfe..40a68609d 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -26,6 +26,7 @@ import InputElementWrapper from "../Input/InputElementWrapper"; import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"; import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; import {Unit} from "../../Models/Unit"; +import VariableInputElement from "../Input/VariableInputElement"; /** * Shows the question element. @@ -45,48 +46,29 @@ export default class TagRenderingQuestion extends Combine { ) { - /* const applicableMappings = + const applicableMappingsSrc = UIEventSource.ListStabilized(tags.map(tags => { - const applicableMappings : {if: TagsFilter, then: any, ifnot?: TagsFilter}[] = [] + const applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[] = [] for (const mapping of configuration.mappings ?? []) { if (mapping.hideInAnswer === true) { continue } if (mapping.hideInAnswer === false || mapping.hideInAnswer === undefined) { - applicableMappings.push(mapping) + applicableMappings.push(mapping) continue } - const condition = mapping.hideInAnswer; + const condition = mapping.hideInAnswer; const isShown = !condition.matchesProperties(tags) - if(isShown){ + if (isShown) { applicableMappings.push(mapping) } } return applicableMappings })); - super( - applicableMappings.map(applicableMappings => { - return TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options) - }) - )*/ - - const applicableMappings = Utils.NoNull((configuration.mappings??[]).filter(mapping => mapping.hideInAnswer !== undefined)) - - super([TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options)]) - } - - private static GenerateFullQuestion(tags: UIEventSource, - applicableMappings: {if: TagsFilter, then: any, ifnot?: TagsFilter}[], - configuration: TagRenderingConfig, - options?: { - units?: Unit[], - afterSave?: () => void, - cancelButton?: BaseUIElement, - saveButtonConstr?: (src: UIEventSource) => BaseUIElement, - bottomText?: (src: UIEventSource) => BaseUIElement - } - ) { + applicableMappingsSrc.addCallbackAndRun(appl => console.log("Currently applicable renderings are:", appl.map(m => m.then.txt).join(", "))) + + if (configuration === undefined) { throw "A question is needed for a question visualization" } @@ -96,13 +78,14 @@ export default class TagRenderingQuestion extends Combine { .SetClass("question-text"); - const inputElement: InputElement = TagRenderingQuestion.GenerateInputElement(configuration, applicableMappings, applicableUnit, tags) + const inputElement: InputElement = + new VariableInputElement(applicableMappingsSrc.map(applicableMappings => + TagRenderingQuestion.GenerateInputElement(configuration, applicableMappings, applicableUnit, tags) + )) + + + // inputElement.GetValue().addCallbackAndRun(s => console.trace(configuration.question.txt, "Current selection is ", s)) - if (inputElement === undefined) { - console.error("MultiAnswer failed - probably not a single option was possible", configuration) - throw "MultiAnswer failed - probably not a single option was possible" - } - inputElement.GetValue().addCallbackAndRun(s => console.trace("Current selection is ", s)) const save = () => { console.log("OnSaveTriggered", inputElement) const selection = inputElement.GetValue().data; @@ -112,7 +95,7 @@ export default class TagRenderingQuestion extends Combine { .applyAction(new ChangeTagAction( tags.data.id, selection, tags.data )).then(_ => { - console.log("Tagchanges applied") + console.log("Tagchanges applied") }) if (options.afterSave) { options.afterSave(); @@ -151,27 +134,26 @@ export default class TagRenderingQuestion extends Combine { ) ).SetClass("block break-all") } - return new Combine([ + super([ question, inputElement, options.cancelButton, saveButton, - bottomTags] - ).SetClass("question") - + bottomTags]) + this.SetClass("question") } - private static GenerateInputElement(configuration: TagRenderingConfig, - applicableMappings: {if: TagsFilter, then: any, ifnot?: TagsFilter}[], + + private static GenerateInputElement(configuration: TagRenderingConfig, + applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[], applicableUnit: Unit, tagsSource: UIEventSource) - : InputElement { + : InputElement { // FreeForm input will be undefined if not present; will already contain a special input element if applicable const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); - const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 let inputEls: InputElement[]; @@ -189,7 +171,7 @@ export default class TagRenderingQuestion extends Combine { // The multianswer will do the ifnot configuration themself return [] } - + const negativeMappings = [] for (let i = 0; i < applicableMappings.length; i++) { @@ -203,7 +185,7 @@ export default class TagRenderingQuestion extends Combine { } - + if (applicableMappings.length < 8 || configuration.multiAnswer || hasImages || ifNotsPresent) { inputEls = (applicableMappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i))); inputEls = Utils.NoNull(inputEls); @@ -226,6 +208,9 @@ export default class TagRenderingQuestion extends Combine { if (inputEls.length == 0) { + if(ff === undefined){ + throw "Error: could not generate a question: freeform and all mappings are undefined" + } return ff; } @@ -358,7 +343,7 @@ export default class TagRenderingQuestion extends Combine { let tagging: TagsFilter = mapping.if; if (ifNot !== undefined) { - tagging = new And([mapping.if, ...ifNot]) + tagging = new And([mapping.if, ...ifNot]) } return new FixedInputElement( diff --git a/test.ts b/test.ts index 0c746b6eb..e6f2a9966 100644 --- a/test.ts +++ b/test.ts @@ -1,8 +1,26 @@ -import Wikidata from "./Logic/Web/Wikidata"; -import WikipediaBox from "./UI/WikipediaBox"; -import Locale from "./UI/i18n/Locale"; -import LanguagePicker from "./UI/LanguagePicker"; +import FeatureInfoBox from "./UI/Popup/FeatureInfoBox"; +import {UIEventSource} from "./Logic/UIEventSource"; +import AllKnownLayers from "./Customizations/AllKnownLayers"; +import State from "./State"; +import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; -new WikipediaBox("Q177").SetStyle("max-height: 25rem") - .AttachTo("maindiv") -LanguagePicker.CreateLanguagePicker(["en","nl","fr","de"]).AttachTo("extradiv") \ No newline at end of file +State.state = new State(AllKnownLayouts.allKnownLayouts.get("charging_stations")) +State.state.changes.pendingChanges.setData([]) +const geojson = { + type: "Feature", + geometry: { + type: "Point", + coordinates: [51.0, 4] + }, + properties: + { + id: "node/42", + amenity: "charging_station", + } +} +State.state.allElements.addOrGetElement(geojson) +const tags = State.state.allElements.getEventSourceById("node/42") +new FeatureInfoBox( + tags, + AllKnownLayers.sharedLayers.get("charging_station") +).AttachTo("maindiv") \ No newline at end of file