From 83964e598c89ff028f8ac38631caa6607ee764b8 Mon Sep 17 00:00:00 2001 From: Harsszegi Tibor Date: Wed, 12 Aug 2020 07:09:18 +0200 Subject: [PATCH 1/4] The keyboard doesn't work with Ionic Ionic uses different event binding compared to Angular (focus vs ionFocus, blur vs ionBlur), so the keyboard directive had to be updated so that it still works with both input/textarea and ion-input/ion-textarea --- src/core/src/directives/keyboard.directive.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/src/directives/keyboard.directive.ts b/src/core/src/directives/keyboard.directive.ts index 117d5bff..6776cabd 100644 --- a/src/core/src/directives/keyboard.directive.ts +++ b/src/core/src/directives/keyboard.directive.ts @@ -6,7 +6,7 @@ import { MatKeyboardComponent } from '../components/keyboard/keyboard.component' import { MatKeyboardService } from '../services/keyboard.service'; @Directive({ - selector: 'input[matKeyboard], textarea[matKeyboard]' + selector: 'ion-input[matKeyboard], ion-textarea[matKeyboard], input[matKeyboard], textarea[matKeyboard]' }) export class MatKeyboardDirective implements OnDestroy { @@ -37,6 +37,7 @@ export class MatKeyboardDirective implements OnDestroy { } @HostListener('focus', ['$event']) + @HostListener('ionFocus', ['$event']) public showKeyboard() { this._keyboardRef = this._keyboardService.open(this.matKeyboard, { darkTheme: this.darkTheme, @@ -60,6 +61,7 @@ export class MatKeyboardDirective implements OnDestroy { } @HostListener('blur', ['$event']) + @HostListener('ionBlur', ['$event']) public hideKeyboard() { if (this._keyboardRef) { this._keyboardRef.dismiss(); From 707b849c6e2c972bd2f5832a5289bdc8f881dbf3 Mon Sep 17 00:00:00 2001 From: Harsszegi Tibor Date: Wed, 12 Aug 2020 11:11:34 +0200 Subject: [PATCH 2/4] nativeElement + Ionic issue this.input.nativeElement can't be directly used with Ionic as obviously that would resolve to an IonInput (or IonTextArea respectively). Ionic provides a getInputElement() function but that returns a Promise, which would mean to refactor internally quite a lot of code in keyboard-key. Instead directly the firstChild attribute is used, which is exactly the same thing. --- .../keyboard-key/keyboard-key.component.ts | 109 +++++++++++------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/src/core/src/components/keyboard-key/keyboard-key.component.ts b/src/core/src/components/keyboard-key/keyboard-key.component.ts index cf74a108..d2b94429 100644 --- a/src/core/src/components/keyboard-key/keyboard-key.component.ts +++ b/src/core/src/components/keyboard-key/keyboard-key.component.ts @@ -141,18 +141,26 @@ export class MatKeyboardKeyComponent implements OnInit, OnDestroy { get inputValue(): string { if (this.control) { return this.control.value; - } else if (this.input && this.input.nativeElement && this.input.nativeElement.value) { - return this.input.nativeElement.value; - } else { - return ''; } + + let el = this._getInputElement(); + if (el && el.value) { + return el.value; + } + + return ''; } set inputValue(inputValue: string) { if (this.control) { this.control.setValue(inputValue); - } else if (this.input && this.input.nativeElement) { - this.input.nativeElement.value = inputValue; + return; + } + + let el = this._getInputElement(); + if (el) { + el.value = inputValue; + return; } } @@ -234,13 +242,13 @@ export class MatKeyboardKeyComponent implements OnInit, OnDestroy { if (char && this.input) { this.replaceSelectedText(char); - this._setCursorPosition(caret + 1); } // Dispatch Input Event for Angular to register a change - if (this.input && this.input.nativeElement) { + let el = this._getInputElement(); + if (el) { setTimeout(() => { - this.input.nativeElement.dispatchEvent(new Event('input', { bubbles: true })); + el.dispatchEvent(new Event('input', { bubbles: true })); }); } } @@ -296,11 +304,11 @@ export class MatKeyboardKeyComponent implements OnInit, OnDestroy { if (char && this.input) { this.replaceSelectedText(char); - this._setCursorPosition(caret + 1); } - if (this.input && this.input.nativeElement) { - setTimeout(() => this.input.nativeElement.dispatchEvent(new Event('input', { bubbles: true }))); + let el = this._getInputElement(); + if (el) { + setTimeout(() => el.dispatchEvent(new Event('input', { bubbles: true }))); } }, REPEAT_INTERVAL); }, REPEAT_TIMEOUT); @@ -331,21 +339,23 @@ export class MatKeyboardKeyComponent implements OnInit, OnDestroy { selectionLength = 1; } - const headPart = value.slice(0, caret); - const endPart = value.slice(caret + selectionLength); - - this.inputValue = [headPart, endPart].join(''); - this._setCursorPosition(caret); + this._getInputElement().setRangeText("", caret, caret + selectionLength); } private replaceSelectedText(char: string): void { const value = this.inputValue ? this.inputValue.toString() : ''; + + let el = this._getInputElement(); + if (el && 'maxLength' in el && value.length + char.length > el.maxLength) { + return; + } + const caret = this.input ? this._getCursorPosition() : 0; const selectionLength = this._getSelectionLength(); const headPart = value.slice(0, caret); const endPart = value.slice(caret + selectionLength); - - this.inputValue = [headPart, char, endPart].join(''); + this._getInputElement().setRangeText(char, caret, caret + selectionLength); + this._setCursorPosition(caret + char.length); } // TODO: Include for repeating keys as well (if this gets implemented) @@ -376,9 +386,10 @@ export class MatKeyboardKeyComponent implements OnInit, OnDestroy { return; } - if ('selectionStart' in this.input.nativeElement) { + let el = this._getInputElement(); + if (el && 'selectionStart' in el) { // Standard-compliant browsers - return this.input.nativeElement.selectionStart; + return el.selectionStart; } else if ('selection' in window.document) { // IE this.input.nativeElement.focus(); @@ -396,12 +407,11 @@ export class MatKeyboardKeyComponent implements OnInit, OnDestroy { return; } - if ('selectionEnd' in this.input.nativeElement) { + let el = this._getInputElement(); + if (el && 'selectionEnd' in el) { // Standard-compliant browsers - return this.input.nativeElement.selectionEnd - this.input.nativeElement.selectionStart; - } - - if ('selection' in window.document) { + return el.selectionEnd - el.selectionStart; + } else if ('selection' in window.document) { // IE this.input.nativeElement.focus(); const selection: any = window.document['selection']; @@ -414,36 +424,55 @@ export class MatKeyboardKeyComponent implements OnInit, OnDestroy { // tslint:disable one-line private _setCursorPosition(position: number): boolean { if (!this.input) { - return; + return false; } - this.inputValue = this.control.value; + if (this.control) { + this.inputValue = this.control.value; + } // ^ this is used to not only get "focus", but // to make sure we don't have it everything -selected- // (it causes an issue in chrome, and having it doesn't hurt any other browser) - - if ('createTextRange' in this.input.nativeElement) { - const range = this.input.nativeElement.createTextRange(); + let el = this._getInputElement(); + // createTextRange is a IE only stuff + if (el && 'createTextRange' in el) { + const range = (el as any).createTextRange(); range.move('character', position); range.select(); return true; - } else { + } else if (el) { // (el.selectionStart === 0 added for Firefox bug) - if (this.input.nativeElement.selectionStart || this.input.nativeElement.selectionStart === 0) { - this.input.nativeElement.focus(); - this.input.nativeElement.setSelectionRange(position, position); + if (el.selectionStart || el.selectionStart === 0) { + el.focus(); + el.setSelectionRange(position, position); return true; } // fail city, fortunately this never happens (as far as I've tested) :) - else { - this.input.nativeElement.focus(); - return false; - } + el.focus(); + return false; } + + return false; } private _isTextarea(): boolean { - return this.input && this.input.nativeElement && this.input.nativeElement.tagName === 'TEXTAREA'; + let el = this._getInputElement(); + if (!el) { + return false; + } + + return el.tagName === 'TEXTAREA'; } + private _getInputElement(): HTMLInputElement | undefined { + if (this.input && this.input.nativeElement) { + if (this.input.nativeElement.localName && this.input.nativeElement.localName.startsWith('ion-')) { + return this.input.nativeElement.firstChild; + } else { + return this.input.nativeElement; + } + } + + return undefined; + } } From 700aed757a8734da3e4c672189325515add4a0e1 Mon Sep 17 00:00:00 2001 From: Harsszegi Tibor Date: Wed, 12 Aug 2020 11:23:02 +0200 Subject: [PATCH 3/4] Maximum limit and selection together doesn't work In case a selection is in place and you wish to replace that with a character and there is maximum limit set and the input field is already filled completely to the maximum, you are unable to replace the selected content. --- .../src/components/keyboard-key/keyboard-key.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/src/components/keyboard-key/keyboard-key.component.ts b/src/core/src/components/keyboard-key/keyboard-key.component.ts index d2b94429..0eae7e0e 100644 --- a/src/core/src/components/keyboard-key/keyboard-key.component.ts +++ b/src/core/src/components/keyboard-key/keyboard-key.component.ts @@ -344,14 +344,14 @@ export class MatKeyboardKeyComponent implements OnInit, OnDestroy { private replaceSelectedText(char: string): void { const value = this.inputValue ? this.inputValue.toString() : ''; + const selectionLength = this._getSelectionLength(); let el = this._getInputElement(); - if (el && 'maxLength' in el && value.length + char.length > el.maxLength) { + if (el && 'maxLength' in el && value.length - selectionLength + char.length > el.maxLength) { return; } const caret = this.input ? this._getCursorPosition() : 0; - const selectionLength = this._getSelectionLength(); const headPart = value.slice(0, caret); const endPart = value.slice(caret + selectionLength); this._getInputElement().setRangeText(char, caret, caret + selectionLength); From f8d4dfa83d6b434bb4388ba5c21866d45b0a9669 Mon Sep 17 00:00:00 2001 From: Harsszegi Tibor Date: Wed, 12 Aug 2020 13:02:16 +0200 Subject: [PATCH 4/4] doesn't work Entering text into doesn't work even if maxLength is not set. --- src/core/src/components/keyboard-key/keyboard-key.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/src/components/keyboard-key/keyboard-key.component.ts b/src/core/src/components/keyboard-key/keyboard-key.component.ts index 0eae7e0e..16c057e9 100644 --- a/src/core/src/components/keyboard-key/keyboard-key.component.ts +++ b/src/core/src/components/keyboard-key/keyboard-key.component.ts @@ -347,7 +347,7 @@ export class MatKeyboardKeyComponent implements OnInit, OnDestroy { const selectionLength = this._getSelectionLength(); let el = this._getInputElement(); - if (el && 'maxLength' in el && value.length - selectionLength + char.length > el.maxLength) { + if (el && 'maxLength' in el && el.maxLength > 0 && value.length - selectionLength + char.length > el.maxLength) { return; }