forked from MapComplete/MapComplete
		
	Nailed phone number input
This commit is contained in:
		
							parent
							
								
									585a4243f7
								
							
						
					
					
						commit
						07c85bb218
					
				
					 3 changed files with 52 additions and 19 deletions
				
			
		|  | @ -11,14 +11,14 @@ export class TextField extends InputElement<string> { | |||
|     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<string>, | ||||
|         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<string> { | |||
|         this.value = options?.value ?? new UIEventSource<string>(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<string> { | |||
|             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<string> { | |||
|         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<string> { | |||
|             // 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<string> { | |||
|         if (t === undefined || t === null) { | ||||
|             return false | ||||
|         } | ||||
|         return this._isValid(t); | ||||
|         return this._isValid(t, undefined); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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<string>, | ||||
|         textArea?: boolean, | ||||
|         textAreaRows?: number, | ||||
|         isValid?: ((s: string) => boolean) | ||||
|         isValid?: ((s: string, country: string) => boolean), | ||||
|         country?: string | ||||
|     }): InputElement<string> { | ||||
|         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<string> = 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<T> { | ||||
|         let textField: InputElement<string>; | ||||
|         if (options.type) { | ||||
|             textField = ValidatedTextField.InputForType(options.type); | ||||
|             textField = ValidatedTextField.InputForType(options.type, options); | ||||
|         } else { | ||||
|             textField = new TextField(options); | ||||
|         } | ||||
|  |  | |||
|  | @ -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 | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue