license import { MDCComponent } from '@material/base/component' ; import { applyPassive } from '@material/dom/events' ; import * as ponyfill from '@material/dom/ponyfill' ; import { MDCFloatingLabel , MDCFloatingLabelFactory } from '@material/floating-label/component' ; import { MDCLineRipple , MDCLineRippleFactory } from '@material/line-ripple/component' ; import { MDCNotchedOutline , MDCNotchedOutlineFactory } from '@material/notched-outline/component' ; import { MDCRippleAdapter } from '@material/ripple/adapter' ; import { MDCRipple , MDCRippleFactory } from '@material/ripple/component' ; import { MDCRippleFoundation } from '@material/ripple/foundation' ; import { MDCRippleCapableSurface } from '@material/ripple/types' ; import { MDCTextFieldAdapter , MDCTextFieldInputAdapter , MDCTextFieldLabelAdapter , MDCTextFieldLineRippleAdapter , MDCTextFieldOutlineAdapter , MDCTextFieldRootAdapter } from './adapter' ; import { MDCTextFieldCharacterCounter , MDCTextFieldCharacterCounterFactory } from './character-counter/component' ; import { MDCTextFieldCharacterCounterFoundation } from './character-counter/foundation' ; import { cssClasses , strings } from './constants' ; import { MDCTextFieldFoundation } from './foundation' ; import { MDCTextFieldHelperText , MDCTextFieldHelperTextFactory } from './helper-text/component' ; import { MDCTextFieldHelperTextFoundation } from './helper-text/foundation' ; import { MDCTextFieldIcon , MDCTextFieldIconFactory } from './icon/component' ; import { MDCTextFieldFoundationMap } from './types' ; export class MDCTextField extends MDCComponent < MDCTextFieldFoundation > implements MDCRippleCapableSurface { static override attachTo ( root ) { return new MDCTextField ( root ) ; } ripple ! : MDCRipple | null ; private input ! : HTMLInputElement ; private characterCounter ! : MDCTextFieldCharacterCounter | null ; private helperText ! : MDCTextFieldHelperText | null ; private label ! : MDCFloatingLabel | null ; private leadingIcon ! : MDCTextFieldIcon | null ; private lineRipple ! : MDCLineRipple | null ; private outline ! : MDCNotchedOutline | null ; private trailingIcon ! : MDCTextFieldIcon | null ; private prefix ! : Element | null ; private suffix ! : Element | null ; override initialize ( rippleFactory = ( el , foundation ) => new MDCRipple ( el , foundation ) , lineRippleFactory = ( el ) => new MDCLineRipple ( el ) , helperTextFactory = ( el ) => new MDCTextFieldHelperText ( el ) , characterCounterFactory = ( el ) => new MDCTextFieldCharacterCounter ( el ) , iconFactory = ( el ) => new MDCTextFieldIcon ( el ) , labelFactory = ( el ) => new MDCFloatingLabel ( el ) , outlineFactory = ( el ) => new MDCNotchedOutline ( el ) ) { this . input = this . root . querySelector < HTMLInputElement > ( strings . INPUT_SELECTOR ) ! ; const labelElement = this . root . querySelector ( strings . LABEL_SELECTOR ) ; this . label = labelElement ? labelFactory ( labelElement ) : null ; const lineRippleElement = this . root . querySelector ( strings . LINE_RIPPLE_SELECTOR ) ; this . lineRipple = lineRippleElement ? lineRippleFactory ( lineRippleElement ) : null ; const outlineElement = this . root . querySelector ( strings . OUTLINE_SELECTOR ) ; this . outline = outlineElement ? outlineFactory ( outlineElement ) : null ; const helperTextStrings = MDCTextFieldHelperTextFoundation . strings ; const nextElementSibling = this . root . nextElementSibling ; const hasHelperLine = ( nextElementSibling && nextElementSibling . classList . contains ( cssClasses . HELPER_LINE ) ) ; const helperTextEl = hasHelperLine && nextElementSibling && nextElementSibling . querySelector ( helperTextStrings . ROOT_SELECTOR ) ; this . helperText = helperTextEl ? helperTextFactory ( helperTextEl ) : null ; const characterCounterStrings = MDCTextFieldCharacterCounterFoundation . strings ; let characterCounterEl = this . root . querySelector ( characterCounterStrings . ROOT_SELECTOR ) ; if ( ! characterCounterEl && hasHelperLine && nextElementSibling ) { characterCounterEl = nextElementSibling . querySelector ( characterCounterStrings . ROOT_SELECTOR ) ; } this . characterCounter = characterCounterEl ? characterCounterFactory ( characterCounterEl ) : null ; const leadingIconEl = this . root . querySelector ( strings . LEADING_ICON_SELECTOR ) ; this . leadingIcon = leadingIconEl ? iconFactory ( leadingIconEl ) : null ; const trailingIconEl = this . root . querySelector ( strings . TRAILING_ICON_SELECTOR ) ; this . trailingIcon = trailingIconEl ? iconFactory ( trailingIconEl ) : null ; this . prefix = this . root . querySelector ( strings . PREFIX_SELECTOR ) ; this . suffix = this . root . querySelector ( strings . SUFFIX_SELECTOR ) ; this . ripple = this . createRipple ( rippleFactory ) ; } override destroy ( ) { if ( this . ripple ) { this . ripple . destroy ( ) ; } if ( this . lineRipple ) { this . lineRipple . destroy ( ) ; } if ( this . helperText ) { this . helperText . destroy ( ) ; } if ( this . characterCounter ) { this . characterCounter . destroy ( ) ; } if ( this . leadingIcon ) { this . leadingIcon . destroy ( ) ; } if ( this . trailingIcon ) { this . trailingIcon . destroy ( ) ; } if ( this . label ) { this . label . destroy ( ) ; } if ( this . outline ) { this . outline . destroy ( ) ; } super . destroy ( ) ; } /**
     * Initializes the Text Field's internal state based on the environment's
     * state.
     */ override initialSyncWithDOM ( ) { this . disabled = this . input . disabled ; } get value ( ) : string { return this . foundation . getValue ( ) ; } param value set value ( value ) { this . foundation . setValue ( value ) ; } get disabled ( ) : boolean { return this . foundation . isDisabled ( ) ; } param disabled set disabled ( disabled ) { this . foundation . setDisabled ( disabled ) ; } get valid ( ) : boolean { return this . foundation . isValid ( ) ; } param valid set valid ( valid ) { this . foundation . setValid ( valid ) ; } get required ( ) : boolean { return this . input . required ; } param required set required ( required ) { this . input . required = required ; } get pattern ( ) : string { return this . input . pattern ; } param pattern set pattern ( pattern ) { this . input . pattern = pattern ; } get minLength ( ) : number { return this . input . minLength ; } param minLength set minLength ( minLength ) { this . input . minLength = minLength ; } get maxLength ( ) : number { return this . input . maxLength ; } param maxLength set maxLength ( maxLength ) { if ( maxLength < 0 ) { this . input . removeAttribute ( 'maxLength' ) ; } else { this . input . maxLength = maxLength ; } } get min ( ) : string { return this . input . min ; } param min set min ( min ) { this . input . min = min ; } get max ( ) : string { return this . input . max ; } param max set max ( max ) { this . input . max = max ; } get step ( ) : string { return this . input . step ; } param step set step ( step ) { this . input . step = step ; } /**
     * Sets the helper text element content.
     */ set helperTextContent ( content ) { this . foundation . setHelperTextContent ( content ) ; } /**
     * Sets the aria label of the leading icon.
     */ set leadingIconAriaLabel ( label ) { this . foundation . setLeadingIconAriaLabel ( label ) ; } /**
     * Sets the text content of the leading icon.
     */ set leadingIconContent ( content ) { this . foundation . setLeadingIconContent ( content ) ; } /**
     * Sets the aria label of the trailing icon.
     */ set trailingIconAriaLabel ( label ) { this . foundation . setTrailingIconAriaLabel ( label ) ; } /**
     * Sets the text content of the trailing icon.
     */ set trailingIconContent ( content ) { this . foundation . setTrailingIconContent ( content ) ; } param useNativeValidation set useNativeValidation ( useNativeValidation ) { this . foundation . setUseNativeValidation ( useNativeValidation ) ; } /**
     * Gets the text content of the prefix, or null if it does not exist.
     */ get prefixText ( ) : string | null { return this . prefix ? this . prefix . textContent : null ; } /**
     * Sets the text content of the prefix, if it exists.
     */ set prefixText ( prefixText ) { if ( this . prefix ) { this . prefix . textContent = prefixText ; } } /**
     * Gets the text content of the suffix, or null if it does not exist.
     */ get suffixText ( ) : string | null { return this . suffix ? this . suffix . textContent : null ; } /**
     * Sets the text content of the suffix, if it exists.
     */ set suffixText ( suffixText ) { if ( this . suffix ) { this . suffix . textContent = suffixText ; } } /**
     * Focuses the input element.
     */ focus ( ) { this . input . focus ( ) ; } /**
     * Recomputes the outline SVG path for the outline element.
     */ layout ( ) { const openNotch = this . foundation . shouldFloat ; this . foundation . notchOutline ( openNotch ) ; } override getDefaultFoundation ( ) { const adapter = { ... this . getRootAdapterMethods ( ) , ... this . getInputAdapterMethods ( ) , ... this . getLabelAdapterMethods ( ) , ... this . getLineRippleAdapterMethods ( ) , ... this . getOutlineAdapterMethods ( ) , } ; return new MDCTextFieldFoundation ( adapter , this . getFoundationMap ( ) ) ; } private getRootAdapterMethods ( ) { return { addClass : ( className ) => this . root . classList . add ( className ) , removeClass : ( className ) => this . root . classList . remove ( className ) , hasClass : ( className ) => this . root . classList . contains ( className ) , registerTextFieldInteractionHandler : ( evtType , handler ) => { this . listen ( evtType , handler ) ; } , deregisterTextFieldInteractionHandler : ( evtType , handler ) => { this . unlisten ( evtType , handler ) ; } , registerValidationAttributeChangeHandler : ( handler ) => { const getAttributesList = ( mutationsList ) : string [ ] => { return mutationsList . map ( ( mutation ) => mutation . attributeName ) . filter ( ( attributeName ) => attributeName ) as string [ ] ; } ; const observer = new MutationObserver ( ( mutationsList ) => handler ( getAttributesList ( mutationsList ) ) ) ; const config = { attributes : true } ; observer . observe ( this . input , config ) ; return observer ; } , deregisterValidationAttributeChangeHandler : ( observer ) => { observer . disconnect ( ) ; } , } ; } private getInputAdapterMethods ( ) { return { getNativeInput : ( ) => this . input , setInputAttr : ( attr , value ) => { this . input . setAttribute ( attr , value ) ; } , removeInputAttr : ( attr ) => { this . input . removeAttribute ( attr ) ; } , isFocused : ( ) => document . activeElement === this . input , registerInputInteractionHandler : ( evtType , handler ) => { this . input . addEventListener ( evtType , handler , applyPassive ( ) ) ; } , deregisterInputInteractionHandler : ( evtType , handler ) => { this . input . removeEventListener ( evtType , handler , applyPassive ( ) ) ; } , } ; } private getLabelAdapterMethods ( ) { return { floatLabel : ( shouldFloat ) => { this . label && this . label . float ( shouldFloat ) ; } , getLabelWidth : ( ) => this . label ? this . label . getWidth ( ) : 0 , hasLabel : ( ) => Boolean ( this . label ) , shakeLabel : ( shouldShake ) => { this . label && this . label . shake ( shouldShake ) ; } , setLabelRequired : ( isRequired ) => { this . label && this . label . setRequired ( isRequired ) ; } , } ; } private getLineRippleAdapterMethods ( ) { return { activateLineRipple : ( ) => { if ( this . lineRipple ) { this . lineRipple . activate ( ) ; } } , deactivateLineRipple : ( ) => { if ( this . lineRipple ) { this . lineRipple . deactivate ( ) ; } } , setLineRippleTransformOrigin : ( normalizedX ) => { if ( this . lineRipple ) { this . lineRipple . setRippleCenter ( normalizedX ) ; } } , } ; } private getOutlineAdapterMethods ( ) { return { closeOutline : ( ) => { this . outline && this . outline . closeNotch ( ) ; } , hasOutline : ( ) => Boolean ( this . outline ) , notchOutline : ( labelWidth ) => { this . outline && this . outline . notch ( labelWidth ) ; } , } ; } return private getFoundationMap ( ) { return { characterCounter : this . characterCounter ? this . characterCounter . foundationForTextField : undefined , helperText : this . helperText ? this . helperText . foundationForTextField : undefined , leadingIcon : this . leadingIcon ? this . leadingIcon . foundationForTextField : undefined , trailingIcon : this . trailingIcon ? this . trailingIcon . foundationForTextField : undefined , } ; } private createRipple ( rippleFactory ) { const isTextArea = this . root . classList . contains ( cssClasses . TEXTAREA ) ; const isOutlined = this . root . classList . contains ( cssClasses . OUTLINED ) ; if ( isTextArea || isOutlined ) { return null ; } const adapter = { ... MDCRipple . createAdapter ( this ) , isSurfaceActive : ( ) => ponyfill . matches ( this . input , ':active' ) , registerInteractionHandler : ( evtType , handler ) => { this . input . addEventListener ( evtType , handler , applyPassive ( ) ) ; } , deregisterInteractionHandler : ( evtType , handler ) => { this . input . removeEventListener ( evtType , handler , applyPassive ( ) ) ; } , } ; return rippleFactory ( this . root , new MDCRippleFoundation ( adapter ) ) ; } }