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) | ||||
|             }) | ||||
|         )*/ | ||||
|          | ||||
|         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 | ||||
|                                         } | ||||
|     ) { | ||||
|         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<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}[], | ||||
| 
 | ||||
|     private static GenerateInputElement(configuration: TagRenderingConfig, | ||||
|                                         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>[]; | ||||
| 
 | ||||
|  | @ -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( | ||||
|  |  | |||
							
								
								
									
										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…
	
	Add table
		Add a link
		
	
		Reference in a new issue