mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 13:32:54 +08:00
refactor(textInput): use ContentChild, code cleanup
This commit is contained in:
@ -1,8 +1,5 @@
|
|||||||
import {Directive, Optional, ElementRef, Renderer} from 'angular2/core';
|
import {Directive, ElementRef, Renderer} from 'angular2/core';
|
||||||
|
|
||||||
import {Config} from '../../config/config';
|
|
||||||
import {TextInput} from '../text-input/text-input';
|
|
||||||
import {pointerCoord, hasPointerMoved} from '../../util/dom';
|
|
||||||
import {Form} from '../../util/form';
|
import {Form} from '../../util/form';
|
||||||
|
|
||||||
|
|
||||||
@ -30,25 +27,16 @@ import {Form} from '../../util/form';
|
|||||||
'id'
|
'id'
|
||||||
],
|
],
|
||||||
host: {
|
host: {
|
||||||
'[attr.id]': 'id',
|
'[attr.id]': 'id'
|
||||||
'(touchstart)': 'pointerStart($event)',
|
|
||||||
'(touchend)': 'pointerEnd($event)',
|
|
||||||
'(mousedown)': 'pointerStart($event)',
|
|
||||||
'(mouseup)': 'pointerEnd($event)'
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class Label {
|
export class Label {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
config: Config,
|
|
||||||
@Optional() container: TextInput,
|
|
||||||
private _form: Form,
|
private _form: Form,
|
||||||
private _elementRef: ElementRef,
|
private _elementRef: ElementRef,
|
||||||
private _renderer: Renderer
|
private _renderer: Renderer
|
||||||
) {
|
) {}
|
||||||
this.scrollAssist = config.get('scrollAssist');
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -57,37 +45,6 @@ export class Label {
|
|||||||
if (!this.id) {
|
if (!this.id) {
|
||||||
this.id = 'lbl-' + this._form.nextId();
|
this.id = 'lbl-' + this._form.nextId();
|
||||||
}
|
}
|
||||||
this.container && this.container.registerLabel(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
pointerStart(ev) {
|
|
||||||
if (this.scrollAssist) {
|
|
||||||
// remember where the touchstart/mousedown started
|
|
||||||
this.startCoord = pointerCoord(ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
pointerEnd(ev) {
|
|
||||||
if (this.container) {
|
|
||||||
|
|
||||||
// get where the touchend/mouseup ended
|
|
||||||
let endCoord = pointerCoord(ev);
|
|
||||||
|
|
||||||
// focus this input if the pointer hasn't moved XX pixels
|
|
||||||
if (!hasPointerMoved(20, this.startCoord, endCoord)) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
this.container.initFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.startCoord = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer, Input, ContentChild} from 'angular2/core';
|
import {Component, Directive, Attribute, forwardRef, Host, Optional, ElementRef, Renderer, Input, Output, EventEmitter, ContentChild, HostListener} from 'angular2/core';
|
||||||
import {NgIf} from 'angular2/common';
|
import {NgIf} from 'angular2/common';
|
||||||
|
|
||||||
import {NavController} from '../nav/nav-controller';
|
import {NavController} from '../nav/nav-controller';
|
||||||
@ -7,11 +7,131 @@ import {Form} from '../../util/form';
|
|||||||
import {Label} from '../label/label';
|
import {Label} from '../label/label';
|
||||||
import {IonicApp} from '../app/app';
|
import {IonicApp} from '../app/app';
|
||||||
import {Content} from '../content/content';
|
import {Content} from '../content/content';
|
||||||
import * as dom from '../../util/dom';
|
import {CSS, hasFocus, pointerCoord, hasPointerMoved} from '../../util/dom';
|
||||||
import {Platform} from '../../platform/platform';
|
import {Platform} from '../../platform/platform';
|
||||||
import {Button} from '../button/button';
|
import {Button} from '../button/button';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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<string> = new EventEmitter();
|
||||||
|
@Output() focusChange: EventEmitter<boolean> = 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
|
* @name Input
|
||||||
* @module ionic
|
* @module ionic
|
||||||
@ -56,49 +176,43 @@ import {Button} from '../button/button';
|
|||||||
'(touchend)': 'pointerEnd($event)',
|
'(touchend)': 'pointerEnd($event)',
|
||||||
'(mouseup)': 'pointerEnd($event)',
|
'(mouseup)': 'pointerEnd($event)',
|
||||||
'class': 'item',
|
'class': 'item',
|
||||||
'[class.ng-untouched]': 'addNgClass("ng-untouched")',
|
'[class.ng-untouched]': 'hasClass("ng-untouched")',
|
||||||
'[class.ng-touched]': 'addNgClass("ng-touched")',
|
'[class.ng-touched]': 'hasClass("ng-touched")',
|
||||||
'[class.ng-pristine]': 'addNgClass("ng-pristine")',
|
'[class.ng-pristine]': 'hasClass("ng-pristine")',
|
||||||
'[class.ng-dirty]': 'addNgClass("ng-dirty")',
|
'[class.ng-dirty]': 'hasClass("ng-dirty")',
|
||||||
'[class.ng-valid]': 'addNgClass("ng-valid")',
|
'[class.ng-valid]': 'hasClass("ng-valid")',
|
||||||
'[class.ng-invalid]': 'addNgClass("ng-invalid")'
|
'[class.ng-invalid]': 'hasClass("ng-invalid")'
|
||||||
},
|
},
|
||||||
template:
|
template:
|
||||||
'<div class="item-inner">' +
|
'<div class="item-inner">' +
|
||||||
'<ng-content></ng-content>' +
|
'<ng-content></ng-content>' +
|
||||||
'<input [type]="type" aria-hidden="true" scroll-assist *ngIf="scrollAssist">' +
|
'<input [type]="type" aria-hidden="true" scroll-assist *ngIf="_assist">' +
|
||||||
'<button clear *ngIf="clearInput && value" class="text-input-clear-icon" (click)="clearTextInput()" (mousedown)="clearTextInput()"></button>' +
|
'<button clear *ngIf="clearInput && value" class="text-input-clear-icon" (click)="clearTextInput()" (mousedown)="clearTextInput()"></button>' +
|
||||||
'</div>',
|
'</div>',
|
||||||
directives: [NgIf, forwardRef(() => InputScrollAssist), forwardRef(() => TextInputElement), Button]
|
directives: [NgIf, forwardRef(() => InputScrollAssist), forwardRef(() => TextInputElement), Button]
|
||||||
})
|
})
|
||||||
export class TextInput {
|
export class TextInput {
|
||||||
@ContentChild(forwardRef(() => TextInputElement)) textInputElement;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@Input() clearInput: any;
|
@Input() clearInput: any;
|
||||||
|
value: string = '';
|
||||||
value: any = '';
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
form: Form,
|
|
||||||
elementRef: ElementRef,
|
|
||||||
config: Config,
|
config: Config,
|
||||||
renderer: Renderer,
|
private _form: Form,
|
||||||
app: IonicApp,
|
private _renderer: Renderer,
|
||||||
platform: Platform,
|
private _elementRef: ElementRef,
|
||||||
@Optional() @Host() scrollView: Content,
|
private _app: IonicApp,
|
||||||
@Optional() navCtrl: NavController,
|
private _platform: Platform,
|
||||||
|
@Optional() @Host() private _scrollView: Content,
|
||||||
|
@Optional() private _nav: NavController,
|
||||||
@Attribute('floating-label') isFloating: string,
|
@Attribute('floating-label') isFloating: string,
|
||||||
@Attribute('stacked-label') isStacked: string,
|
@Attribute('stacked-label') isStacked: string,
|
||||||
@Attribute('fixed-label') isFixed: string,
|
@Attribute('fixed-label') isFixed: string,
|
||||||
@Attribute('inset') isInset: string
|
@Attribute('inset') isInset: string
|
||||||
) {
|
) {
|
||||||
this.renderer = renderer;
|
_form.register(this);
|
||||||
|
|
||||||
this.form = form;
|
|
||||||
form.register(this);
|
|
||||||
|
|
||||||
this.type = 'text';
|
this.type = 'text';
|
||||||
this.lastTouch = 0;
|
this.lastTouch = 0;
|
||||||
@ -106,40 +220,44 @@ export class TextInput {
|
|||||||
// make more gud with pending @Attributes API
|
// make more gud with pending @Attributes API
|
||||||
this.displayType = (isFloating === '' ? 'floating' : (isStacked === '' ? 'stacked' : (isFixed === '' ? 'fixed' : (isInset === '' ? 'inset' : null))));
|
this.displayType = (isFloating === '' ? 'floating' : (isStacked === '' ? 'stacked' : (isFixed === '' ? 'fixed' : (isInset === '' ? 'inset' : null))));
|
||||||
|
|
||||||
this.app = app;
|
this._assist = config.get('scrollAssist');
|
||||||
this.elementRef = elementRef;
|
|
||||||
this.platform = platform;
|
|
||||||
this.navCtrl = navCtrl;
|
|
||||||
|
|
||||||
this.scrollView = scrollView;
|
|
||||||
this.scrollAssist = config.get('scrollAssist');
|
|
||||||
this.keyboardHeight = config.get('keyboardHeight');
|
this.keyboardHeight = config.get('keyboardHeight');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* This function is used to add the Angular css classes associated with inputs in forms
|
|
||||||
*/
|
*/
|
||||||
addNgClass(className) {
|
@ContentChild(TextInputElement)
|
||||||
this.input && this.input.elementRef.nativeElement.classList.contains(className);
|
set _setInput(textInputElement) {
|
||||||
}
|
if (textInputElement) {
|
||||||
|
textInputElement.addClass('item-input');
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
registerInput(textInputElement) {
|
|
||||||
if (this.displayType) {
|
if (this.displayType) {
|
||||||
textInputElement.addClass(this.displayType + '-input');
|
textInputElement.addClass(this.displayType + '-input');
|
||||||
}
|
}
|
||||||
this.input = textInputElement;
|
this.input = textInputElement;
|
||||||
this.type = textInputElement.type || 'text';
|
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('<input> or <textarea> elements required within <ion-input>')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
registerLabel(label) {
|
@ContentChild(Label)
|
||||||
if (this.displayType) {
|
set _setLabel(label) {
|
||||||
|
if (label && this.displayType) {
|
||||||
label.addClass(this.displayType + '-label');
|
label.addClass(this.displayType + '-label');
|
||||||
}
|
}
|
||||||
this.label = label;
|
this.label = label;
|
||||||
@ -160,24 +278,24 @@ export class TextInput {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
if (this.input && this.label) {
|
let self = this;
|
||||||
|
if (self.input && self.label) {
|
||||||
// if there is an input and a label
|
// if there is an input and a label
|
||||||
// then give the label an ID
|
// then give the label an ID
|
||||||
// and tell the input the ID of who it's labelled by
|
// and tell the input the ID of who it's labelled by
|
||||||
this.input.labelledBy(this.label.id);
|
self.input.labelledBy(self.label.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let self = this;
|
self.scrollMove = function(ev) {
|
||||||
self.scrollMove = (ev) => {
|
if (!(self._nav && self._nav.isTransitioning())) {
|
||||||
if (!(this.navCtrl && this.navCtrl.isTransitioning())) {
|
|
||||||
self.deregMove();
|
self.deregMove();
|
||||||
|
|
||||||
if (self.hasFocus) {
|
if (self.hasFocus()) {
|
||||||
self.input.hideFocus(true);
|
self.input.hideFocus(true);
|
||||||
this.scrollView.onScrollEnd(() => {
|
self._scrollView.onScrollEnd(function() {
|
||||||
self.input.hideFocus(false);
|
self.input.hideFocus(false);
|
||||||
|
|
||||||
if (self.hasFocus) {
|
if (self.hasFocus()) {
|
||||||
self.regMove();
|
self.regMove();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -198,9 +316,9 @@ export class TextInput {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
pointerStart(ev) {
|
pointerStart(ev) {
|
||||||
if (this.scrollAssist && this.app.isEnabled()) {
|
if (this._assist && this._app.isEnabled()) {
|
||||||
// remember where the touchstart/mousedown started
|
// remember where the touchstart/mousedown started
|
||||||
this.startCoord = dom.pointerCoord(ev);
|
this.startCoord = pointerCoord(ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,17 +326,17 @@ export class TextInput {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
pointerEnd(ev) {
|
pointerEnd(ev) {
|
||||||
if (!this.app.isEnabled()) {
|
if (!this._app.isEnabled()) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
} else if (this.scrollAssist && ev.type === 'touchend') {
|
} else if (this._assist && ev.type === 'touchend') {
|
||||||
// get where the touchend/mouseup ended
|
// get where the touchend/mouseup ended
|
||||||
let endCoord = dom.pointerCoord(ev);
|
let endCoord = pointerCoord(ev);
|
||||||
|
|
||||||
// focus this input if the pointer hasn't moved XX pixels
|
// focus this input if the pointer hasn't moved XX pixels
|
||||||
// and the input doesn't already have focus
|
// and the input doesn't already have focus
|
||||||
if (!dom.hasPointerMoved(8, this.startCoord, endCoord) && !this.hasFocus) {
|
if (!hasPointerMoved(8, this.startCoord, endCoord) && !this.hasFocus()) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
@ -243,15 +361,15 @@ export class TextInput {
|
|||||||
initFocus() {
|
initFocus() {
|
||||||
// begin the process of setting focus to the inner input element
|
// begin the process of setting focus to the inner input element
|
||||||
|
|
||||||
let scrollView = this.scrollView;
|
let scrollView = this._scrollView;
|
||||||
|
|
||||||
if (scrollView && this.scrollAssist) {
|
if (scrollView && this._assist) {
|
||||||
// this input is inside of a scroll view
|
// this input is inside of a scroll view
|
||||||
|
|
||||||
// find out if text input should be manually scrolled into view
|
// find out if text input should be manually scrolled into view
|
||||||
let ele = this.elementRef.nativeElement;
|
let ele = this._elementRef.nativeElement;
|
||||||
|
|
||||||
let scrollData = TextInput.getScrollData(ele.offsetTop, ele.offsetHeight, scrollView.getDimensions(), this.keyboardHeight, this.platform.height());
|
let scrollData = TextInput.getScrollData(ele.offsetTop, ele.offsetHeight, scrollView.getDimensions(), this.keyboardHeight, this._platform.height());
|
||||||
if (scrollData.scrollAmount > -3 && scrollData.scrollAmount < 3) {
|
if (scrollData.scrollAmount > -3 && scrollData.scrollAmount < 3) {
|
||||||
// the text input is in a safe position that doesn't require
|
// the text input is in a safe position that doesn't require
|
||||||
// it to be scrolled into view, just set focus now
|
// it to be scrolled into view, just set focus now
|
||||||
@ -266,8 +384,8 @@ export class TextInput {
|
|||||||
// manually scroll the text input to the top
|
// manually scroll the text input to the top
|
||||||
// do not allow any clicks while it's scrolling
|
// do not allow any clicks while it's scrolling
|
||||||
let scrollDuration = getScrollAssistDuration(scrollData.scrollAmount);
|
let scrollDuration = getScrollAssistDuration(scrollData.scrollAmount);
|
||||||
this.app.setEnabled(false, scrollDuration);
|
this._app.setEnabled(false, scrollDuration);
|
||||||
this.navCtrl && this.navCtrl.setTransitioning(true, scrollDuration);
|
this._nav && this._nav.setTransitioning(true, scrollDuration);
|
||||||
|
|
||||||
// temporarily move the focus to the focus holder so the browser
|
// temporarily move the focus to the focus holder so the browser
|
||||||
// doesn't freak out while it's trying to get the input in place
|
// doesn't freak out while it's trying to get the input in place
|
||||||
@ -281,8 +399,8 @@ export class TextInput {
|
|||||||
this.input.relocate(false);
|
this.input.relocate(false);
|
||||||
|
|
||||||
// all good, allow clicks again
|
// all good, allow clicks again
|
||||||
this.app.setEnabled(true);
|
this._app.setEnabled(true);
|
||||||
this.navCtrl && this.navCtrl.setTransitioning(false);
|
this._nav && this._nav.setTransitioning(false);
|
||||||
this.regMove();
|
this.regMove();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -296,11 +414,81 @@ export class TextInput {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {TODO} inputOffsetTop TODO
|
*/
|
||||||
* @param {TODO} inputOffsetHeight TODO
|
setFocus() {
|
||||||
* @param {TODO} scrollViewDimensions TODO
|
if (this.input) {
|
||||||
* @param {TODO} keyboardHeight TODO
|
this._form.setAsFocused(this);
|
||||||
* @returns {TODO} TODO
|
|
||||||
|
// set focus on the actual input element
|
||||||
|
this.input.setFocus();
|
||||||
|
|
||||||
|
// ensure the body hasn't scrolled down
|
||||||
|
document.body.scrollTop = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
regMove() {
|
||||||
|
if (this._assist && this._scrollView) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.deregMove();
|
||||||
|
this.deregScroll = this._scrollView.addScrollEventListener(this.scrollMove);
|
||||||
|
}, 80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
deregMove() {
|
||||||
|
this.deregScroll && this.deregScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
focusChange(hasFocus) {
|
||||||
|
this._renderer.setElementClass(this._elementRef, 'input-focused', hasFocus);
|
||||||
|
if (!hasFocus) {
|
||||||
|
this.deregMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
hasFocus() {
|
||||||
|
return !!this.input && this.input.hasFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
hasValue(inputValue) {
|
||||||
|
let inputHasValue = !!(inputValue && inputValue !== '');
|
||||||
|
this._renderer.setElementClass(this._elementRef, 'input-has-value', inputHasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* This function is used to add the Angular css classes associated with inputs in forms
|
||||||
|
*/
|
||||||
|
hasClass(className) {
|
||||||
|
this.input && this.input.hasClass(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.deregMove();
|
||||||
|
this._form.deregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
static getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, plaformHeight) {
|
static getScrollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight, plaformHeight) {
|
||||||
// compute input's Y values relative to the body
|
// compute input's Y values relative to the body
|
||||||
@ -416,212 +604,21 @@ export class TextInput {
|
|||||||
return scrollData;
|
return scrollData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
focusChange(hasFocus) {
|
|
||||||
this.renderer.setElementClass(this.elementRef, 'input-focused', hasFocus);
|
|
||||||
if (!hasFocus) {
|
|
||||||
this.deregMove();
|
|
||||||
this.input.hideFocus(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
hasValue(inputValue) {
|
|
||||||
this.renderer.setElementClass(this.elementRef, 'input-has-value', inputValue && inputValue !== '');
|
|
||||||
this.value = inputValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
setFocus() {
|
|
||||||
if (this.input) {
|
|
||||||
this.form.setAsFocused(this);
|
|
||||||
|
|
||||||
// set focus on the actual input element
|
|
||||||
this.input.setFocus();
|
|
||||||
|
|
||||||
// ensure the body hasn't scrolled down
|
|
||||||
document.body.scrollTop = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
regMove() {
|
|
||||||
if (this.scrollAssist && this.scrollView) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.deregMove();
|
|
||||||
this.deregScroll = this.scrollView.addScrollEventListener(this.scrollMove);
|
|
||||||
}, 80);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
deregMove() {
|
|
||||||
this.deregScroll && this.deregScroll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
get hasFocus() {
|
|
||||||
return !!this.input && this.input.hasFocus;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.deregMove();
|
|
||||||
this.form.deregister(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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]',
|
|
||||||
inputs: ['value', 'ngModel'],
|
|
||||||
host: {
|
|
||||||
'(focus)': 'focusChange(true)',
|
|
||||||
'(blur)': 'focusChange(false)',
|
|
||||||
'(keyup)': 'onKeyup($event)'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
export class TextInputElement {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
@Attribute('type') type: string,
|
|
||||||
elementRef: ElementRef,
|
|
||||||
renderer: Renderer,
|
|
||||||
@Optional() wrapper: TextInput
|
|
||||||
) {
|
|
||||||
this.type = type;
|
|
||||||
this.elementRef = elementRef;
|
|
||||||
this.wrapper = wrapper;
|
|
||||||
this.renderer = renderer;
|
|
||||||
|
|
||||||
// all text inputs (textarea, input[type=text],input[type=password], etc)
|
|
||||||
renderer.setElementClass(elementRef, 'text-input', true);
|
|
||||||
|
|
||||||
if (wrapper) {
|
|
||||||
// it's within ionic's ion-input, let ion-input handle what's up
|
|
||||||
renderer.setElementClass(elementRef, 'item-input', true);
|
|
||||||
wrapper.registerInput(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
if (this.ngModel) this.value = this.ngModel;
|
|
||||||
this.wrapper && this.wrapper.hasValue(this.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
focusChange(changed) {
|
|
||||||
this.wrapper && this.wrapper.focusChange(changed);
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyup(ev) {
|
|
||||||
this.wrapper && this.wrapper.hasValue(ev.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
labelledBy(val) {
|
|
||||||
this.renderer.setElementAttribute(this.elementRef, 'aria-labelledby', val);
|
|
||||||
}
|
|
||||||
|
|
||||||
setFocus() {
|
|
||||||
this.getNativeElement().focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
relocate(shouldRelocate, inputRelativeY) {
|
|
||||||
if (this._relocated !== shouldRelocate) {
|
|
||||||
|
|
||||||
let focusedInputEle = this.getNativeElement();
|
|
||||||
if (shouldRelocate) {
|
|
||||||
let clonedInputEle = cloneInput(focusedInputEle, 'cloned-input');
|
|
||||||
|
|
||||||
focusedInputEle.classList.add('hide-focused-input');
|
|
||||||
focusedInputEle.style[dom.CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`;
|
|
||||||
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
|
|
||||||
|
|
||||||
this.wrapper.setFocus();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
focusedInputEle.classList.remove('hide-focused-input');
|
|
||||||
focusedInputEle.style[dom.CSS.transform] = '';
|
|
||||||
let clonedInputEle = focusedInputEle.parentNode.querySelector('.cloned-input');
|
|
||||||
if (clonedInputEle) {
|
|
||||||
clonedInputEle.parentNode.removeChild(clonedInputEle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._relocated = shouldRelocate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hideFocus(shouldHideFocus) {
|
|
||||||
let focusedInputEle = this.getNativeElement();
|
|
||||||
|
|
||||||
if (shouldHideFocus) {
|
|
||||||
let clonedInputEle = cloneInput(focusedInputEle, 'cloned-hidden');
|
|
||||||
|
|
||||||
focusedInputEle.classList.add('hide-focused-input');
|
|
||||||
focusedInputEle.style[dom.CSS.transform] = 'translate3d(-9999px,0,0)';
|
|
||||||
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
focusedInputEle.classList.remove('hide-focused-input');
|
|
||||||
focusedInputEle.style[dom.CSS.transform] = '';
|
|
||||||
let clonedInputEle = focusedInputEle.parentNode.querySelector('.cloned-hidden');
|
|
||||||
if (clonedInputEle) {
|
|
||||||
clonedInputEle.parentNode.removeChild(clonedInputEle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasFocus() {
|
|
||||||
return dom.hasFocus(this.getNativeElement());
|
|
||||||
}
|
|
||||||
|
|
||||||
addClass(className) {
|
|
||||||
this.renderer.setElementClass(this.elementRef, className, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
getNativeElement() {
|
|
||||||
return this.elementRef.nativeElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[scroll-assist]',
|
selector: '[scroll-assist]'
|
||||||
host: {
|
|
||||||
'(focus)': 'receivedFocus($event)'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
class InputScrollAssist {
|
class InputScrollAssist {
|
||||||
|
|
||||||
constructor(form: Form, textInput: TextInput) {
|
constructor(private _form: Form, private _input: TextInput) {}
|
||||||
this.form = form;
|
|
||||||
this.textInput = textInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
receivedFocus(ev) {
|
@HostListener('focus')
|
||||||
this.form.focusNext(this.textInput);
|
receivedFocus() {
|
||||||
|
this._form.focusNext(this._input);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -636,7 +633,6 @@ function cloneInput(srcInput, addCssClass) {
|
|||||||
return clonedInputEle;
|
return clonedInputEle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const SCROLL_ASSIST_SPEED = 0.4;
|
const SCROLL_ASSIST_SPEED = 0.4;
|
||||||
|
|
||||||
function getScrollAssistDuration(distanceToScroll) {
|
function getScrollAssistDuration(distanceToScroll) {
|
||||||
|
Reference in New Issue
Block a user