forked from MapComplete/MapComplete
Fix dynamism in questions with new VariableInputElement
This commit is contained in:
parent
b6b20ed3ca
commit
abae813606
6 changed files with 98 additions and 61 deletions
|
@ -7,7 +7,6 @@ export class VariableUiElement extends BaseUIElement {
|
|||
constructor(contents: UIEventSource<string | BaseUIElement | BaseUIElement[]>) {
|
||||
super();
|
||||
this._contents = contents;
|
||||
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
|
|
35
UI/Input/VariableInputElement.ts
Normal file
35
UI/Input/VariableInputElement.ts
Normal file
|
@ -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<T> extends InputElement<T> {
|
||||
|
||||
private readonly value: UIEventSource<T>;
|
||||
private readonly element: BaseUIElement
|
||||
public readonly IsSelected: UIEventSource<boolean>;
|
||||
private readonly upstream: UIEventSource<InputElement<T>>;
|
||||
|
||||
constructor(upstream: UIEventSource<InputElement<T>>) {
|
||||
|
||||
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<T> {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
return this.element.ConstructElement();
|
||||
}
|
||||
|
||||
|
||||
IsValid(t: T): boolean {
|
||||
return this.upstream.data.IsValid(t);
|
||||
}
|
||||
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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 = <TagsFilter> mapping.hideInAnswer;
|
||||
const condition = <TagsFilter>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)
|
||||
})
|
||||
)*/
|
||||
applicableMappingsSrc.addCallbackAndRun(appl => console.log("Currently applicable renderings are:", appl.map(m => m.then.txt).join(", ")))
|
||||
|
||||
const applicableMappings = Utils.NoNull((configuration.mappings??[]).filter(mapping => mapping.hideInAnswer !== undefined))
|
||||
|
||||
super([TagRenderingQuestion.GenerateFullQuestion(tags, applicableMappings, configuration, options)])
|
||||
}
|
||||
|
||||
private static GenerateFullQuestion(tags: UIEventSource<any>,
|
||||
applicableMappings: {if: TagsFilter, then: any, ifnot?: TagsFilter}[],
|
||||
configuration: TagRenderingConfig,
|
||||
options?: {
|
||||
units?: Unit[],
|
||||
afterSave?: () => void,
|
||||
cancelButton?: BaseUIElement,
|
||||
saveButtonConstr?: (src: UIEventSource<TagsFilter>) => BaseUIElement,
|
||||
bottomText?: (src: UIEventSource<TagsFilter>) => BaseUIElement
|
||||
}
|
||||
) {
|
||||
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<TagsFilter> = TagRenderingQuestion.GenerateInputElement(configuration, applicableMappings, applicableUnit, tags)
|
||||
const inputElement: InputElement<TagsFilter> =
|
||||
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}[],
|
||||
applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[],
|
||||
applicableUnit: Unit,
|
||||
tagsSource: UIEventSource<any>)
|
||||
: InputElement<TagsFilter> {
|
||||
: InputElement<TagsFilter> {
|
||||
|
||||
// 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<TagsFilter>[];
|
||||
|
||||
|
@ -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(
|
||||
|
|
32
test.ts
32
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")
|
||||
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")
|
Loading…
Reference in a new issue