forked from EVOgeek/Vmeda.Online
		
	MOBILE-3947 core: Slot core-show-password on ion-inputs
This commit is contained in:
		
							parent
							
								
									243386232e
								
							
						
					
					
						commit
						be7f86edd2
					
				| @ -28,11 +28,11 @@ | ||||
|                 <ion-card *ngIf="askPassword"> | ||||
|                     <form (ngSubmit)="submitPassword($event, passwordinput)" #passwordForm> | ||||
|                         <ion-item class="ion-text-wrap"> | ||||
|                             <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 }}" | ||||
|                                     core-auto-focus #passwordinput [clearOnEdit]="false" /> | ||||
|                             </core-show-password> | ||||
|                             <ion-input labelPlacement="stacked" name="password" type="password" | ||||
|                                 placeholder="{{ 'core.login.password' | translate }}" core-auto-focus #passwordinput [clearOnEdit]="false" | ||||
|                                 [label]="'addon.mod_lesson.enterpassword' | translate"> | ||||
|                                 <core-show-password slot="end" /> | ||||
|                             </ion-input> | ||||
|                         </ion-item> | ||||
|                         <ion-button expand="block" type="submit"> | ||||
|                             {{ 'addon.mod_lesson.continue' | translate }} | ||||
|  | ||||
| @ -5,9 +5,9 @@ | ||||
|     </ion-label> | ||||
| </ion-item> | ||||
| <ion-item [formGroup]="form"> | ||||
|     <ion-label class="sr-only">{{ 'addon.mod_quiz.quizpassword' | translate }}</ion-label> | ||||
|     <core-show-password name="quizpassword"> | ||||
|         <ion-input id="addon-mod_quiz-accessrule-password-input" name="quizpassword" type="password" | ||||
|             placeholder="{{ 'addon.mod_quiz.quizpassword' | translate }}" [formControlName]="'quizpassword'" [clearOnEdit]="false" /> | ||||
|     </core-show-password> | ||||
|     <ion-input id="addon-mod_quiz-accessrule-password-input" name="quizpassword" type="password" | ||||
|         placeholder="{{ 'addon.mod_quiz.quizpassword' | translate }}" [formControlName]="'quizpassword'" [clearOnEdit]="false" | ||||
|         [attr.aria-label]="'addon.mod_quiz.quizpassword' | translate"> | ||||
|         <core-show-password slot="end" /> | ||||
|     </ion-input> | ||||
| </ion-item> | ||||
|  | ||||
| @ -14,11 +14,11 @@ | ||||
|     <form (ngSubmit)="submitPassword($event)" #passwordForm> | ||||
|         <div> | ||||
|             <ion-item> | ||||
|                 <core-show-password name="password"> | ||||
|                     <ion-input [attr.aria-label]="placeholder | translate" class="ion-text-wrap core-ioninput-password" name="password" | ||||
|                         type="password" placeholder="{{ placeholder | translate }}" [(ngModel)]="password" core-auto-focus | ||||
|                         [clearOnEdit]="false" /> | ||||
|                 </core-show-password> | ||||
|                 <ion-input [attr.aria-label]="placeholder | translate" class="ion-text-wrap core-ioninput-password" name="password" | ||||
|                     type="password" placeholder="{{ placeholder | translate }}" [(ngModel)]="password" core-auto-focus | ||||
|                     [clearOnEdit]="false"> | ||||
|                     <core-show-password slot="end" /> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
|             <ion-item *ngIf="error" class="ion-text-wrap ion-padding-top text-danger"> | ||||
|                 <core-format-text [text]="error | translate" /> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| <ng-content></ng-content> | ||||
| <ng-content /> | ||||
| <ion-button fill="clear" [attr.aria-label]="(shown ? 'core.hide' : 'core.show') | translate" core-suppress-events (onClick)="toggle($event)" | ||||
|     (mousedown)="doNotBlur($event)" (keydown)="doNotBlur($event)" (keyup)="toggle($event)"> | ||||
|     <ion-icon [name]="shown ? 'fas-eye-slash' : 'fas-eye'" slot="icon-only" aria-hidden="true" /> | ||||
|  | ||||
| @ -3,30 +3,29 @@ | ||||
| :host { | ||||
|     display: contents; | ||||
| 
 | ||||
|     ion-button { | ||||
|     // Only applies to deprecated way (surrounding). | ||||
|     ::ng-deep ion-input + ion-button { | ||||
|         background: transparent; | ||||
|         padding: 0 calc(var(--padding-start) / 2); | ||||
|         position: absolute; | ||||
|         @include safe-area-position(null, 0px, null, null); | ||||
|         padding: 0 var(--inner-padding-end) 0 4px; | ||||
|         margin-top: 0; | ||||
|         margin-bottom: 0; | ||||
|         z-index: 3; | ||||
|         bottom: 0; | ||||
|         position: absolute; | ||||
|         @include safe-area-position(null, 0px, null, null); | ||||
|         top: 0; | ||||
|     } | ||||
| 
 | ||||
|     // Only applies to deprecated way (surrounding). | ||||
|     ::ng-deep ion-input { | ||||
|         --padding-end: 56px !important; | ||||
|     } | ||||
| 
 | ||||
|     ::ng-deep ion-input.input-label-placement-stacked + ion-button { | ||||
|         top: 14px; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| ::ng-deep ion-input { | ||||
|     --padding-end: 47px !important; | ||||
| } | ||||
| 
 | ||||
| :host-context(.md.item-label.stacked) ion-button { | ||||
|     bottom: 0; | ||||
| } | ||||
| 
 | ||||
| :host-context(.iositem-label.stacked) ion-button { | ||||
|     bottom: -5px; | ||||
| } | ||||
| 
 | ||||
| :host-context(.ios) ion-button { | ||||
|     bottom: 0; | ||||
| ion-button { | ||||
|     z-index: 5; | ||||
|     pointer-events: visible; | ||||
| } | ||||
|  | ||||
| @ -18,18 +18,31 @@ import { IonInput } from '@ionic/angular'; | ||||
| import { CorePlatform } from '@services/platform'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { CoreLogger } from '@singletons/logger'; | ||||
| 
 | ||||
| /** | ||||
|  * Component to allow showing and hiding a password. The affected input MUST have a name to identify it. | ||||
|  * This component allows to show/hide a password. | ||||
|  * It's meant to be used with ion-input. | ||||
|  * It's recommended to use it as a slot of the input. | ||||
|  * | ||||
|  * @description | ||||
|  * This directive needs to surround the input with the password. | ||||
|  * | ||||
|  * You need to supply the name of the input. | ||||
|  * There are 2 ways to use ths component: | ||||
|  * - Slot it to start or end on the ion-input element. | ||||
|  * - Surround the ion-input with the password with this component. This is deprecated. | ||||
|  * | ||||
|  * Example: | ||||
|  * In order to help finding the input you can specify the name of the input or the ion-input element. | ||||
|  * | ||||
|  * <core-show-password name="password"> | ||||
|  * | ||||
|  * Example of new usage: | ||||
|  * | ||||
|  * <ion-input type="password" name="password"> | ||||
|  *     <core-show-password slot="end" /> | ||||
|  * </ion-input> | ||||
|  * | ||||
|  * Example deprecated usage: | ||||
|  * | ||||
|  * <core-show-password> | ||||
|  *     <ion-input type="password" name="password"></ion-input> | ||||
|  * </core-show-password> | ||||
|  */ | ||||
| @ -40,17 +53,30 @@ import { CoreUtils } from '@services/utils/utils'; | ||||
| }) | ||||
| export class CoreShowPasswordComponent implements OnInit, AfterViewInit { | ||||
| 
 | ||||
|     @Input() name?: string; // Name of the input affected.
 | ||||
|     @Input() initialShown?: boolean | string; // Whether the password should be shown at start.
 | ||||
|     @ContentChild(IonInput) ionInput?: IonInput; | ||||
| 
 | ||||
|     shown = false; // Whether the password is shown.
 | ||||
|     @Input() name = ''; // Deprecated. Not used anymore.
 | ||||
|     @ContentChild(IonInput) ionInput?: IonInput | HTMLIonInputElement; // Deprecated. Use slot instead.
 | ||||
| 
 | ||||
|     protected input?: HTMLInputElement; // Input affected.
 | ||||
|     protected element: HTMLElement; // Current element.
 | ||||
|     protected input?: HTMLInputElement; | ||||
|     protected hostElement: HTMLElement; | ||||
|     protected logger: CoreLogger; | ||||
| 
 | ||||
|     constructor(element: ElementRef) { | ||||
|         this.element = element.nativeElement; | ||||
|         this.hostElement = element.nativeElement; | ||||
|         this.logger = CoreLogger.getInstance('CoreShowPasswordComponent'); | ||||
|     } | ||||
| 
 | ||||
|     get shown(): boolean { | ||||
|         return this.input?.type === 'text'; | ||||
|     } | ||||
| 
 | ||||
|     set shown(shown: boolean) { | ||||
|         if (!this.input) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.input.type = shown ? 'text' : 'password'; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -64,28 +90,12 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit { | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async ngAfterViewInit(): Promise<void> { | ||||
|         if (this.ionInput) { | ||||
|             try { | ||||
|                 // It's an ion-input, use it to get the native element.
 | ||||
|                 this.input = await this.ionInput.getInputElement(); | ||||
|                 this.setData(this.input); | ||||
|             } catch (error) { | ||||
|                 // This should never fail, but it does in some testing environment because Ionic elements are not
 | ||||
|                 // rendered properly. So in case this fails, we'll just ignore the error.
 | ||||
|             } | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // Search the input.
 | ||||
|         this.input = this.element.querySelector<HTMLInputElement>('input[name="' + this.name + '"]') ?? undefined; | ||||
|         await this.setInputElement(); | ||||
| 
 | ||||
|         if (!this.input) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.setData(this.input); | ||||
| 
 | ||||
|         // By default, don't autocapitalize and autocorrect.
 | ||||
|         if (!this.input.getAttribute('autocorrect')) { | ||||
|             this.input.setAttribute('autocorrect', 'off'); | ||||
| @ -96,12 +106,33 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Set label, icon name and input type. | ||||
|      * | ||||
|      * @param input The input element. | ||||
|      * Set the input element to affect. | ||||
|      */ | ||||
|     protected setData(input: HTMLInputElement): void { | ||||
|         input.type = this.shown ? 'text' : 'password'; | ||||
|     protected async setInputElement(): Promise<void> { | ||||
|         if (!this.ionInput) { | ||||
|             this.ionInput = this.hostElement.closest('ion-input') ?? undefined; | ||||
| 
 | ||||
|             this.hostElement.setAttribute('slot', 'end'); | ||||
|         } else { | ||||
|             // It's outside ion-input, warn devs.
 | ||||
|             this.logger.warn('Deprecated CoreShowPasswordComponent usage, it\'s not needed to surround ion-input anymore.'); | ||||
|         } | ||||
| 
 | ||||
|         if (!this.ionInput) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             this.input = await this.ionInput.getInputElement(); | ||||
|         } catch { | ||||
|             // This should never fail, but it does in some testing environment because Ionic elements are not
 | ||||
|             // rendered properly. So in case this fails it will try to find through the name and ignore the error.
 | ||||
|             const name = this.ionInput.name; | ||||
|             if (!name) { | ||||
|                 return; | ||||
|             } | ||||
|             this.input = this.hostElement.querySelector<HTMLInputElement>('input[name="' + name + '"]') ?? undefined; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -110,7 +141,7 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit { | ||||
|      * @param event The mouse event. | ||||
|      */ | ||||
|     toggle(event: Event): void { | ||||
|         if (event.type == 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) { | ||||
|         if (event.type === 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
| @ -120,13 +151,8 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit { | ||||
|         const isFocused = document.activeElement === this.input; | ||||
|         this.shown = !this.shown; | ||||
| 
 | ||||
|         if (!this.input) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         this.setData(this.input); | ||||
|         // In Android, the keyboard is closed when the input type changes. Focus it again.
 | ||||
|         if (isFocused && CorePlatform.isAndroid()) { | ||||
|         if (this.input && isFocused && CorePlatform.isAndroid()) { | ||||
|             CoreDomUtils.focusElement(this.input); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -18,15 +18,9 @@ | ||||
| 
 | ||||
|     ion-button.core-button-as-link { | ||||
|         --color: var(--core-login-text-color); | ||||
|         text-decoration-color: var(--core-login-text-color); | ||||
| 
 | ||||
|         ion-label { | ||||
|             color: var(--core-login-text-color); | ||||
|         } | ||||
|         text-decoration-color: var(--color); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     .core-login-reconnect-warning { | ||||
|         margin: 0px 0px 32px 0px; | ||||
|     } | ||||
|  | ||||
| @ -47,11 +47,11 @@ | ||||
|                             required="true" [attr.aria-label]="'core.login.username' | translate " /> | ||||
|                     </ion-item> | ||||
|                     <ion-item class="ion-margin-bottom" lines="inset"> | ||||
|                         <core-show-password name="password"> | ||||
|                             <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||
|                                 formControlName="password" [clearOnEdit]="false" autocomplete="current-password" enterkeyhint="go" | ||||
|                                 required="true" [attr.aria-label]="'core.login.password' | translate " /> | ||||
|                         </core-show-password> | ||||
|                         <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||
|                             formControlName="password" [clearOnEdit]="false" autocomplete="current-password" enterkeyhint="go" | ||||
|                             required="true" [attr.aria-label]="'core.login.password' | translate "> | ||||
|                             <core-show-password slot="end" /> | ||||
|                         </ion-input> | ||||
|                     </ion-item> | ||||
|                     <ion-button expand="block" type="submit" [disabled]="!credForm.valid" | ||||
|                         class="ion-margin core-login-login-button ion-text-wrap"> | ||||
|  | ||||
| @ -104,13 +104,12 @@ | ||||
|                         <core-input-errors [control]="signupForm.controls.username" [errorMessages]="usernameErrors" /> | ||||
|                     </ion-item> | ||||
|                     <ion-item class="ion-text-wrap"> | ||||
|                         <ion-label position="stacked"> | ||||
|                             <p [core-mark-required]="true">{{ 'core.login.password' | translate }}</p> | ||||
|                         </ion-label> | ||||
|                         <core-show-password name="password"> | ||||
|                             <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||
|                                 formControlName="password" [clearOnEdit]="false" autocomplete="new-password" required="true" /> | ||||
|                         </core-show-password> | ||||
|                         <ion-input labelPlacement="stacked" name="password" type="password" | ||||
|                             placeholder="{{ 'core.login.password' | translate }}" formControlName="password" [clearOnEdit]="false" | ||||
|                             autocomplete="new-password" required="true"> | ||||
|                             <div slot="label" [core-mark-required]="true">{{ 'core.login.password' | translate }}</div> | ||||
|                             <core-show-password slot="end" /> | ||||
|                         </ion-input> | ||||
|                         <p *ngIf="settings.passwordpolicy" class="core-input-footnote"> | ||||
|                             {{settings.passwordpolicy}} | ||||
|                         </p> | ||||
|  | ||||
| @ -58,12 +58,12 @@ | ||||
|             <div class="core-login-methods"> | ||||
|                 <form *ngIf="!isBrowserSSO" [formGroup]="credForm" (ngSubmit)="login($event)" class="core-login-form" #reconnectForm> | ||||
|                     <ion-item class="ion-margin-bottom" lines="inset"> | ||||
|                         <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" | ||||
|                                 [attr.aria-label]="'core.login.password' | translate" /> | ||||
|                         </core-show-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" | ||||
|                             [attr.aria-label]="'core.login.password' | translate"> | ||||
|                             <core-show-password slot="end" /> | ||||
|                         </ion-input> | ||||
|                     </ion-item> | ||||
|                     <ion-button type="submit" expand="block" [disabled]="!credForm.valid" | ||||
|                         class="ion-margin core-login-login-button ion-text-wrap"> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user