MOBILE-3947 core: Slot core-show-password on ion-inputs
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…
Reference in New Issue