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[]>) { |     constructor(contents: UIEventSource<string | BaseUIElement | BaseUIElement[]>) { | ||||||
|         super(); |         super(); | ||||||
|         this._contents = contents; |         this._contents = contents; | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected InnerConstructElement(): HTMLElement { |     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 => |         const renderingIsShown = tags.map(tags => | ||||||
|             configuration.IsKnown(tags) && |             configuration.IsKnown(tags) && | ||||||
|             (configuration?.condition?.matchesProperties(tags) ?? true)) |             (configuration?.condition?.matchesProperties(tags) ?? true)) | ||||||
|  |          | ||||||
|         super( |         super( | ||||||
|             new Lazy(() => EditableTagRendering.CreateRendering(tags, configuration, units, editMode)), |             new Lazy(() => EditableTagRendering.CreateRendering(tags, configuration, units, editMode)), | ||||||
|             undefined, |             undefined, | ||||||
|  | @ -49,8 +50,8 @@ export default class EditableTagRendering extends Toggle { | ||||||
|             ]).SetClass("flex justify-between w-full") |             ]).SetClass("flex justify-between w-full") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|             const question = new Lazy(() => { |             const question = new Lazy(() => | ||||||
|                 return new TagRenderingQuestion(tags, configuration, |                 new TagRenderingQuestion(tags, configuration, | ||||||
|                     { |                     { | ||||||
|                         units: units, |                         units: units, | ||||||
|                         cancelButton: Translations.t.general.cancel.Clone() |                         cancelButton: Translations.t.general.cancel.Clone() | ||||||
|  | @ -61,10 +62,7 @@ export default class EditableTagRendering extends Toggle { | ||||||
|                         afterSave: () => { |                         afterSave: () => { | ||||||
|                             editMode.setData(false) |                             editMode.setData(false) | ||||||
|                         } |                         } | ||||||
|                     }) |                     })) | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|             }) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|             rendering = new Toggle( |             rendering = new Toggle( | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import BaseUIElement from "../BaseUIElement"; | ||||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | import {VariableUiElement} from "../Base/VariableUIElement"; | ||||||
| import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; | import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; | ||||||
| import {Unit} from "../../Models/Unit"; | import {Unit} from "../../Models/Unit"; | ||||||
|  | import Lazy from "../Base/Lazy"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -27,7 +28,8 @@ export default class QuestionBox extends VariableUiElement { | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 const tagRenderingQuestions = tagRenderings |                 const tagRenderingQuestions = tagRenderings | ||||||
|                     .map((tagRendering, i) => new TagRenderingQuestion(tagsSource, tagRendering, |                     .map((tagRendering, i) => | ||||||
|  |                         new Lazy(() => new TagRenderingQuestion(tagsSource, tagRendering, | ||||||
|                         { |                         { | ||||||
|                             units: units, |                             units: units, | ||||||
|                             afterSave: () => { |                             afterSave: () => { | ||||||
|  | @ -41,7 +43,7 @@ export default class QuestionBox extends VariableUiElement { | ||||||
|                                     skippedQuestions.ping(); |                                     skippedQuestions.ping(); | ||||||
|                                 }) |                                 }) | ||||||
|                         } |                         } | ||||||
|                     )); |                     ))); | ||||||
| 
 | 
 | ||||||
|                 const skippedQuestionsButton = Translations.t.general.skippedQuestions.Clone() |                 const skippedQuestionsButton = Translations.t.general.skippedQuestions.Clone() | ||||||
|                     .onClick(() => { |                     .onClick(() => { | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ import InputElementWrapper from "../Input/InputElementWrapper"; | ||||||
| import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"; | import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"; | ||||||
| import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; | import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; | ||||||
| import {Unit} from "../../Models/Unit"; | import {Unit} from "../../Models/Unit"; | ||||||
|  | import VariableInputElement from "../Input/VariableInputElement"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Shows the question element. |  * Shows the question element. | ||||||
|  | @ -45,7 +46,7 @@ export default class TagRenderingQuestion extends Combine { | ||||||
|     ) { |     ) { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|       /*  const applicableMappings = |         const applicableMappingsSrc = | ||||||
|             UIEventSource.ListStabilized(tags.map(tags => { |             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 ?? []) { |                 for (const mapping of configuration.mappings ?? []) { | ||||||
|  | @ -65,28 +66,9 @@ export default class TagRenderingQuestion extends Combine { | ||||||
|                 return applicableMappings |                 return applicableMappings | ||||||
|             })); |             })); | ||||||
| 
 | 
 | ||||||
|         super( |         applicableMappingsSrc.addCallbackAndRun(appl => console.log("Currently applicable renderings are:", appl.map(m => m.then.txt).join(", "))) | ||||||
|             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<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) { |         if (configuration === undefined) { | ||||||
|             throw "A question is needed for a question visualization" |             throw "A question is needed for a question visualization" | ||||||
|         } |         } | ||||||
|  | @ -96,13 +78,14 @@ export default class TagRenderingQuestion extends Combine { | ||||||
|             .SetClass("question-text"); |             .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 = () => { |         const save = () => { | ||||||
|             console.log("OnSaveTriggered", inputElement) |             console.log("OnSaveTriggered", inputElement) | ||||||
|             const selection = inputElement.GetValue().data; |             const selection = inputElement.GetValue().data; | ||||||
|  | @ -151,16 +134,16 @@ export default class TagRenderingQuestion extends Combine { | ||||||
|                 ) |                 ) | ||||||
|             ).SetClass("block break-all") |             ).SetClass("block break-all") | ||||||
|         } |         } | ||||||
|         return new Combine([ |         super([ | ||||||
|             question, |             question, | ||||||
|             inputElement, |             inputElement, | ||||||
|             options.cancelButton, |             options.cancelButton, | ||||||
|             saveButton, |             saveButton, | ||||||
|             bottomTags] |             bottomTags]) | ||||||
|         ).SetClass("question") |         this.SetClass("question") | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     private static GenerateInputElement(configuration: TagRenderingConfig, |     private static GenerateInputElement(configuration: TagRenderingConfig, | ||||||
|                                         applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[], |                                         applicableMappings: { if: TagsFilter, then: any, ifnot?: TagsFilter }[], | ||||||
|                                         applicableUnit: Unit, |                                         applicableUnit: Unit, | ||||||
|  | @ -171,7 +154,6 @@ export default class TagRenderingQuestion extends Combine { | ||||||
|         const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); |         const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 |         const hasImages = applicableMappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0 | ||||||
|         let inputEls: InputElement<TagsFilter>[]; |         let inputEls: InputElement<TagsFilter>[]; | ||||||
| 
 | 
 | ||||||
|  | @ -226,6 +208,9 @@ export default class TagRenderingQuestion extends Combine { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         if (inputEls.length == 0) { |         if (inputEls.length == 0) { | ||||||
|  |             if(ff === undefined){ | ||||||
|  |                 throw "Error: could not generate a question: freeform and all mappings are undefined" | ||||||
|  |             } | ||||||
|             return ff; |             return ff; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										32
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										32
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -1,8 +1,26 @@ | ||||||
| import Wikidata from "./Logic/Web/Wikidata"; | import FeatureInfoBox from "./UI/Popup/FeatureInfoBox"; | ||||||
| import WikipediaBox from "./UI/WikipediaBox"; | import {UIEventSource} from "./Logic/UIEventSource"; | ||||||
| import Locale from "./UI/i18n/Locale"; | import AllKnownLayers from "./Customizations/AllKnownLayers"; | ||||||
| import LanguagePicker from "./UI/LanguagePicker"; | import State from "./State"; | ||||||
|  | import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; | ||||||
| 
 | 
 | ||||||
| new WikipediaBox("Q177").SetStyle("max-height: 25rem") | State.state = new State(AllKnownLayouts.allKnownLayouts.get("charging_stations")) | ||||||
|     .AttachTo("maindiv") | State.state.changes.pendingChanges.setData([]) | ||||||
| LanguagePicker.CreateLanguagePicker(["en","nl","fr","de"]).AttachTo("extradiv") | 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue