import { VerticalTextAlignment , verticalTextAlignmentProperty } from '@nativescript-community/text' ; import { themer } from '@nativescript-community/ui-material-core' ; import { buttonColorProperty , counterMaxLengthProperty , digitsProperty , errorColorProperty , errorProperty , floatingColorProperty , floatingInactiveColorProperty , floatingProperty , helperColorProperty , helperProperty , strokeColorProperty , strokeDisabledColorProperty , strokeInactiveColorProperty } from '@nativescript-community/ui-material-core/textbase/cssproperties' ; import { Background , Color , Font , Style , Utils , _updateCharactersInRangeReplacementString , backgroundInternalProperty , editableProperty , fontInternalProperty , paddingBottomProperty , paddingLeftProperty , paddingRightProperty , paddingTopProperty , placeholderColorProperty } from '@nativescript/core' ; import { textProperty } from '@nativescript/core/ui/text-base' ; import { layout } from '@nativescript/core/utils' ; import { TextFieldBase } from './textfield.common' ; @ NativeClass class TextInputControllerUnderlineImpl extends MDCTextInputControllerUnderline { _owner : WeakRef < TextField > ; public static initWithOwner ( owner ) { const delegate = TextInputControllerUnderlineImpl . new ( ) as TextInputControllerUnderlineImpl ; delegate . _owner = new WeakRef ( owner ) ; return delegate ; } textInsetsWithSizeThatFitsWidthHint ( defaultValue , widthHint ) { let result = super . textInsetsWithSizeThatFitsWidthHint ( defaultValue , widthHint ) ; const owner = this . _owner ? this . _owner . get ( ) : null ; if ( owner ) { result = owner . _getTextInsetsForBounds ( result ) ; } return result ; } } @ NativeClass class TextInputControllerImpl extends MDCTextInputControllerBase { _owner : WeakRef < TextField > ; public static initWithOwner ( owner ) { const delegate = TextInputControllerImpl . new ( ) as TextInputControllerImpl ; delegate . _owner = new WeakRef ( owner ) ; return delegate ; } textInsetsWithSizeThatFitsWidthHint ( defaultValue , widthHint ) { let result = super . textInsetsWithSizeThatFitsWidthHint ( defaultValue , widthHint ) ; const owner = this . _owner ? this . _owner . get ( ) : null ; if ( owner ) { result = owner . _getTextInsetsForBounds ( result ) ; } return result ; } } @ NativeClass class TextInputControllerOutlinedImpl extends MDCTextInputControllerOutlined { _owner : WeakRef < TextField > ; public static initWithOwner ( owner ) { const delegate = TextInputControllerOutlinedImpl . new ( ) as TextInputControllerOutlinedImpl ; delegate . _owner = new WeakRef ( owner ) ; return delegate ; } textInsetsWithSizeThatFitsWidthHint ( defaultValue , widthHint ) { let result = super . textInsetsWithSizeThatFitsWidthHint ( defaultValue , widthHint ) ; const owner = this . _owner ? this . _owner . get ( ) : null ; if ( owner ) { result = owner . _getTextInsetsForBounds ( result ) ; } return result ; } } @ NativeClass class TextInputControllerFilledImpl extends MDCTextInputControllerFilled { _owner : WeakRef < TextField > ; public static initWithOwner ( owner ) { const delegate = TextInputControllerFilledImpl . new ( ) as TextInputControllerFilledImpl ; delegate . _owner = new WeakRef ( owner ) ; return delegate ; } textInsetsWithSizeThatFitsWidthHint ( defaultValue , widthHint ) { let result = super . textInsetsWithSizeThatFitsWidthHint ( defaultValue , widthHint ) ; const owner = this . _owner ? this . _owner . get ( ) : null ; if ( owner ) { result = owner . _getTextInsetsForBounds ( result ) ; } return result ; } } declare module '@nativescript/core/ui/text-field' { interface TextField { textFieldShouldChangeCharactersInRangeReplacementString ( textField , range , replacementString ) : boolean ; } } export class TextField extends TextFieldBase { nativeViewProtected : MDCTextField ; nativeTextViewProtected : MDCTextField ; private _controller : MDCTextInputControllerBase ; public readonly style : Style & { variant : 'outline' | 'underline' | 'filled' ; } ; public nsdigits ? : NSCharacterSet ; firstEdit : boolean ; public clearFocus ( ) { this . dismissSoftInput ( ) ; } public requestFocus ( ) { this . focus ( ) ; } textFieldShouldChangeCharactersInRangeReplacementString ( textField , range , replacementString ) { if ( this . nsdigits && replacementString . length > 0 && NSString . stringWithString ( replacementString ) . rangeOfCharacterFromSet ( this . nsdigits ) . location === NSNotFound ) { return false ; } if ( this . secureWithoutAutofill && ! textField . secureTextEntry ) { /**
             * Helps avoid iOS 12+ autofill strong password suggestion prompt
             * Discussed in several circles but for example:
             * https://github.com/expo/expo/issues/2571#issuecomment-473347380
             */ textField . secureTextEntry = true ; } const delta = replacementString . length - range . length ; if ( delta > 0 ) { if ( textField . text . length + delta > this . maxLength ) { return false ; } } if ( this . updateTextTrigger === 'textChanged' ) { if ( textField . secureTextEntry && this . firstEdit ) { textProperty . nativeValueChange ( this , replacementString ) ; } else { if ( range . location <= textField . text . length ) { const newText = NSString . stringWithString ( textField . text ) . stringByReplacingCharactersInRangeWithString ( range , replacementString ) ; textProperty . nativeValueChange ( this , newText ) ; } } } if ( this . formattedText ) { _updateCharactersInRangeReplacementString ( this . formattedText , range . location , range . length , replacementString ) ; } this . firstEdit = false ; if ( this . mCanAutoSize ) { this . requestLayout ( ) ; } return true ; } private mCanAutoSize = false ; public onMeasure ( widthMeasureSpec , heightMeasureSpec ) { const widthMode = layout . getMeasureSpecMode ( widthMeasureSpec ) ; this . mCanAutoSize = widthMode !== layout . EXACTLY ; super . onMeasure ( widthMeasureSpec , heightMeasureSpec ) ; } _getTextInsetsForBounds ( insets ) { const style = this . style ; if ( this . variant === 'underline' && this . _controller . underlineHeightNormal === 0 ) { insets . top = 0 ; insets . bottom = 0 ; } if ( paddingTopProperty . isSet ( style ) ) { insets . top = Utils . layout . toDeviceIndependentPixels ( this . effectivePaddingTop ) ; } if ( paddingRightProperty . isSet ( style ) ) { insets . right = Utils . layout . toDeviceIndependentPixels ( this . effectivePaddingRight ) ; } if ( paddingBottomProperty . isSet ( style ) ) { insets . bottom = Utils . layout . toDeviceIndependentPixels ( this . effectivePaddingBottom ) ; } if ( paddingLeftProperty . isSet ( style ) ) { insets . left = Utils . layout . toDeviceIndependentPixels ( this . effectivePaddingLeft ) ; } return insets ; } public createNativeView ( ) { const view = MDCTextField . new ( ) ; view . clearButtonMode = UITextFieldViewMode . Never ; const scheme = MDCContainerScheme . new ( ) ; scheme . colorScheme = themer . getAppColorScheme ( ) ; if ( this . style . variant === 'filled' ) { this . _controller = TextInputControllerFilledImpl . initWithOwner ( this ) ; } else if ( this . style . variant === 'outline' ) { this . _controller = TextInputControllerOutlinedImpl . initWithOwner ( this ) ; } else if ( this . style . variant === 'underline' ) { this . _controller = TextInputControllerUnderlineImpl . initWithOwner ( this ) ; } else { this . _controller = TextInputControllerImpl . initWithOwner ( this ) ; this . _controller . floatingEnabled = false ; } this . _controller . textInput = view ; if ( this . style . variant === 'filled' ) { ( this . _controller as TextInputControllerFilledImpl ) . applyThemeWithScheme ( scheme ) ; } else if ( this . style . variant === 'outline' ) { ( this . _controller as TextInputControllerOutlinedImpl ) . applyThemeWithScheme ( scheme ) ; } else if ( this . style . variant === 'underline' ) { ( this . _controller as TextInputControllerUnderlineImpl ) . applyThemeWithScheme ( scheme ) ; } else { this . _controller . underlineHeightActive = 0 ; this . _controller . underlineHeightNormal = 0 ; } view . textInsetsMode = MDCTextInputTextInsetsMode . IfContent ; return view ; } blur ( ) { this . dismissSoftInput ( ) ; } public setSelection ( start , stop ? ) { const view = this . nativeTextViewProtected ; if ( stop !== undefined ) { const begin = view . beginningOfDocument ; view . selectedTextRange = view . textRangeFromPositionToPosition ( view . positionFromPositionOffset ( begin , start ) , view . positionFromPositionOffset ( begin , stop ) ) ; } else { const begin = view . beginningOfDocument ; const pos = view . positionFromPositionOffset ( begin , start ) ; view . selectedTextRange = view . textRangeFromPositionToPosition ( pos , pos ) ; } } [ editableProperty . setNative ] ( value ) { this . clearFocus ( ) ; } [ floatingColorProperty . setNative ] ( value ) { const color = value instanceof Color ? value . ios : value ; this . _controller . floatingPlaceholderActiveColor = color ; this . _updateAttributedPlaceholder ( ) ; } [ floatingInactiveColorProperty . setNative ] ( value ) { const color = value instanceof Color ? value . ios : value ; this . _controller . floatingPlaceholderNormalColor = color ; this . _updateAttributedPlaceholder ( ) ; } [ placeholderColorProperty . setNative ] ( value ) { const color = value instanceof Color ? value . ios : value ; this . _controller . inlinePlaceholderColor = color ; if ( ! this . floatingColor ) { this . _controller . floatingPlaceholderActiveColor = color ; } this . _updateAttributedPlaceholder ( ) ; } [ errorColorProperty . setNative ] ( value ) { const color = value instanceof Color ? value . ios : value ; this . _controller . errorColor = color ; } [ strokeColorProperty . setNative ] ( value ) { const color = value instanceof Color ? value . ios : value ; this . _controller . activeColor = color ; } [ strokeInactiveColorProperty . setNative ] ( value ) { const color = value instanceof Color ? value . ios : value ; this . _controller . normalColor = color ; } [ strokeDisabledColorProperty . setNative ] ( value ) { const color = value instanceof Color ? value . ios : value ; this . _controller . disabledColor = color ; } [ buttonColorProperty . setNative ] ( value ) { const color = value instanceof Color ? value . ios : value ; this . _controller . textInputClearButtonTintColor = color ; } [ helperProperty . setNative ] ( value ) { this . _controller . helperText = value ; } [ helperColorProperty . setNative ] ( value ) { const temp = typeof value === 'string' ? new Color ( value ) : value ; const color = temp . ios ; this . _controller . leadingUnderlineLabelTextColor = color ; } [ counterMaxLengthProperty . setNative ] ( value ) { this . _controller . characterCountMax = value ; } [ floatingProperty . setNative ] ( value ) { this . _controller . floatingEnabled = value ; } [ errorProperty . setNative ] ( value ) { this . _controller . setErrorTextErrorAccessibilityValue ( value , value ) ; } [ digitsProperty . setNative ] ( value ) { if ( value && value . length > 0 ) { this . nsdigits = NSCharacterSet . characterSetWithCharactersInString ( value ) ; } else { this . nsdigits = null ; } } [ backgroundInternalProperty . setNative ] ( value ) { switch ( this . variant ) { case 'none' : super [ backgroundInternalProperty . setNative ] ( value ) ; break ; case 'outline' : case 'filled' : case 'underline' : { if ( value . color ) { this . _controller . borderFillColor = value . color . ios ; } if ( value . borderTopColor ) { this . _controller . activeColor = value . borderTopColor . ios ; } break ; } } } [ fontInternalProperty . setNative ] ( value ) { super [ fontInternalProperty . setNative ] ( value ) ; const font = value instanceof Font ? value . getUIFont ( this . _controller . inlinePlaceholderFont ) : value ; this . _controller . inlinePlaceholderFont = font ; } [ verticalTextAlignmentProperty . setNative ] ( value ) { const view = this . nativeTextViewProtected ; view . backgroundColor = UIColor . redColor ; switch ( value ) { case 'initial' : case 'top' : view . contentVerticalAlignment = UIControlContentVerticalAlignment . Top ; break ; case 'middle' : view . contentVerticalAlignment = UIControlContentVerticalAlignment . Center ; break ; case 'bottom' : view . contentVerticalAlignment = UIControlContentVerticalAlignment . Bottom ; break ; } } }