import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer, Input, Output, EventEmitter, ContentChild, ContentChildren, HostListener} from 'angular2/core'; import {NgIf} from 'angular2/common'; import {NavController} from '../nav/nav-controller'; import {Config} from '../../config/config'; import {Form} from '../../util/form'; import {Label} from '../label/label'; import {IonicApp} from '../app/app'; import {Content} from '../content/content'; import {CSS, hasFocus, pointerCoord, hasPointerMoved} from '../../util/dom'; import {Platform} from '../../platform/platform'; import {Button} from '../button/button'; import {Icon} from '../icon/icon'; /** * @private */ @Directive({ selector: 'textarea,input[type=text],input[type=password],input[type=number],input[type=search],input[type=email],input[type=url],input[type=tel],input[type=date],input[type=datetime],input[type=datetime-local],input[type=week],input[type=time]', host: { 'class': 'text-input' } }) export class TextInputElement { @Input() value: string; @Input() ngModel: any; @Output() valueChange: EventEmitter = new EventEmitter(); @Output() focusChange: EventEmitter = new EventEmitter(); constructor( @Attribute('type') type: string, private _elementRef: ElementRef, private _renderer: Renderer ) { this.type = type || 'text'; } ngOnInit() { if (this.ngModel) { this.value = this.ngModel; } else { this.value = this._elementRef.nativeElement.value; } } @HostListener('keyup', ['$event']) _keyup(ev) { this.valueChange.emit(ev.target.value); } @HostListener('focus') _focus() { this.focusChange.emit(true); } @HostListener('blur') _blur() { this.focusChange.emit(false); this.hideFocus(false); } labelledBy(val) { this._renderer.setElementAttribute(this._elementRef, 'aria-labelledby', val); } setFocus() { this.element().focus(); } relocate(shouldRelocate, inputRelativeY) { if (this._relocated !== shouldRelocate) { let focusedInputEle = this.element(); if (shouldRelocate) { let clonedInputEle = cloneInput(focusedInputEle, 'cloned-input'); focusedInputEle.classList.add('hide-focused-input'); focusedInputEle.style[CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`; focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle); this.setFocus(); } else { focusedInputEle.classList.remove('hide-focused-input'); focusedInputEle.style[CSS.transform] = ''; let clonedInputEle = focusedInputEle.parentNode.querySelector('.cloned-input'); if (clonedInputEle) { clonedInputEle.parentNode.removeChild(clonedInputEle); } } this._relocated = shouldRelocate; } } hideFocus(shouldHideFocus) { let focusedInputEle = this.element(); if (shouldHideFocus) { let clonedInputEle = cloneInput(focusedInputEle, 'cloned-hidden'); focusedInputEle.classList.add('hide-focused-input'); focusedInputEle.style[CSS.transform] = 'translate3d(-9999px,0,0)'; focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle); } else { focusedInputEle.classList.remove('hide-focused-input'); focusedInputEle.style[CSS.transform] = ''; let clonedInputEle = focusedInputEle.parentNode.querySelector('.cloned-hidden'); if (clonedInputEle) { clonedInputEle.parentNode.removeChild(clonedInputEle); } } } hasFocus() { return hasFocus(this.element()); } addClass(className) { this._renderer.setElementClass(this._elementRef, className, true); } hasClass(className) { this._elementRef.nativeElement.classList.contains(className); } element() { return this._elementRef.nativeElement; } } /** * @name Input * @module ionic * @description * * `ion-input` is a generic wrapper for both inputs and textareas. You can give `ion-input` attributes to tell it how to handle a child `ion-label` component. * * @property [fixed-label] - a persistant label that sits next the the input * @property [floating-label] - a label that will float about the input if the input is empty of looses focus * @property [stacked-label] - A stacked label will always appear on top of the input * @property [inset] - The input will be inset * @property [clearInput] - A clear icon will appear in the input which clears it * * @usage * ```html * * Username * * * * * * * * * Username * * * * * Username * * * ``` * */ @Component({ selector: 'ion-input', host: { '(touchstart)': 'pointerStart($event)', '(touchend)': 'pointerEnd($event)', '(mouseup)': 'pointerEnd($event)', 'class': 'item', '[class.ng-untouched]': 'hasClass("ng-untouched")', '[class.ng-touched]': 'hasClass("ng-touched")', '[class.ng-pristine]': 'hasClass("ng-pristine")', '[class.ng-dirty]': 'hasClass("ng-dirty")', '[class.ng-valid]': 'hasClass("ng-valid")', '[class.ng-invalid]': 'hasClass("ng-invalid")' }, template: '
' + '' + '' + '' + '
', directives: [NgIf, forwardRef(() => InputScrollAssist), forwardRef(() => TextInputElement), Button] }) export class TextInput { /** * @private */ @Input() clearInput: any; value: string = ''; constructor( config: Config, private _form: Form, private _renderer: Renderer, private _elementRef: ElementRef, private _app: IonicApp, private _platform: Platform, @Optional() @Host() private _scrollView: Content, @Optional() private _nav: NavController, @Attribute('floating-label') isFloating: string, @Attribute('stacked-label') isStacked: string, @Attribute('fixed-label') isFixed: string, @Attribute('inset') isInset: string ) { _form.register(this); this.type = 'text'; this.lastTouch = 0; // make more gud with pending @Attributes API this.displayType = (isFloating === '' ? 'floating' : (isStacked === '' ? 'stacked' : (isFixed === '' ? 'fixed' : (isInset === '' ? 'inset' : null)))); this._assist = config.get('scrollAssist'); this.keyboardHeight = config.get('keyboardHeight'); } /** * @private */ @ContentChild(TextInputElement) set _setInput(textInputElement) { if (textInputElement) { textInputElement.addClass('item-input'); if (this.displayType) { textInputElement.addClass(this.displayType + '-input'); } this.input = textInputElement; this.type = textInputElement.type; this.hasValue(this.input.value); textInputElement.valueChange.subscribe(inputValue => { this.hasValue(inputValue); }); this.focusChange(this.hasFocus()); textInputElement.focusChange.subscribe(hasFocus => { this.focusChange(hasFocus); }); } else { console.error(' or