import { TextFieldBase } from "@material/mwc-textfield/mwc-textfield-base"; import { styles } from "@material/mwc-textfield/mwc-textfield.css"; import type { TemplateResult, PropertyValues } from "lit"; import { html, css } from "lit"; import { customElement, property, query } from "lit/decorators"; import { mainWindow } from "../common/dom/get_main_window"; @customElement("ha-textfield") export class HaTextField extends TextFieldBase { @property({ type: Boolean }) public invalid?: boolean; @property({ attribute: "error-message" }) public errorMessage?: string; // @ts-ignore @property({ type: Boolean }) public icon = false; // @ts-ignore // eslint-disable-next-line lit/attribute-names @property({ type: Boolean }) public iconTrailing = false; @property() public autocomplete?: string; @property({ type: Boolean }) public autocorrect = true; @property({ attribute: "input-spellcheck" }) public inputSpellcheck?: string; @query("input") public formElement!: HTMLInputElement; override updated(changedProperties: PropertyValues) { super.updated(changedProperties); if ( changedProperties.has("invalid") || changedProperties.has("errorMessage") ) { this.setCustomValidity( this.invalid ? this.errorMessage || this.validationMessage || "Invalid" : "" ); if ( this.invalid || this.validateOnInitialRender || (changedProperties.has("invalid") && changedProperties.get("invalid") !== undefined) ) { // Only report validity if the field is invalid or the invalid state has changed from // true to false to prevent setting empty required fields to invalid on first render this.reportValidity(); } } if (changedProperties.has("autocomplete")) { if (this.autocomplete) { this.formElement.setAttribute("autocomplete", this.autocomplete); } else { this.formElement.removeAttribute("autocomplete"); } } if (changedProperties.has("autocorrect")) { if (this.autocorrect === false) { this.formElement.setAttribute("autocorrect", "off"); } else { this.formElement.removeAttribute("autocorrect"); } } if (changedProperties.has("inputSpellcheck")) { if (this.inputSpellcheck) { this.formElement.setAttribute("spellcheck", this.inputSpellcheck); } else { this.formElement.removeAttribute("spellcheck"); } } } protected override renderIcon( _icon: string, isTrailingIcon = false ): TemplateResult { const type = isTrailingIcon ? "trailing" : "leading"; return html` `; } static override styles = [ styles, css` .mdc-text-field__input { width: var(--ha-textfield-input-width, 100%); } .mdc-text-field:not(.mdc-text-field--with-leading-icon) { padding: var(--text-field-padding, 0px 16px); } .mdc-text-field__affix--suffix { padding-left: var(--text-field-suffix-padding-left, 12px); padding-right: var(--text-field-suffix-padding-right, 0px); padding-inline-start: var(--text-field-suffix-padding-left, 12px); padding-inline-end: var(--text-field-suffix-padding-right, 0px); direction: ltr; } .mdc-text-field--with-leading-icon { padding-inline-start: var(--text-field-suffix-padding-left, 0px); padding-inline-end: var(--text-field-suffix-padding-right, 16px); direction: var(--direction); } .mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon { padding-left: var(--text-field-suffix-padding-left, 0px); padding-right: var(--text-field-suffix-padding-right, 0px); padding-inline-start: var(--text-field-suffix-padding-left, 0px); padding-inline-end: var(--text-field-suffix-padding-right, 0px); } .mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--suffix { color: var(--secondary-text-color); } .mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon { color: var(--secondary-text-color); } .mdc-text-field__icon--leading { margin-inline-start: 16px; margin-inline-end: 8px; direction: var(--direction); } .mdc-text-field__icon--trailing { padding: var(--textfield-icon-trailing-padding, 12px); } .mdc-floating-label:not(.mdc-floating-label--float-above) { max-width: calc(100% - 16px); } .mdc-floating-label--float-above { max-width: calc((100% - 16px) / 0.75); transition: none; } input { text-align: var(--text-field-text-align, start); } input[type="color"] { height: 20px; } /* Edge, hide reveal password icon */ ::-ms-reveal { display: none; } /* Chrome, Safari, Edge, Opera */ :host([no-spinner]) input::-webkit-outer-spin-button, :host([no-spinner]) input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; } /* Firefox */ :host([no-spinner]) input[type="number"] { -moz-appearance: textfield; } .mdc-text-field__ripple { overflow: hidden; } .mdc-text-field { overflow: var(--text-field-overflow); } .mdc-floating-label { padding-inline-end: 16px; padding-inline-start: initial; inset-inline-start: 16px !important; inset-inline-end: initial !important; transform-origin: var(--float-start); direction: var(--direction); text-align: var(--float-start); box-sizing: border-box; text-overflow: ellipsis; } .mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label { max-width: calc( 100% - 48px - var(--text-field-suffix-padding-left, 0px) ); inset-inline-start: calc( 48px + var(--text-field-suffix-padding-left, 0px) ) !important; inset-inline-end: initial !important; direction: var(--direction); } .mdc-text-field__input[type="number"] { direction: var(--direction); } .mdc-text-field__affix--prefix { padding-right: var(--text-field-prefix-padding-right, 2px); padding-inline-end: var(--text-field-prefix-padding-right, 2px); padding-inline-start: initial; } .mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--prefix { color: var(--mdc-text-field-label-ink-color); } #helper-text ha-markdown { display: inline-block; } `, // safari workaround - must be explicit mainWindow.document.dir === "rtl" ? css` .mdc-text-field--with-leading-icon, .mdc-text-field__icon--leading, .mdc-floating-label, .mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label, .mdc-text-field__input[type="number"] { direction: rtl; --direction: rtl; } ` : css``, ]; } declare global { interface HTMLElementTagNameMap { "ha-textfield": HaTextField; } }