Fix bug with multiple image uploads, refactoring of TextField

This commit is contained in:
Pieter Vander Vennet 2020-09-25 17:57:01 +02:00
parent 8fd4270545
commit e46ea51d44
13 changed files with 53 additions and 91 deletions

View file

@ -20,7 +20,10 @@ export default class GeneralSettingsPanel extends UIElement {
const languagesField = const languagesField =
ValidatedTextField.Mapped( 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(";")); languages => languages.join(";"));
this.languages = languagesField.GetValue(); this.languages = languagesField.GetValue();

View file

@ -7,7 +7,6 @@ import {OsmConnection} from "../../Logic/Osm/OsmConnection";
import {FixedUiElement} from "../Base/FixedUiElement"; import {FixedUiElement} from "../Base/FixedUiElement";
import {TextField} from "../Input/TextField"; import {TextField} from "../Input/TextField";
import {SubtleButton} from "../Base/SubtleButton"; import {SubtleButton} from "../Base/SubtleButton";
import {LayerConfigJson} from "../../Customizations/JSON/LayerConfigJson";
export default class SavePanel extends UIElement { export default class SavePanel extends UIElement {
private json: UIElement; private json: UIElement;
@ -35,10 +34,7 @@ export default class SavePanel extends UIElement {
const jsonTextField = new TextField({ const jsonTextField = new TextField({
placeholder: "JSON Config", placeholder: "JSON Config",
fromString: str => str,
toString: str => str,
value: jsonStr, value: jsonStr,
startValidated: false,
textArea: true, textArea: true,
textAreaRows: 20 textAreaRows: 20
}); });

View file

@ -3,7 +3,7 @@ import {UIEventSource} from "../../Logic/UIEventSource";
import {InputElement} from "../Input/InputElement"; import {InputElement} from "../Input/InputElement";
import SingleSetting from "./SingleSetting"; import SingleSetting from "./SingleSetting";
import SettingsTable from "./SettingsTable"; import SettingsTable from "./SettingsTable";
import {TextField, ValidatedTextField} from "../Input/TextField"; import {TextField} from "../Input/TextField";
import Combine from "../Base/Combine"; import Combine from "../Base/Combine";
import MultiLingualTextFields from "../Input/MultiLingualTextFields"; import MultiLingualTextFields from "../Input/MultiLingualTextFields";
import {AndOrTagInput} from "../Input/AndOrTagInput"; import {AndOrTagInput} from "../Input/AndOrTagInput";
@ -16,6 +16,7 @@ import {UserDetails} from "../../Logic/Osm/OsmConnection";
import {State} from "../../State"; import {State} from "../../State";
import {VariableUiElement} from "../Base/VariableUIElement"; import {VariableUiElement} from "../Base/VariableUIElement";
import {FromJSON} from "../../Customizations/JSON/FromJSON"; import {FromJSON} from "../../Customizations/JSON/FromJSON";
import ValidatedTextField from "../Input/ValidatedTextField";
export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> { export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> {
@ -62,11 +63,11 @@ export default class TagRenderingPanel extends InputElement<TagRenderingConfigJs
const questionSettings = [ 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"), , "question", "Question", "If the key or mapping doesn't match, this question is asked"),
"<h3>Freeform key</h3>", "<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 specified, the rendering will search if this key is present." +
"If it is, the rendering above will be used to display the element.<br/>" + "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>"), "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>)[] = [ const settings: (string | SingleSetting<any>)[] = [
setting( 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."), 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` : "", questionsNotUnlocked ? `You need at least ${State.userJourney.themeGeneratorFullUnlock} changesets to unlock the 'question'-field and to use your theme to edit OSM data` : "",

View file

@ -6,6 +6,7 @@ import Combine from "./Base/Combine";
import {State} from "../State"; import {State} from "../State";
import {UIEventSource} from "../Logic/UIEventSource"; import {UIEventSource} from "../Logic/UIEventSource";
import {Imgur} from "../Logic/Web/Imgur"; import {Imgur} from "../Logic/Web/Imgur";
import {FixedUiElement} from "./Base/FixedUiElement";
export class ImageUploadFlow extends UIElement { export class ImageUploadFlow extends UIElement {
private _licensePicker: UIElement; private _licensePicker: UIElement;
@ -69,7 +70,7 @@ export class ImageUploadFlow extends UIElement {
if (this._isUploading.data == 1) { if (this._isUploading.data == 1) {
currentState.push(t.uploadingPicture); currentState.push(t.uploadingPicture);
} else if (this._isUploading.data > 0) { } 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) { if (this._didFail.data) {
@ -80,14 +81,15 @@ export class ImageUploadFlow extends UIElement {
currentState.push(t.uploadDone) currentState.push(t.uploadDone)
} }
let currentStateHtml = ""; let currentStateHtml : UIElement = new FixedUiElement("");
if (currentState.length > 0) { if (currentState.length > 0) {
currentStateHtml = new Combine(currentState).Render(); currentStateHtml = new Combine(currentState);
if (!this._allDone.data) { if (!this._allDone.data) {
currentStateHtml = "<span class='alert'>" + currentStateHtml.SetClass("alert");
currentStateHtml + }else{
"</span>"; currentStateHtml.SetClass("thanks");
} }
currentStateHtml.SetStyle("display:block ruby")
} }
const extraInfo = new Combine([ const extraInfo = new Combine([

View file

@ -35,7 +35,7 @@ export default class InputElementMap<T, X> extends InputElement<X> {
}), extraSources, x => { }), extraSources, x => {
return fromX(x); return fromX(x);
}); });
} }w
GetValue(): UIEventSource<X> { GetValue(): UIEventSource<X> {
return this._value; return this._value;

View file

@ -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);
}
}

View file

@ -3,9 +3,9 @@ import {UIEventSource} from "../../Logic/UIEventSource";
import {TextField} from "./TextField"; import {TextField} from "./TextField";
export default class MultiLingualTextFields extends InputElement<any> { export default class MultiLingualTextFields extends InputElement<any> {
private _fields: Map<string, TextField<string>> = new Map<string, TextField<string>>(); private _fields: Map<string, TextField> = new Map<string, TextField>();
private _value: UIEventSource<any>; private readonly _value: UIEventSource<any>;
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
constructor(languages: UIEventSource<string[]>, constructor(languages: UIEventSource<string[]>,
textArea: boolean = false, textArea: boolean = false,
@ -13,9 +13,9 @@ export default class MultiLingualTextFields extends InputElement<any> {
super(undefined); super(undefined);
this._value = value ?? new UIEventSource({}); this._value = value ?? new UIEventSource({});
this._value.addCallbackAndRun(latestData => { this._value.addCallbackAndRun(latestData => {
if(typeof(latestData) === "string"){ if (typeof (latestData) === "string") {
console.warn("Refusing string for multilingual input",latestData); console.warn("Refusing string for multilingual input", latestData);
self._value.setData({}); self._value.setData({});
} }
}) })
@ -26,7 +26,7 @@ export default class MultiLingualTextFields extends InputElement<any> {
if (languages === undefined) { if (languages === undefined) {
return; return;
} }
const newFields = new Map<string, TextField<string>>(); const newFields = new Map<string, TextField>();
for (const language of languages) { for (const language of languages) {
if (language.length != 2) { if (language.length != 2) {
continue; continue;
@ -34,7 +34,7 @@ export default class MultiLingualTextFields extends InputElement<any> {
let oldField = self._fields.get(language); let oldField = self._fields.get(language);
if (oldField === undefined) { if (oldField === undefined) {
oldField = TextField.StringInput(textArea); oldField = new TextField({textArea: textArea});
oldField.GetValue().addCallback(str => { oldField.GetValue().addCallback(str => {
self._value.data[language] = str; self._value.data[language] = str;
self._value.ping(); self._value.ping();

View file

@ -1,10 +1,5 @@
import {InputElement} from "./InputElement";
import {UIEventSource} from "../../Logic/UIEventSource"; import {UIEventSource} from "../../Logic/UIEventSource";
import {UIElement} from "../UIElement";
import Combine from "../Base/Combine";
import {SubtleButton} from "../Base/SubtleButton";
import TagInput from "./SingleTagInput"; import TagInput from "./SingleTagInput";
import {FixedUiElement} from "../Base/FixedUiElement";
import {MultiInput} from "./MultiInput"; import {MultiInput} from "./MultiInput";
export class MultiTagInput extends MultiInput<string> { export class MultiTagInput extends MultiInput<string> {

View file

@ -7,6 +7,7 @@ import {Utils} from "../../Utils";
import {UIElement} from "../UIElement"; import {UIElement} from "../UIElement";
import {VariableUiElement} from "../Base/VariableUIElement"; import {VariableUiElement} from "../Base/VariableUIElement";
import {FromJSON} from "../../Customizations/JSON/FromJSON"; import {FromJSON} from "../../Customizations/JSON/FromJSON";
import ValidatedTextField from "./ValidatedTextField";
export default class SingleTagInput extends InputElement<string> { 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", placeholder: "value - if blank, matches if key is NOT present",
fromString: str => str,
toString: str => str,
value: new UIEventSource<string>("") value: new UIEventSource<string>("")
} }
); );

View file

@ -51,6 +51,8 @@ export class TextField extends InputElement<string> {
// @ts-ignore // @ts-ignore
field.value = t; field.value = t;
}); });
this.dumbMode = false;
this.SetClass("deadbeef")
} }
GetValue(): UIEventSource<string> { GetValue(): UIEventSource<string> {
@ -60,17 +62,24 @@ export class TextField extends InputElement<string> {
InnerRender(): string { InnerRender(): string {
if (this._isArea) { 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("'", "&#39"); const placeholder = this._placeholder.InnerRender().replace("'", "&#39");
return `<form onSubmit='return false' class='form-text-field'>` + return `<span id="${this.id}"><form onSubmit='return false' class='form-text-field'>` +
`<input type='text' placeholder='${placeholder}' id='${this.id}'>` + `<input type='text' placeholder='${placeholder}' id='txt-${this.id}'>` +
`</form>`; `</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; const self = this;
field.oninput = () => { field.oninput = () => {
// @ts-ignore // @ts-ignore

View file

@ -186,7 +186,6 @@ export default class ValidatedTextField {
isValid?: ((string: string) => boolean) isValid?: ((string: string) => boolean)
}): InputElement<T> { }): InputElement<T> {
const textField = new TextField(options); const textField = new TextField(options);
return new InputElementMap( return new InputElementMap(
textField, (a, b) => a === b, textField, (a, b) => a === b,
fromString, toString fromString, toString

View file

@ -157,7 +157,7 @@ export class ShareScreen extends UIElement {
}, optionParts); }, optionParts);
this.iframe = url.map(url => `&lt;iframe src="${url}" width="100%" height="100%" title="${layout.title.InnerRender()} with MapComplete"&gt;&lt;/iframe&gt`); this.iframe = url.map(url => `&lt;iframe src="${url}" width="100%" height="100%" title="${layout.title?.InnerRender()??""} with MapComplete"&gt;&lt;/iframe&gt`);
this._iframeCode = new VariableUiElement( this._iframeCode = new VariableUiElement(
url.map((url) => { url.map((url) => {

View file

@ -34,13 +34,13 @@ export default class Translations {
}), }),
uploadingMultiple: new T({ uploadingMultiple: new T({
en: 'Uploading {count} of your picture...', en: "Uploading {count} of your picture...",
nl: 'Bezig met {count} foto\'s te uploaden...', nl: "Bezig met {count} foto's te uploaden...",
ca: 'Pujant {count} de la teva imatge...', ca: "Pujant {count} de la teva imatge...",
es: 'Subiendo {count} de tus fotos...', es: "Subiendo {count} de tus fotos...",
fr: 'Mettre votre {count} photos en ligne', fr: "Mettre votre {count} photos en ligne",
gl: 'Subindo {count} das túas imaxes...', gl: "Subindo {count} das túas imaxes...",
de: '{count} Ihrer Bilder hochgeladen...' de: "{count} Ihrer Bilder hochgeladen..."
}), }),
pleaseLogin: new T({ pleaseLogin: new T({