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"> |                 <ion-card *ngIf="askPassword"> | ||||||
|                     <form (ngSubmit)="submitPassword($event, passwordinput)" #passwordForm> |                     <form (ngSubmit)="submitPassword($event, passwordinput)" #passwordForm> | ||||||
|                         <ion-item class="ion-text-wrap"> |                         <ion-item class="ion-text-wrap"> | ||||||
|                             <ion-label position="stacked">{{ 'addon.mod_lesson.enterpassword' | translate }}</ion-label> |                             <ion-input labelPlacement="stacked" name="password" type="password" | ||||||
|                             <core-show-password name="password"> |                                 placeholder="{{ 'core.login.password' | translate }}" core-auto-focus #passwordinput [clearOnEdit]="false" | ||||||
|                                 <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" |                                 [label]="'addon.mod_lesson.enterpassword' | translate"> | ||||||
|                                     core-auto-focus #passwordinput [clearOnEdit]="false" /> |                                 <core-show-password slot="end" /> | ||||||
|                             </core-show-password> |                             </ion-input> | ||||||
|                         </ion-item> |                         </ion-item> | ||||||
|                         <ion-button expand="block" type="submit"> |                         <ion-button expand="block" type="submit"> | ||||||
|                             {{ 'addon.mod_lesson.continue' | translate }} |                             {{ 'addon.mod_lesson.continue' | translate }} | ||||||
|  | |||||||
| @ -5,9 +5,9 @@ | |||||||
|     </ion-label> |     </ion-label> | ||||||
| </ion-item> | </ion-item> | ||||||
| <ion-item [formGroup]="form"> | <ion-item [formGroup]="form"> | ||||||
|     <ion-label class="sr-only">{{ 'addon.mod_quiz.quizpassword' | translate }}</ion-label> |     <ion-input id="addon-mod_quiz-accessrule-password-input" name="quizpassword" type="password" | ||||||
|     <core-show-password name="quizpassword"> |         placeholder="{{ 'addon.mod_quiz.quizpassword' | translate }}" [formControlName]="'quizpassword'" [clearOnEdit]="false" | ||||||
|         <ion-input id="addon-mod_quiz-accessrule-password-input" name="quizpassword" type="password" |         [attr.aria-label]="'addon.mod_quiz.quizpassword' | translate"> | ||||||
|             placeholder="{{ 'addon.mod_quiz.quizpassword' | translate }}" [formControlName]="'quizpassword'" [clearOnEdit]="false" /> |         <core-show-password slot="end" /> | ||||||
|     </core-show-password> |     </ion-input> | ||||||
| </ion-item> | </ion-item> | ||||||
|  | |||||||
| @ -14,11 +14,11 @@ | |||||||
|     <form (ngSubmit)="submitPassword($event)" #passwordForm> |     <form (ngSubmit)="submitPassword($event)" #passwordForm> | ||||||
|         <div> |         <div> | ||||||
|             <ion-item> |             <ion-item> | ||||||
|                 <core-show-password name="password"> |                 <ion-input [attr.aria-label]="placeholder | translate" class="ion-text-wrap core-ioninput-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 | ||||||
|                         type="password" placeholder="{{ placeholder | translate }}" [(ngModel)]="password" core-auto-focus |                     [clearOnEdit]="false"> | ||||||
|                         [clearOnEdit]="false" /> |                     <core-show-password slot="end" /> | ||||||
|                 </core-show-password> |                 </ion-input> | ||||||
|             </ion-item> |             </ion-item> | ||||||
|             <ion-item *ngIf="error" class="ion-text-wrap ion-padding-top text-danger"> |             <ion-item *ngIf="error" class="ion-text-wrap ion-padding-top text-danger"> | ||||||
|                 <core-format-text [text]="error | translate" /> |                 <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)" | <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)"> |     (mousedown)="doNotBlur($event)" (keydown)="doNotBlur($event)" (keyup)="toggle($event)"> | ||||||
|     <ion-icon [name]="shown ? 'fas-eye-slash' : 'fas-eye'" slot="icon-only" aria-hidden="true" /> |     <ion-icon [name]="shown ? 'fas-eye-slash' : 'fas-eye'" slot="icon-only" aria-hidden="true" /> | ||||||
|  | |||||||
| @ -3,30 +3,29 @@ | |||||||
| :host { | :host { | ||||||
|     display: contents; |     display: contents; | ||||||
| 
 | 
 | ||||||
|     ion-button { |     // Only applies to deprecated way (surrounding). | ||||||
|  |     ::ng-deep ion-input + ion-button { | ||||||
|         background: transparent; |         background: transparent; | ||||||
|         padding: 0 calc(var(--padding-start) / 2); |         padding: 0 var(--inner-padding-end) 0 4px; | ||||||
|         position: absolute; |  | ||||||
|         @include safe-area-position(null, 0px, null, null); |  | ||||||
|         margin-top: 0; |         margin-top: 0; | ||||||
|         margin-bottom: 0; |         margin-bottom: 0; | ||||||
|         z-index: 3; |         position: absolute; | ||||||
|         bottom: 0; |         @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 { | ion-button { | ||||||
|     --padding-end: 47px !important; |     z-index: 5; | ||||||
| } |     pointer-events: visible; | ||||||
| 
 |  | ||||||
| :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; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,18 +18,31 @@ import { IonInput } from '@ionic/angular'; | |||||||
| import { CorePlatform } from '@services/platform'; | import { CorePlatform } from '@services/platform'; | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| import { CoreUtils } from '@services/utils/utils'; | 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 |  * @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> |  *     <ion-input type="password" name="password"></ion-input> | ||||||
|  * </core-show-password> |  * </core-show-password> | ||||||
|  */ |  */ | ||||||
| @ -40,17 +53,30 @@ import { CoreUtils } from '@services/utils/utils'; | |||||||
| }) | }) | ||||||
| export class CoreShowPasswordComponent implements OnInit, AfterViewInit { | 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.
 |     @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 input?: HTMLInputElement; | ||||||
|     protected element: HTMLElement; // Current element.
 |     protected hostElement: HTMLElement; | ||||||
|  |     protected logger: CoreLogger; | ||||||
| 
 | 
 | ||||||
|     constructor(element: ElementRef) { |     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 |      * @inheritdoc | ||||||
|      */ |      */ | ||||||
|     async ngAfterViewInit(): Promise<void> { |     async ngAfterViewInit(): Promise<void> { | ||||||
|         if (this.ionInput) { |         await this.setInputElement(); | ||||||
|             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; |  | ||||||
| 
 | 
 | ||||||
|         if (!this.input) { |         if (!this.input) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         this.setData(this.input); |  | ||||||
| 
 |  | ||||||
|         // By default, don't autocapitalize and autocorrect.
 |         // By default, don't autocapitalize and autocorrect.
 | ||||||
|         if (!this.input.getAttribute('autocorrect')) { |         if (!this.input.getAttribute('autocorrect')) { | ||||||
|             this.input.setAttribute('autocorrect', 'off'); |             this.input.setAttribute('autocorrect', 'off'); | ||||||
| @ -96,12 +106,33 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Set label, icon name and input type. |      * Set the input element to affect. | ||||||
|      * |  | ||||||
|      * @param input The input element. |  | ||||||
|      */ |      */ | ||||||
|     protected setData(input: HTMLInputElement): void { |     protected async setInputElement(): Promise<void> { | ||||||
|         input.type = this.shown ? 'text' : 'password'; |         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. |      * @param event The mouse event. | ||||||
|      */ |      */ | ||||||
|     toggle(event: Event): void { |     toggle(event: Event): void { | ||||||
|         if (event.type == 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) { |         if (event.type === 'keyup' && !this.isValidKeyboardKey(<KeyboardEvent>event)) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -120,13 +151,8 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit { | |||||||
|         const isFocused = document.activeElement === this.input; |         const isFocused = document.activeElement === this.input; | ||||||
|         this.shown = !this.shown; |         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.
 |         // 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); |             CoreDomUtils.focusElement(this.input); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -18,15 +18,9 @@ | |||||||
| 
 | 
 | ||||||
|     ion-button.core-button-as-link { |     ion-button.core-button-as-link { | ||||||
|         --color: var(--core-login-text-color); |         --color: var(--core-login-text-color); | ||||||
|         text-decoration-color: var(--core-login-text-color); |         text-decoration-color: var(--color); | ||||||
| 
 |  | ||||||
|         ion-label { |  | ||||||
|             color: var(--core-login-text-color); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     .core-login-reconnect-warning { |     .core-login-reconnect-warning { | ||||||
|         margin: 0px 0px 32px 0px; |         margin: 0px 0px 32px 0px; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -47,11 +47,11 @@ | |||||||
|                             required="true" [attr.aria-label]="'core.login.username' | translate " /> |                             required="true" [attr.aria-label]="'core.login.username' | translate " /> | ||||||
|                     </ion-item> |                     </ion-item> | ||||||
|                     <ion-item class="ion-margin-bottom" lines="inset"> |                     <ion-item class="ion-margin-bottom" lines="inset"> | ||||||
|                         <core-show-password name="password"> |                         <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||||
|                             <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" |                             formControlName="password" [clearOnEdit]="false" autocomplete="current-password" enterkeyhint="go" | ||||||
|                                 formControlName="password" [clearOnEdit]="false" autocomplete="current-password" enterkeyhint="go" |                             required="true" [attr.aria-label]="'core.login.password' | translate "> | ||||||
|                                 required="true" [attr.aria-label]="'core.login.password' | translate " /> |                             <core-show-password slot="end" /> | ||||||
|                         </core-show-password> |                         </ion-input> | ||||||
|                     </ion-item> |                     </ion-item> | ||||||
|                     <ion-button expand="block" type="submit" [disabled]="!credForm.valid" |                     <ion-button expand="block" type="submit" [disabled]="!credForm.valid" | ||||||
|                         class="ion-margin core-login-login-button ion-text-wrap"> |                         class="ion-margin core-login-login-button ion-text-wrap"> | ||||||
|  | |||||||
| @ -104,13 +104,12 @@ | |||||||
|                         <core-input-errors [control]="signupForm.controls.username" [errorMessages]="usernameErrors" /> |                         <core-input-errors [control]="signupForm.controls.username" [errorMessages]="usernameErrors" /> | ||||||
|                     </ion-item> |                     </ion-item> | ||||||
|                     <ion-item class="ion-text-wrap"> |                     <ion-item class="ion-text-wrap"> | ||||||
|                         <ion-label position="stacked"> |                         <ion-input labelPlacement="stacked" name="password" type="password" | ||||||
|                             <p [core-mark-required]="true">{{ 'core.login.password' | translate }}</p> |                             placeholder="{{ 'core.login.password' | translate }}" formControlName="password" [clearOnEdit]="false" | ||||||
|                         </ion-label> |                             autocomplete="new-password" required="true"> | ||||||
|                         <core-show-password name="password"> |                             <div slot="label" [core-mark-required]="true">{{ 'core.login.password' | translate }}</div> | ||||||
|                             <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" |                             <core-show-password slot="end" /> | ||||||
|                                 formControlName="password" [clearOnEdit]="false" autocomplete="new-password" required="true" /> |                         </ion-input> | ||||||
|                         </core-show-password> |  | ||||||
|                         <p *ngIf="settings.passwordpolicy" class="core-input-footnote"> |                         <p *ngIf="settings.passwordpolicy" class="core-input-footnote"> | ||||||
|                             {{settings.passwordpolicy}} |                             {{settings.passwordpolicy}} | ||||||
|                         </p> |                         </p> | ||||||
|  | |||||||
| @ -58,12 +58,12 @@ | |||||||
|             <div class="core-login-methods"> |             <div class="core-login-methods"> | ||||||
|                 <form *ngIf="!isBrowserSSO" [formGroup]="credForm" (ngSubmit)="login($event)" class="core-login-form" #reconnectForm> |                 <form *ngIf="!isBrowserSSO" [formGroup]="credForm" (ngSubmit)="login($event)" class="core-login-form" #reconnectForm> | ||||||
|                     <ion-item class="ion-margin-bottom" lines="inset"> |                     <ion-item class="ion-margin-bottom" lines="inset"> | ||||||
|                         <core-show-password name="password"> |                         <ion-input class="core-ioninput-password" name="password" type="password" | ||||||
|                             <ion-input class="core-ioninput-password" name="password" type="password" |                             placeholder="{{ 'core.login.password' | translate }}" formControlName="password" [clearOnEdit]="false" | ||||||
|                                 placeholder="{{ 'core.login.password' | translate }}" formControlName="password" [clearOnEdit]="false" |                             autocomplete="current-password" enterkeyhint="go" required="true" | ||||||
|                                 autocomplete="current-password" enterkeyhint="go" required="true" |                             [attr.aria-label]="'core.login.password' | translate"> | ||||||
|                                 [attr.aria-label]="'core.login.password' | translate" /> |                             <core-show-password slot="end" /> | ||||||
|                         </core-show-password> |                         </ion-input> | ||||||
|                     </ion-item> |                     </ion-item> | ||||||
|                     <ion-button type="submit" expand="block" [disabled]="!credForm.valid" |                     <ion-button type="submit" expand="block" [disabled]="!credForm.valid" | ||||||
|                         class="ion-margin core-login-login-button ion-text-wrap"> |                         class="ion-margin core-login-login-button ion-text-wrap"> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user