MOBILE-3749 a11y: Repair autofocus directive
This commit is contained in:
		
							parent
							
								
									a8c40ff1d0
								
							
						
					
					
						commit
						c3d0e39c3e
					
				| @ -58,7 +58,7 @@ | ||||
|                             <ion-label position="stacked">{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label> | ||||
|                             <core-show-password name="password"> | ||||
|                                 <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||
|                                     [autofocus]="true" #passwordinput [clearOnEdit]="false"> | ||||
|                                     core-auto-focus #passwordinput [clearOnEdit]="false"> | ||||
|                                 </ion-input> | ||||
|                             </core-show-password> | ||||
|                         </ion-item> | ||||
|  | ||||
| @ -15,7 +15,7 @@ | ||||
|             <ion-label>{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label> | ||||
|             <core-show-password name="password"> | ||||
|                 <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||
|                     [autofocus]="true" #passwordinput [clearOnEdit]="false"> | ||||
|                     core-auto-focus #passwordinput [clearOnEdit]="false"> | ||||
|                 </ion-input> | ||||
|             </core-show-password> | ||||
|         </ion-item> | ||||
|  | ||||
| @ -14,7 +14,7 @@ | ||||
| 
 | ||||
|         <!-- Form to edit the file's name. --> | ||||
|         <ion-input type="text" name="filename" [placeholder]="'core.filename' | translate" autocapitalize="none" autocorrect="off" | ||||
|             (click)="$event.stopPropagation()" [autofocus]="true" [(ngModel)]="newFileName" *ngIf="editMode"> | ||||
|             (click)="$event.stopPropagation()" core-auto-focus [(ngModel)]="newFileName" *ngIf="editMode"> | ||||
|         </ion-input> | ||||
| 
 | ||||
|         <div class="buttons" slot="end" *ngIf="manage"> | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <form #messageForm> | ||||
|     <textarea class="core-send-message-input" [autofocus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows | ||||
|     <textarea class="core-send-message-input" [core-auto-focus]="showKeyboard" [placeholder]="placeholder" rows="1" core-auto-rows | ||||
|         [(ngModel)]="message" name="message" (onResize)="textareaResized()" (keyup.enter)="enterClicked($event)" | ||||
|         (keyup.control.enter)="enterClicked($event, 'control')" (keyup.meta.enter)="enterClicked($event, 'meta')"></textarea> | ||||
|     <ion-button fill="clear" size="large" type="submit" [disabled]="!message || sendDisabled" | ||||
|  | ||||
| @ -12,7 +12,7 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Directive, Input, OnInit, ElementRef } from '@angular/core'; | ||||
| import { Directive, Input, ElementRef, AfterViewInit } from '@angular/core'; | ||||
| 
 | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| @ -20,16 +20,17 @@ import { CoreUtils } from '@services/utils/utils'; | ||||
| /** | ||||
|  * Directive to auto focus an element when a view is loaded. | ||||
|  * | ||||
|  * You can apply it conditionallity assigning it a boolean value: <ion-input [core-auto-focus]="{{showKeyboard}}"> | ||||
|  * The value of the input will decide if show keyboard when focusing the element (only on Android). | ||||
|  * In case value is nofocus, the directive is disabled. | ||||
|  * | ||||
|  * @deprecated since 3.9.5. ion-input now supports an [autofocus] attribute, please use that one instead. | ||||
|  * <ion-input [core-auto-focus]="showKeyboard"> | ||||
|  */ | ||||
| @Directive({ | ||||
|     selector: '[core-auto-focus]', | ||||
| }) | ||||
| export class CoreAutoFocusDirective implements OnInit { | ||||
| export class CoreAutoFocusDirective implements AfterViewInit { | ||||
| 
 | ||||
|     @Input('core-auto-focus') coreAutoFocus: boolean | string = true; | ||||
|     @Input('core-auto-focus') showKeyboard: boolean | string = true; | ||||
| 
 | ||||
|     protected element: HTMLElement; | ||||
| 
 | ||||
| @ -38,31 +39,54 @@ export class CoreAutoFocusDirective implements OnInit { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component being initialized. | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         this.autoFocus(); | ||||
|     ngAfterViewInit(): void { | ||||
|         if (this.showKeyboard === 'nofocus') { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.setFocus(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Function after the view is initialized. | ||||
|      * Function to focus the element. | ||||
|      * | ||||
|      * @param retries Internal param to stop retrying then 0. | ||||
|      */ | ||||
|     protected autoFocus(): void { | ||||
|         const autoFocus = CoreUtils.isTrueOrOne(this.coreAutoFocus); | ||||
|         if (autoFocus) { | ||||
|             // Wait a bit to make sure the view is loaded.
 | ||||
|             setTimeout(() => { | ||||
|                 // If it's a ion-input or ion-textarea, search the right input to use.
 | ||||
|                 let element = this.element; | ||||
|                 if (this.element.tagName == 'ION-INPUT') { | ||||
|                     element = this.element.querySelector('input') || element; | ||||
|                 } else if (this.element.tagName == 'ION-TEXTAREA') { | ||||
|                     element = this.element.querySelector('textarea') || element; | ||||
|                 } | ||||
| 
 | ||||
|                 CoreDomUtils.focusElement(element); | ||||
|             }, 200); | ||||
|     protected setFocus(retries = 10): void { | ||||
|         if (retries == 0) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Wait a bit to make sure the view is loaded.
 | ||||
|         setTimeout(() => { | ||||
|             // If it's a ion-input or ion-textarea, search the right input to use.
 | ||||
|             let element: HTMLElement | null = null; | ||||
| 
 | ||||
|             if (this.element.tagName == 'ION-INPUT') { | ||||
|                 element = this.element.querySelector('input'); | ||||
|             } else if (this.element.tagName == 'ION-TEXTAREA') { | ||||
|                 element = this.element.querySelector('textarea'); | ||||
|             } else { | ||||
|                 element = this.element; | ||||
|             } | ||||
| 
 | ||||
|             if (!element) { | ||||
|                 this.setFocus(retries - 1); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             const showKeyboard = this.showKeyboard === '' || CoreUtils.isTrueOrOne(this.showKeyboard); | ||||
|             CoreDomUtils.focusElement(element, showKeyboard); | ||||
| 
 | ||||
|             if (element != document.activeElement) { | ||||
|                 this.setFocus(retries - 1); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
|         }, 200); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -20,7 +20,7 @@ | ||||
|                     type="password" | ||||
|                     placeholder="{{ 'core.courses.password' | translate }}" | ||||
|                     [(ngModel)]="password" | ||||
|                     [autofocus]="true" | ||||
|                     core-auto-focus | ||||
|                     [clearOnEdit]="false"> | ||||
|                 </ion-input> | ||||
|             </core-show-password> | ||||
|  | ||||
| @ -34,7 +34,7 @@ | ||||
|                 <ion-label></ion-label> | ||||
|                 <ion-input type="text" name="username" placeholder="{{ 'core.login.username' | translate }}" | ||||
|                     formControlName="username" autocapitalize="none" autocorrect="off" autocomplete="username" enterkeyhint="next" | ||||
|                     required="true"> | ||||
|                     required="true" core-auto-focus> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
|             <ion-item *ngIf="siteChecked && !isBrowserSSO" class="ion-margin-bottom"> | ||||
|  | ||||
| @ -31,7 +31,7 @@ | ||||
|             <ion-item> | ||||
|                 <ion-label></ion-label> | ||||
|                 <ion-input type="text" name="value" placeholder="{{ 'core.login.usernameoremail' | translate }}" | ||||
|                     formControlName="value" autocapitalize="none" autocorrect="off" [autofocus]="autoFocus"> | ||||
|                     formControlName="value" autocapitalize="none" autocorrect="off" [core-auto-focus]="showKeyboard"> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
|             <ion-button type="submit" class="ion-margin" expand="block" [disabled]="!myForm.valid"> | ||||
|  | ||||
| @ -35,7 +35,7 @@ export class CoreLoginForgottenPasswordPage implements OnInit { | ||||
| 
 | ||||
|     myForm!: FormGroup; | ||||
|     siteUrl!: string; | ||||
|     autoFocus!: boolean; | ||||
|     showKeyboard!: boolean; | ||||
| 
 | ||||
|     constructor( | ||||
|         protected formBuilder: FormBuilder, | ||||
| @ -55,7 +55,7 @@ export class CoreLoginForgottenPasswordPage implements OnInit { | ||||
|         } | ||||
| 
 | ||||
|         this.siteUrl = siteUrl; | ||||
|         this.autoFocus = Platform.is('tablet'); | ||||
|         this.showKeyboard = Platform.is('tablet'); | ||||
|         this.myForm = this.formBuilder.group({ | ||||
|             field: ['username', Validators.required], | ||||
|             value: [CoreNavigator.getRouteParam<string>('username') || '', Validators.required], | ||||
|  | ||||
| @ -42,7 +42,7 @@ | ||||
|             <core-show-password name="password"> | ||||
|                 <ion-input class="core-ioninput-password" name="password" type="password" | ||||
|                     placeholder="{{ 'core.login.password' | translate }}" formControlName="password" [clearOnEdit]="false" | ||||
|                     autocomplete="current-password" enterkeyhint="go" required="true"> | ||||
|                     autocomplete="current-password" enterkeyhint="go" required="true" core-auto-focus> | ||||
|                 </ion-input> | ||||
|             </core-show-password> | ||||
|         </ion-item> | ||||
|  | ||||
| @ -27,7 +27,7 @@ | ||||
|                     <h2>{{ 'core.login.siteaddress' | translate }}</h2> | ||||
|                 </ion-label> | ||||
|                 <ion-input name="url" type="url" placeholder="{{ 'core.login.siteaddressplaceholder' | translate }}" | ||||
|                     formControlName="siteUrl" [autofocus]="showKeyboard && !showScanQR"> | ||||
|                     formControlName="siteUrl" [core-auto-focus]="showKeyboard"> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
|         </ng-container> | ||||
| @ -37,7 +37,7 @@ | ||||
|                     <h2>{{ 'core.login.siteaddress' | translate }}</h2> | ||||
|                 </ion-label> | ||||
|                 <ion-input name="url" placeholder="{{ 'core.login.siteaddressplaceholder' | translate }}" formControlName="siteUrl" | ||||
|                     [autofocus]="showKeyboard && !showScanQR" (ionChange)="searchSite($event, siteForm.value.siteUrl)"> | ||||
|                     [core-auto-focus]="showKeyboard" (ionChange)="searchSite($event, siteForm.value.siteUrl)"> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
| 
 | ||||
|  | ||||
| @ -3,7 +3,7 @@ | ||||
|         <ion-item> | ||||
|             <ion-label></ion-label> | ||||
|             <ion-input type="search" name="search" [(ngModel)]="searchText" [placeholder]="placeholder" | ||||
|                 [autocorrect]="autocorrect" [spellcheck]="spellcheck" [autofocus]="autoFocus" | ||||
|                 [autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus || 'nofocus'" | ||||
|                 [disabled]="disabled" role="searchbox" (ionFocus)="focus($event)"> | ||||
|             </ion-input> | ||||
|             <ion-button slot="end" fill="clear" type="submit" size="small" [attr.aria-label]="searchLabel" | ||||
|  | ||||
| @ -391,11 +391,12 @@ export class CoreDomUtilsProvider { | ||||
|      * Focus an element and open keyboard. | ||||
|      * | ||||
|      * @param el HTML element to focus. | ||||
|      * @param showKeyboard Show keyboard when focusing the element. | ||||
|      */ | ||||
|     focusElement(el: HTMLElement): void { | ||||
|     focusElement(el: HTMLElement, showKeyboard = true): void { | ||||
|         if (el?.focus) { | ||||
|             el.focus(); | ||||
|             if (CoreApp.isAndroid() && this.supportsInputKeyboard(el)) { | ||||
|             if (showKeyboard && CoreApp.isAndroid() && this.supportsInputKeyboard(el)) { | ||||
|                 // On some Android versions the keyboard doesn't open automatically.
 | ||||
|                 CoreApp.openKeyboard(); | ||||
|             } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user