diff --git a/UI/Input/TextField.ts b/UI/Input/TextField.ts index 6a20faa..0ae1e28 100644 --- a/UI/Input/TextField.ts +++ b/UI/Input/TextField.ts @@ -11,14 +11,14 @@ export class TextField extends InputElement { private readonly _isArea: boolean; private readonly _textAreaRows: number; - private readonly _isValid: (string) => boolean; + private readonly _isValid: (string, country) => boolean; constructor(options?: { placeholder?: string | UIElement, value?: UIEventSource, textArea?: boolean, textAreaRows?: number, - isValid?: ((s: string) => boolean) + isValid?: ((s: string, country?: string) => boolean) }) { super(undefined); const self = this; @@ -28,7 +28,7 @@ export class TextField extends InputElement { this.value = options?.value ?? new UIEventSource(undefined); this._textAreaRows = options.textAreaRows; - this._isValid = options.isValid ?? ((str) => true); + this._isValid = options.isValid ?? ((str, country) => true); this._placeholder = Translations.W(options.placeholder ?? ""); this.ListenTo(this._placeholder._source); @@ -37,6 +37,7 @@ export class TextField extends InputElement { self.IsSelected.setData(true) }); this.value.addCallback((t) => { + console.log("Setting actual value to", t); const field = document.getElementById("txt-"+this.id); if (field === undefined || field === null) { return; @@ -74,10 +75,12 @@ export class TextField extends InputElement { const field = document.getElementById("txt-" + this.id); const self = this; field.oninput = () => { + + // How much characters are on the right, not including spaces? // @ts-ignore - const endDistance = field.value.length - field.selectionEnd; + const endDistance = field.value.substring(field.selectionEnd).replace(/ /g,'').length; // @ts-ignore - const val: string = field.value; + let val: string = field.value; if (!self.IsValid(val)) { self.value.setData(undefined); } else { @@ -86,8 +89,21 @@ export class TextField extends InputElement { // Setting the value might cause the value to be set again. We keep the distance _to the end_ stable, as phone number formatting might cause the start to change // See https://github.com/pietervdvn/MapComplete/issues/103 // We reread the field value - it might have changed! + // @ts-ignore - self.SetCursorPosition(field.value.length - endDistance); + val = field.value; + let newCursorPos = endDistance; + while(newCursorPos >= 0 && + // We count the number of _actual_ characters (non-space characters) on the right of the new value + // This count should become bigger then the end distance + val.substr(newCursorPos).replace(/ /g, '').length <= endDistance + ){ + newCursorPos --; + } + newCursorPos++; + + // @ts-ignore + self.SetCursorPosition(newCursorPos); }; if (this.value.data !== undefined && this.value.data !== null) { @@ -127,7 +143,7 @@ export class TextField extends InputElement { if (t === undefined || t === null) { return false } - return this._isValid(t); + return this._isValid(t, undefined); } } diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index d6659d0..c8e25b3 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -140,9 +140,7 @@ export default class ValidatedTextField { } return parsePhoneNumberFromString(str, country?.toUpperCase())?.isValid() ?? false }, - (str, country: any) => { - return parsePhoneNumberFromString(str, country?.toUpperCase()).formatInternational() - } + (str, country: any) => parsePhoneNumberFromString(str, country?.toUpperCase()).formatInternational() ) ] @@ -170,21 +168,38 @@ export default class ValidatedTextField { value?: UIEventSource, textArea?: boolean, textAreaRows?: number, - isValid?: ((s: string) => boolean) + isValid?: ((s: string, country: string) => boolean), + country?: string }): InputElement { options = options ?? {}; options.placeholder = options.placeholder ?? type; const tp: TextFieldDef = ValidatedTextField.AllTypes[type] - let isValid = tp.isValid; + const isValidTp = tp.isValid; + let isValid; if (options.isValid) { const optValid = options.isValid; isValid = (str, country) => { - return ValidatedTextField.AllTypes[type](str, country) && optValid(str); + if(str === undefined){ + return false; + } + return isValidTp(str, country ?? options.country) && optValid(str, country ?? options.country); } + }else{ + isValid = isValidTp; } options.isValid = isValid; let input: InputElement = new TextField(options); + if (tp.reformat) { + input.GetValue().addCallbackAndRun(str => { + if (!options.isValid(str, options.country)) { + return; + } + const formatted = tp.reformat(str, options.country); + input.GetValue().setData(formatted); + }) + } + if (tp.inputHelper) { input = new CombinedInputElement(input, tp.inputHelper(input.GetValue())); } @@ -252,11 +267,12 @@ export default class ValidatedTextField { startValidated?: boolean, textArea?: boolean, textAreaRows?: number, - isValid?: ((string: string) => boolean) + isValid?: ((string: string) => boolean), + country?: string }): InputElement { let textField: InputElement; if (options.type) { - textField = ValidatedTextField.InputForType(options.type); + textField = ValidatedTextField.InputForType(options.type, options); } else { textField = new TextField(options); } diff --git a/UI/TagRendering.ts b/UI/TagRendering.ts index 71cd2a5..20928cd 100644 --- a/UI/TagRendering.ts +++ b/UI/TagRendering.ts @@ -326,15 +326,13 @@ export class TagRendering extends UIElement implements TagDependantUIElement { } - let formatter = ValidatedTextField.AllTypes[type].reformat ?? ((str) => str); - const pickString = (string: any) => { if (string === "" || string === undefined) { return undefined; } - const tag = new Tag(freeform.key, formatter(string, this._source.data._country)); + const tag = new Tag(freeform.key, string); if (freeform.extraTags === undefined) { return tag; @@ -361,11 +359,14 @@ export class TagRendering extends UIElement implements TagDependantUIElement { return undefined; } + console.log("Creating a freeform input element for ", this._source.data._country); + return ValidatedTextField.Mapped(pickString, toString, { placeholder: this._freeform.placeholder, type: type, isValid: (str) => (str.length <= 255), - textArea: isTextArea + textArea: isTextArea, + country: this._source.data._country }) }