diff --git a/core/api.txt b/core/api.txt index b6b11321b68..9cfc65f586f 100644 --- a/core/api.txt +++ b/core/api.txt @@ -563,6 +563,7 @@ ion-input,prop,enterkeyhint,"done" | "enter" | "go" | "next" | "previous" | "sea ion-input,prop,errorText,string | undefined,undefined,false,false ion-input,prop,fill,"outline" | "solid" | undefined,undefined,false,false ion-input,prop,helperText,string | undefined,undefined,false,false +ion-input,prop,hidePasswordIcon,string | undefined,undefined,false,false ion-input,prop,inputmode,"decimal" | "email" | "none" | "numeric" | "search" | "tel" | "text" | "url" | undefined,undefined,false,false ion-input,prop,label,string | undefined,undefined,false,false ion-input,prop,labelPlacement,"end" | "fixed" | "floating" | "stacked" | "start",'start',false,false @@ -578,6 +579,8 @@ ion-input,prop,placeholder,string | undefined,undefined,false,false ion-input,prop,readonly,boolean,false,false,false ion-input,prop,required,boolean,false,false,false ion-input,prop,shape,"round" | undefined,undefined,false,false +ion-input,prop,showPasswordIcon,string | undefined,undefined,false,false +ion-input,prop,showPasswordToggle,boolean,false,false,false ion-input,prop,spellcheck,boolean,false,false,false ion-input,prop,step,string | undefined,undefined,false,false ion-input,prop,type,"date" | "datetime-local" | "email" | "month" | "number" | "password" | "search" | "tel" | "text" | "time" | "url" | "week",'text',false,false diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 61f44b7dc1f..4e4f941a3d8 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1206,6 +1206,10 @@ export namespace Components { * Text that is placed under the input and displayed when no error is detected. */ "helperText"?: string; + /** + * Set the icon that can be used to represent hiding a password. Defaults to `eyeOff`. + */ + "hidePasswordIcon"?: string; /** * A hint to the browser for which keyboard to display. Possible values: `"none"`, `"text"`, `"tel"`, `"url"`, `"email"`, `"numeric"`, `"decimal"`, and `"search"`. */ @@ -1270,6 +1274,14 @@ export namespace Components { * The shape of the input. If "round" it will have an increased border radius. */ "shape"?: 'round'; + /** + * Set the icon that can be used to represent showing a password. Defaults to `eye`. + */ + "showPasswordIcon"?: string; + /** + * If `true`, a password toggle icon will appear in the input. Clicking the icon toggles the input type between `"text"` and `"password"`, allowing the user to view or hide the input value. + */ + "showPasswordToggle": boolean; /** * If `true`, the element will have its spelling and grammar checked. */ @@ -5902,6 +5914,10 @@ declare namespace LocalJSX { * Text that is placed under the input and displayed when no error is detected. */ "helperText"?: string; + /** + * Set the icon that can be used to represent hiding a password. Defaults to `eyeOff`. + */ + "hidePasswordIcon"?: string; /** * A hint to the browser for which keyboard to display. Possible values: `"none"`, `"text"`, `"tel"`, `"url"`, `"email"`, `"numeric"`, `"decimal"`, and `"search"`. */ @@ -5982,6 +5998,14 @@ declare namespace LocalJSX { * The shape of the input. If "round" it will have an increased border radius. */ "shape"?: 'round'; + /** + * Set the icon that can be used to represent showing a password. Defaults to `eye`. + */ + "showPasswordIcon"?: string; + /** + * If `true`, a password toggle icon will appear in the input. Clicking the icon toggles the input type between `"text"` and `"password"`, allowing the user to view or hide the input value. + */ + "showPasswordToggle"?: boolean; /** * If `true`, the element will have its spelling and grammar checked. */ diff --git a/core/src/components/input/input.ios.scss b/core/src/components/input/input.ios.scss index d17929f7016..a48a91b6980 100644 --- a/core/src/components/input/input.ios.scss +++ b/core/src/components/input/input.ios.scss @@ -9,7 +9,8 @@ font-size: $input-ios-font-size; } -.input-clear-icon ion-icon { +.input-clear-icon ion-icon, +.input-password-toggle ion-icon { width: 18px; height: 18px; } diff --git a/core/src/components/input/input.md.scss b/core/src/components/input/input.md.scss index f00e98db39a..6f0e62065df 100644 --- a/core/src/components/input/input.md.scss +++ b/core/src/components/input/input.md.scss @@ -15,7 +15,8 @@ font-size: $input-md-font-size; } -.input-clear-icon ion-icon { +.input-clear-icon ion-icon, +.input-password-toggle ion-icon { width: 22px; height: 22px; } @@ -42,7 +43,7 @@ } .input-bottom .counter { - letter-spacing: .0333333333em; + letter-spacing: 0.0333333333em; } // Input Label diff --git a/core/src/components/input/input.scss b/core/src/components/input/input.scss index b8a9cabf2dc..4ec2c1df98b 100644 --- a/core/src/components/input/input.scss +++ b/core/src/components/input/input.scss @@ -180,7 +180,8 @@ // Clear Input Icon // -------------------------------------------------- -.input-clear-icon { +.input-clear-icon, +.input-password-toggle { @include margin(auto); @include padding(0); @include background-position(center); @@ -202,11 +203,11 @@ color: $text-color-step-400; - visibility: hidden; appearance: none; } -:host(.in-item-color) .input-clear-icon { +:host(.in-item-color) .input-clear-icon, +:host(.in-item-color) .input-password-toggle { color: inherit; } @@ -217,12 +218,13 @@ * However, the clear button always disappears after * being activated, so we never get to that state. */ -.input-clear-icon:focus { +.input-clear-icon:focus, +.input-password-toggle:focus { opacity: 0.5; } -:host(.has-value) .input-clear-icon { - visibility: visible; +:host(:not(.has-value)) .input-clear-icon { + visibility: hidden; } // Input Wrapper @@ -399,7 +401,7 @@ */ max-width: 200px; - transition: color 150ms cubic-bezier(.4, 0, .2, 1), transform 150ms cubic-bezier(.4, 0, .2, 1); + transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1), transform 150ms cubic-bezier(0.4, 0, 0.2, 1); /** * This ensures that double tapping this text diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 46ffd83f0d0..833069b2954 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -7,7 +7,7 @@ import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnRea import { createSlotMutationController } from '@utils/slot-mutation-controller'; import type { SlotMutationController } from '@utils/slot-mutation-controller'; import { createColorClasses, hostContext } from '@utils/theme'; -import { closeCircle, closeSharp } from 'ionicons/icons'; +import { closeCircle, closeSharp, eyeOff, eye } from 'ionicons/icons'; import { getIonMode } from '../../global/ionic-global'; import type { AutocompleteTypes, Color, StyleEventDetail, TextFieldTypes } from '../../interface'; @@ -162,6 +162,11 @@ export class Input implements ComponentInterface { */ @Prop() helperText?: string; + /** + * Set the icon that can be used to represent hiding a password. Defaults to `eyeOff`. + */ + @Prop() hidePasswordIcon?: string; + /** * The visible label associated with the input. * @@ -238,6 +243,17 @@ export class Input implements ComponentInterface { */ @Prop() shape?: 'round'; + /** + * Set the icon that can be used to represent showing a password. Defaults to `eye`. + */ + @Prop() showPasswordIcon?: string; + + /** + * If `true`, a password toggle icon will appear in the input. + * Clicking the icon toggles the input type between `"text"` and `"password"`, allowing the user to view or hide the input value. + */ + @Prop() showPasswordToggle = false; + /** * If `true`, the element will have its spelling and grammar checked. */ @@ -252,7 +268,7 @@ export class Input implements ComponentInterface { /** * The type of control to display. The default type is text. */ - @Prop() type: TextFieldTypes = 'text'; + @Prop({ mutable: true }) type: TextFieldTypes = 'text'; /** * The value of the input. @@ -451,6 +467,7 @@ export class Input implements ComponentInterface { if (input) { this.value = input.value || ''; } + this.emitInputChange(ev); }; @@ -541,6 +558,10 @@ export class Input implements ComponentInterface { this.emitInputChange(ev); }; + private togglePasswordVisibility = () => { + this.type = this.type === 'text' ? 'password' : 'text'; + }; + private hasValue(): boolean { return this.getValue().length > 0; } @@ -562,7 +583,6 @@ export class Input implements ComponentInterface { return