forked from MapComplete/MapComplete
		
	Fix bug with multiple image uploads, refactoring of TextField
This commit is contained in:
		
							parent
							
								
									8fd4270545
								
							
						
					
					
						commit
						e46ea51d44
					
				
					 13 changed files with 53 additions and 91 deletions
				
			
		|  | @ -20,7 +20,10 @@ export default class GeneralSettingsPanel extends UIElement { | |||
| 
 | ||||
|         const languagesField = | ||||
|             ValidatedTextField.Mapped( | ||||
|                 str => str?.split(";")?.map(str => str.trim().toLowerCase()), | ||||
|                 str => { | ||||
|                     console.log("Language from str", str); | ||||
|                     return str?.split(";")?.map(str => str.trim().toLowerCase()); | ||||
|                 }, | ||||
|                 languages => languages.join(";")); | ||||
|         this.languages = languagesField.GetValue(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,7 +7,6 @@ import {OsmConnection} from "../../Logic/Osm/OsmConnection"; | |||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import {TextField} from "../Input/TextField"; | ||||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import {LayerConfigJson} from "../../Customizations/JSON/LayerConfigJson"; | ||||
| 
 | ||||
| export default class SavePanel extends UIElement { | ||||
|     private json: UIElement; | ||||
|  | @ -35,10 +34,7 @@ export default class SavePanel extends UIElement { | |||
| 
 | ||||
|        const jsonTextField = new TextField({ | ||||
|             placeholder: "JSON Config", | ||||
|             fromString: str => str, | ||||
|             toString: str => str, | ||||
|             value: jsonStr, | ||||
|             startValidated: false, | ||||
|             textArea: true, | ||||
|             textAreaRows: 20 | ||||
|         }); | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ import {UIEventSource} from "../../Logic/UIEventSource"; | |||
| import {InputElement} from "../Input/InputElement"; | ||||
| import SingleSetting from "./SingleSetting"; | ||||
| import SettingsTable from "./SettingsTable"; | ||||
| import {TextField, ValidatedTextField} from "../Input/TextField"; | ||||
| import {TextField} from "../Input/TextField"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import MultiLingualTextFields from "../Input/MultiLingualTextFields"; | ||||
| import {AndOrTagInput} from "../Input/AndOrTagInput"; | ||||
|  | @ -16,6 +16,7 @@ import {UserDetails} from "../../Logic/Osm/OsmConnection"; | |||
| import {State} from "../../State"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import {FromJSON} from "../../Customizations/JSON/FromJSON"; | ||||
| import ValidatedTextField from "../Input/ValidatedTextField"; | ||||
| 
 | ||||
| export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> { | ||||
| 
 | ||||
|  | @ -62,11 +63,11 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs | |||
|         const questionSettings = [ | ||||
| 
 | ||||
| 
 | ||||
|             setting(options?.noLanguage ? TextField.StringInput() : new MultiLingualTextFields(languages) | ||||
|             setting(options?.noLanguage ? new TextField({placeholder:"question"}) : new MultiLingualTextFields(languages) | ||||
|                 , "question", "Question", "If the key or mapping doesn't match, this question is asked"), | ||||
| 
 | ||||
|             "<h3>Freeform key</h3>", | ||||
|             setting(TextField.KeyInput(true), ["freeform", "key"], "Freeform key<br/>", | ||||
|             setting(ValidatedTextField.KeyInput(true), ["freeform", "key"], "Freeform key<br/>", | ||||
|                 "If specified, the rendering will search if this key is present." + | ||||
|                 "If it is, the rendering above will be used to display the element.<br/>" + | ||||
|                 "The rendering will go into question mode if <ul><li>this key is not present</li><li>No single mapping matches</li><li>A question is given</li>"), | ||||
|  | @ -80,7 +81,7 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs | |||
| 
 | ||||
|         const settings: (string | SingleSetting<any>)[] = [ | ||||
|             setting( | ||||
|                 options?.noLanguage ? TextField.StringInput() : | ||||
|                 options?.noLanguage ? new TextField({placeholder:"Rendering"}) : | ||||
|                     new MultiLingualTextFields(languages), "render", "Value to show", " Renders this value. Note that <span class='literal-code'>{key}</span>-parts are substituted by the corresponding values of the element. If neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value."), | ||||
| 
 | ||||
|             questionsNotUnlocked ? `You need at least ${State.userJourney.themeGeneratorFullUnlock} changesets to unlock the 'question'-field and to use your theme to edit OSM data` : "", | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ import Combine from "./Base/Combine"; | |||
| import {State} from "../State"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import {Imgur} from "../Logic/Web/Imgur"; | ||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | ||||
| 
 | ||||
| export class ImageUploadFlow extends UIElement { | ||||
|     private _licensePicker: UIElement; | ||||
|  | @ -69,7 +70,7 @@ export class ImageUploadFlow extends UIElement { | |||
|         if (this._isUploading.data == 1) { | ||||
|             currentState.push(t.uploadingPicture); | ||||
|         } else if (this._isUploading.data > 0) { | ||||
|             currentState.push(t.uploadingMultiple.Subs({count: this._isUploading.data})); | ||||
|             currentState.push(t.uploadingMultiple.Subs({count: ""+this._isUploading.data})); | ||||
|         } | ||||
| 
 | ||||
|         if (this._didFail.data) { | ||||
|  | @ -80,14 +81,15 @@ export class ImageUploadFlow extends UIElement { | |||
|             currentState.push(t.uploadDone) | ||||
|         } | ||||
| 
 | ||||
|         let currentStateHtml = ""; | ||||
|         let currentStateHtml : UIElement = new FixedUiElement(""); | ||||
|         if (currentState.length > 0) { | ||||
|             currentStateHtml = new Combine(currentState).Render(); | ||||
|             currentStateHtml = new Combine(currentState); | ||||
|             if (!this._allDone.data) { | ||||
|                 currentStateHtml = "<span class='alert'>" + | ||||
|                     currentStateHtml + | ||||
|                     "</span>"; | ||||
|                 currentStateHtml.SetClass("alert"); | ||||
|             }else{ | ||||
|                 currentStateHtml.SetClass("thanks"); | ||||
|             } | ||||
|             currentStateHtml.SetStyle("display:block ruby") | ||||
|         } | ||||
| 
 | ||||
|         const extraInfo = new Combine([ | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ export default class InputElementMap<T, X> extends InputElement<X> { | |||
|             }), extraSources, x => { | ||||
|                 return fromX(x); | ||||
|             }); | ||||
|     } | ||||
|     }w | ||||
| 
 | ||||
|     GetValue(): UIEventSource<X> { | ||||
|         return this._value; | ||||
|  |  | |||
|  | @ -1,42 +0,0 @@ | |||
| import {InputElement} from "./InputElement"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| 
 | ||||
| export class InputElementWrapper<T> extends InputElement<T>{ | ||||
|     private pre: UIElement ; | ||||
|     private input: InputElement<T>; | ||||
|     private post: UIElement ; | ||||
|      | ||||
|     IsSelected: UIEventSource<boolean> | ||||
|      | ||||
|      | ||||
|     constructor( | ||||
|         pre: UIElement | string, | ||||
|         input: InputElement<T>, | ||||
|         post: UIElement | string | ||||
|          | ||||
|     ) { | ||||
|         super(undefined); | ||||
|         // this.pre = typeof(pre) === 'string' ?  new FixedUiElement(pre) : pre
 | ||||
|         this.pre = Translations.W(pre) | ||||
|         this.input = input; | ||||
|         // this.post =typeof(post) === 'string' ?  new FixedUiElement(post) : post
 | ||||
|         this.post = Translations.W(post) | ||||
|         this.IsSelected = input.IsSelected; | ||||
|     } | ||||
|      | ||||
|      | ||||
|     GetValue(): UIEventSource<T> { | ||||
|         return this.input.GetValue(); | ||||
|     } | ||||
|      | ||||
|     InnerRender(): string { | ||||
|         return this.pre.Render() + this.input.Render() + this.post.Render(); | ||||
|     } | ||||
| 
 | ||||
|     IsValid(t: T): boolean { | ||||
|         return this.input.IsValid(t); | ||||
|     } | ||||
|      | ||||
| } | ||||
|  | @ -3,9 +3,9 @@ import {UIEventSource} from "../../Logic/UIEventSource"; | |||
| import {TextField} from "./TextField"; | ||||
| 
 | ||||
| export default class MultiLingualTextFields extends InputElement<any> { | ||||
|     private _fields: Map<string, TextField<string>> = new Map<string, TextField<string>>(); | ||||
|     private _value: UIEventSource<any>; | ||||
|     IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
|     private _fields: Map<string, TextField> = new Map<string, TextField>(); | ||||
|     private readonly _value: UIEventSource<any>; | ||||
|     public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
| 
 | ||||
|     constructor(languages: UIEventSource<string[]>, | ||||
|                 textArea: boolean = false, | ||||
|  | @ -14,8 +14,8 @@ export default class MultiLingualTextFields extends InputElement<any> { | |||
|         this._value = value ?? new UIEventSource({}); | ||||
| 
 | ||||
|         this._value.addCallbackAndRun(latestData => { | ||||
|             if(typeof(latestData) === "string"){ | ||||
|                 console.warn("Refusing string for multilingual input",latestData); | ||||
|             if (typeof (latestData) === "string") { | ||||
|                 console.warn("Refusing string for multilingual input", latestData); | ||||
|                 self._value.setData({}); | ||||
|             } | ||||
|         }) | ||||
|  | @ -26,7 +26,7 @@ export default class MultiLingualTextFields extends InputElement<any> { | |||
|             if (languages === undefined) { | ||||
|                 return; | ||||
|             } | ||||
|             const newFields = new Map<string, TextField<string>>(); | ||||
|             const newFields = new Map<string, TextField>(); | ||||
|             for (const language of languages) { | ||||
|                 if (language.length != 2) { | ||||
|                     continue; | ||||
|  | @ -34,7 +34,7 @@ export default class MultiLingualTextFields extends InputElement<any> { | |||
|                  | ||||
|                 let oldField = self._fields.get(language); | ||||
|                 if (oldField === undefined) { | ||||
|                     oldField = TextField.StringInput(textArea); | ||||
|                     oldField = new TextField({textArea: textArea}); | ||||
|                     oldField.GetValue().addCallback(str => { | ||||
|                         self._value.data[language] = str; | ||||
|                         self._value.ping(); | ||||
|  |  | |||
|  | @ -1,10 +1,5 @@ | |||
| import {InputElement} from "./InputElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import TagInput from "./SingleTagInput"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import {MultiInput} from "./MultiInput"; | ||||
| 
 | ||||
| export class MultiTagInput extends MultiInput<string> { | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import {Utils} from "../../Utils"; | |||
| import {UIElement} from "../UIElement"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import {FromJSON} from "../../Customizations/JSON/FromJSON"; | ||||
| import ValidatedTextField from "./ValidatedTextField"; | ||||
| 
 | ||||
| export default class SingleTagInput extends InputElement<string> { | ||||
| 
 | ||||
|  | @ -32,12 +33,10 @@ export default class SingleTagInput extends InputElement<string> { | |||
|             } | ||||
|         )); | ||||
| 
 | ||||
|         this.key = TextField.KeyInput(); | ||||
|         this.key = ValidatedTextField.KeyInput(); | ||||
| 
 | ||||
|         this.value = new TextField<string>({ | ||||
|         this.value = new TextField({ | ||||
|                 placeholder: "value - if blank, matches if key is NOT present", | ||||
|                 fromString: str => str, | ||||
|                 toString: str => str, | ||||
|                 value: new UIEventSource<string>("") | ||||
|             } | ||||
|         ); | ||||
|  |  | |||
|  | @ -51,6 +51,8 @@ export class TextField extends InputElement<string> { | |||
|             // @ts-ignore
 | ||||
|             field.value = t; | ||||
|         }); | ||||
|         this.dumbMode = false; | ||||
|         this.SetClass("deadbeef") | ||||
|     } | ||||
| 
 | ||||
|     GetValue(): UIEventSource<string> { | ||||
|  | @ -60,17 +62,24 @@ export class TextField extends InputElement<string> { | |||
|     InnerRender(): string { | ||||
| 
 | ||||
|         if (this._isArea) { | ||||
|             return `<textarea id="${this.id}" class="form-text-field" rows="${this._textAreaRows}" cols="50" style="max-width: 100%; width: 100%; box-sizing: border-box"></textarea>` | ||||
|             return `<span id="${this.id}"><textarea id="txt-${this.id}" class="form-text-field" rows="${this._textAreaRows}" cols="50" style="max-width: 100%; width: 100%; box-sizing: border-box"></textarea></span>` | ||||
|         } | ||||
| 
 | ||||
|         const placeholder = this._placeholder.InnerRender().replace("'", "'"); | ||||
| 
 | ||||
|         return `<form onSubmit='return false' class='form-text-field'>` + | ||||
|             `<input type='text' placeholder='${placeholder}' id='${this.id}'>` + | ||||
|             `</form>`; | ||||
|         return `<span id="${this.id}"><form onSubmit='return false' class='form-text-field'>` + | ||||
|             `<input type='text' placeholder='${placeholder}' id='txt-${this.id}'>` + | ||||
|             `</form></span>`; | ||||
|     } | ||||
|      | ||||
|     InnerUpdate(field) { | ||||
|     Update() { | ||||
|         console.log("Updating TF") | ||||
|         super.Update(); | ||||
|     } | ||||
| 
 | ||||
|     InnerUpdate() { | ||||
|         console.log("Inner Updating TF") | ||||
|         const field = document.getElementById("txt-" + this.id); | ||||
|         const self = this; | ||||
|         field.oninput = () => { | ||||
|             // @ts-ignore
 | ||||
|  |  | |||
|  | @ -186,7 +186,6 @@ export default class ValidatedTextField { | |||
|         isValid?: ((string: string) => boolean) | ||||
|     }): InputElement<T> { | ||||
|         const textField = new TextField(options); | ||||
| 
 | ||||
|         return new InputElementMap( | ||||
|             textField, (a, b) => a === b, | ||||
|             fromString, toString | ||||
|  |  | |||
|  | @ -157,7 +157,7 @@ export class ShareScreen extends UIElement { | |||
|         }, optionParts); | ||||
| 
 | ||||
| 
 | ||||
|         this.iframe = url.map(url => `<iframe src="${url}" width="100%" height="100%" title="${layout.title.InnerRender()} with MapComplete"></iframe>`); | ||||
|         this.iframe = url.map(url => `<iframe src="${url}" width="100%" height="100%" title="${layout.title?.InnerRender()??""} with MapComplete"></iframe>`); | ||||
|          | ||||
|         this._iframeCode = new VariableUiElement( | ||||
|             url.map((url) => { | ||||
|  |  | |||
|  | @ -34,13 +34,13 @@ export default class Translations { | |||
|             }), | ||||
| 
 | ||||
|             uploadingMultiple: new T({ | ||||
|                 en: 'Uploading {count} of your picture...', | ||||
|                 nl: 'Bezig met {count} foto\'s te uploaden...', | ||||
|                 ca: 'Pujant {count} de la teva imatge...', | ||||
|                 es: 'Subiendo {count} de tus fotos...', | ||||
|                 fr: 'Mettre votre {count} photos en ligne', | ||||
|                 gl: 'Subindo {count} das túas imaxes...', | ||||
|                 de: '{count} Ihrer Bilder hochgeladen...' | ||||
|                 en: "Uploading {count} of your picture...", | ||||
|                 nl: "Bezig met {count} foto's te uploaden...", | ||||
|                 ca: "Pujant {count} de la teva imatge...", | ||||
|                 es: "Subiendo {count} de tus fotos...", | ||||
|                 fr: "Mettre votre {count} photos en ligne", | ||||
|                 gl: "Subindo {count} das túas imaxes...", | ||||
|                 de: "{count} Ihrer Bilder hochgeladen..." | ||||
|             }), | ||||
| 
 | ||||
|             pleaseLogin: new T({ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue