mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
refactor(input): improve focus handling, alpha39 fixes
This commit is contained in:
@ -6,7 +6,7 @@ export * from 'ionic/components/blur/blur'
|
|||||||
export * from 'ionic/components/button/button'
|
export * from 'ionic/components/button/button'
|
||||||
export * from 'ionic/components/checkbox/checkbox'
|
export * from 'ionic/components/checkbox/checkbox'
|
||||||
export * from 'ionic/components/content/content'
|
export * from 'ionic/components/content/content'
|
||||||
export * from 'ionic/components/form/input'
|
export * from 'ionic/components/form/form'
|
||||||
export * from 'ionic/components/icon/icon'
|
export * from 'ionic/components/icon/icon'
|
||||||
export * from 'ionic/components/item/item'
|
export * from 'ionic/components/item/item'
|
||||||
export * from 'ionic/components/item/item-group'
|
export * from 'ionic/components/item/item-group'
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import {Component, View, Directive, ElementRef, Optional, NgControl} from 'angular2/angular2';
|
import {Component, View, Directive, Optional, NgControl} from 'angular2/angular2';
|
||||||
|
|
||||||
import {Ion} from '../ion';
|
import {Ion} from '../ion';
|
||||||
import {IonInput} from '../form/input';
|
import {IonicForm} from '../form/form';
|
||||||
import {IonicConfig} from '../../config/config';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The checkbox is no different than the HTML checkbox input, except it's styled differently
|
* The checkbox is no different than the HTML checkbox input, except it's styled differently
|
||||||
@ -44,21 +43,14 @@ import {IonicConfig} from '../../config/config';
|
|||||||
'<ng-content></ng-content>' +
|
'<ng-content></ng-content>' +
|
||||||
'</ion-item-content>'
|
'</ion-item-content>'
|
||||||
})
|
})
|
||||||
export class Checkbox extends Ion {
|
export class Checkbox {
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {ElementRef} elementRef TODO
|
|
||||||
* @param {IonicConfig} ionicConfig TODO
|
|
||||||
* @param {NgControl=} ngControl TODO
|
|
||||||
*/
|
|
||||||
constructor(
|
constructor(
|
||||||
elementRef: ElementRef,
|
form: IonicForm,
|
||||||
config: IonicConfig,
|
|
||||||
@Optional() ngControl: NgControl
|
@Optional() ngControl: NgControl
|
||||||
) {
|
) {
|
||||||
super(elementRef, config);
|
this.form = form;
|
||||||
this.tabIndex = 0;
|
form.register(this);
|
||||||
this.id = IonInput.nextId();
|
|
||||||
|
|
||||||
this.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
this.onTouched = (_) => {};
|
this.onTouched = (_) => {};
|
||||||
@ -72,8 +64,7 @@ export class Checkbox extends Ion {
|
|||||||
* TODO
|
* TODO
|
||||||
*/
|
*/
|
||||||
onInit() {
|
onInit() {
|
||||||
super.onInit();
|
this.labelId = 'label-' + this.inputId;
|
||||||
this.labelId = 'label-' + this.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -121,4 +112,8 @@ export class Checkbox extends Ion {
|
|||||||
* @param {Function} fn onTouched event handler.
|
* @param {Function} fn onTouched event handler.
|
||||||
*/
|
*/
|
||||||
registerOnTouched(fn) { this.onTouched = fn; }
|
registerOnTouched(fn) { this.onTouched = fn; }
|
||||||
|
|
||||||
|
onDestroy() {
|
||||||
|
this.form.deregister(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
import {Component, Directive, View, Host, Attribute, ElementRef, forwardRef} from 'angular2/angular2';
|
|
||||||
|
|
||||||
import {IonicConfig} from '../../config/config';
|
|
||||||
import {IonInput} from './input';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
@Component({
|
|
||||||
selector: 'focus-holder'
|
|
||||||
})
|
|
||||||
@View({
|
|
||||||
template: '<input tabindex="999"><input tabindex="1001"><input tabindex="1002">',
|
|
||||||
directives: [forwardRef(() => FocusInput)]
|
|
||||||
})
|
|
||||||
export class FocusHolder {
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
this.i = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} inputType TODO
|
|
||||||
*/
|
|
||||||
setFocusHolder(inputType) {
|
|
||||||
this.i[2].type = inputType;
|
|
||||||
this.i[2].setFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} tabIndex TODO
|
|
||||||
*/
|
|
||||||
receivedFocus(tabIndex) {
|
|
||||||
if (tabIndex === '999') {
|
|
||||||
// focus on the previous input
|
|
||||||
IonInput.focusPrevious();
|
|
||||||
|
|
||||||
} else if (tabIndex === '1001') {
|
|
||||||
// focus on the next input
|
|
||||||
IonInput.focusNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} input TODO
|
|
||||||
*/
|
|
||||||
register(input) {
|
|
||||||
this.i.push(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: 'input',
|
|
||||||
host: {
|
|
||||||
'[type]': 'type',
|
|
||||||
'(focus)': 'holder.receivedFocus(tabindex)',
|
|
||||||
'(keydown)': 'keydown($event)'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
class FocusInput {
|
|
||||||
constructor(
|
|
||||||
elementRef: ElementRef,
|
|
||||||
@Host() holder: FocusHolder,
|
|
||||||
@Attribute('tabindex') tabindex: string
|
|
||||||
) {
|
|
||||||
this.elementRef = elementRef;
|
|
||||||
this.holder = holder;
|
|
||||||
this.tabindex = tabindex;
|
|
||||||
this.holder.register(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
setFocus() {
|
|
||||||
this.elementRef.nativeElement.focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
keydown(ev) {
|
|
||||||
// prevent any keyboard typing when a holder has focus
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
get type() {
|
|
||||||
// default to text type if unknown
|
|
||||||
return this._t || 'text';
|
|
||||||
}
|
|
||||||
|
|
||||||
set type(val) {
|
|
||||||
this._t = val;
|
|
||||||
}
|
|
||||||
}
|
|
235
ionic/components/form/form.ts
Normal file
235
ionic/components/form/form.ts
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
import {Injectable, NgZone} from 'angular2/angular2';
|
||||||
|
|
||||||
|
import {IonicConfig} from '../../config/config';
|
||||||
|
import {raf} from '../../util/dom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Input component is used to focus text input elements.
|
||||||
|
*
|
||||||
|
* The `focusNext()` and `focusPrevious()` methods make it easy to focus input elements across all devices.
|
||||||
|
*
|
||||||
|
* @usage
|
||||||
|
* ```html
|
||||||
|
* <ion-input>
|
||||||
|
* <ion-label>Name</ion-label>
|
||||||
|
* <input value="Name" type="text">
|
||||||
|
* </ion-input>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class IonicForm {
|
||||||
|
|
||||||
|
constructor(config: IonicConfig, zone: NgZone) {
|
||||||
|
this._config = config;
|
||||||
|
this._zone = zone;
|
||||||
|
|
||||||
|
this._inputs = [];
|
||||||
|
this._ids = -1;
|
||||||
|
this._focused = null;
|
||||||
|
|
||||||
|
zone.runOutsideAngular(() => {
|
||||||
|
if (this._config.get('keyboardScrollAssist')) {
|
||||||
|
this.initHolders(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._config.get('keyboardInputListener') !== false) {
|
||||||
|
this.initKeyInput(document);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
initKeyInput(document) {
|
||||||
|
/* Focus Outline
|
||||||
|
* --------------------------------------------------
|
||||||
|
* When a keydown event happens, from a tab key, then the
|
||||||
|
* 'key-input' class is added to the body element so focusable
|
||||||
|
* elements have an outline. On a mousedown or touchstart
|
||||||
|
* event then the 'key-input' class is removed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let isKeyInputEnabled = false;
|
||||||
|
|
||||||
|
function keyDown(ev) {
|
||||||
|
if (!isKeyInputEnabled && ev.keyCode == 9) {
|
||||||
|
isKeyInputEnabled = true;
|
||||||
|
raf(enableKeyInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointerDown() {
|
||||||
|
isKeyInputEnabled = false;
|
||||||
|
raf(enableKeyInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function enableKeyInput() {
|
||||||
|
document.body.classList[isKeyInputEnabled ? 'add' : 'remove']('key-input');
|
||||||
|
|
||||||
|
document.removeEventListener('mousedown', pointerDown);
|
||||||
|
document.removeEventListener('touchstart', pointerDown);
|
||||||
|
|
||||||
|
if (isKeyInputEnabled) {
|
||||||
|
document.addEventListener('mousedown', pointerDown);
|
||||||
|
document.addEventListener('touchstart', pointerDown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', keyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
initHolders(document) {
|
||||||
|
// raw DOM fun
|
||||||
|
this._holder = document.createElement('focus-holder');
|
||||||
|
|
||||||
|
this._prev = document.createElement('input');
|
||||||
|
this._prev.tabIndex = NO_FOCUS_TAB_INDEX;
|
||||||
|
this._holder.appendChild(this._prev);
|
||||||
|
|
||||||
|
this._next = document.createElement('input');
|
||||||
|
this._next.tabIndex = NO_FOCUS_TAB_INDEX;
|
||||||
|
this._holder.appendChild(this._next);
|
||||||
|
|
||||||
|
this._temp = document.createElement('input');
|
||||||
|
this._temp.tabIndex = NO_FOCUS_TAB_INDEX;
|
||||||
|
this._holder.appendChild(this._temp);
|
||||||
|
|
||||||
|
document.body.appendChild(this._holder);
|
||||||
|
|
||||||
|
function preventDefault(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._prev.addEventListener('keydown', preventDefault);
|
||||||
|
this._next.addEventListener('keydown', preventDefault);
|
||||||
|
this._temp.addEventListener('keydown', preventDefault);
|
||||||
|
|
||||||
|
this._prev.addEventListener('focus', () => {
|
||||||
|
this.focusPrevious();
|
||||||
|
});
|
||||||
|
|
||||||
|
this._next.addEventListener('focus', () => {
|
||||||
|
this.focusNext();
|
||||||
|
});
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
let resetTimer;
|
||||||
|
function queueReset() {
|
||||||
|
clearTimeout(resetTimer);
|
||||||
|
|
||||||
|
resetTimer = setTimeout(function() {
|
||||||
|
self._zone.run(() => {
|
||||||
|
self.resetInputs();
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('focusin', queueReset);
|
||||||
|
document.addEventListener('focusout', queueReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocusHolder(type) {
|
||||||
|
if (this._temp) {
|
||||||
|
this._temp.tabIndex = TEMP_TAB_INDEX;
|
||||||
|
|
||||||
|
this._temp.type = type || 'text';
|
||||||
|
console.debug('setFocusHolder', this._temp.type);
|
||||||
|
this._temp.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {TODO} input TODO
|
||||||
|
*/
|
||||||
|
register(input) {
|
||||||
|
console.debug('register input', input);
|
||||||
|
|
||||||
|
input.inputId = ++this._ids;
|
||||||
|
input.tabIndex = NORMAL_FOCUS_TAB_INDEX;
|
||||||
|
this._inputs.push(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
deregister(input) {
|
||||||
|
console.debug('deregister input', input);
|
||||||
|
|
||||||
|
let index = this._inputs.indexOf(input);
|
||||||
|
if (index > -1) {
|
||||||
|
this._inputs.splice(index, 1);
|
||||||
|
}
|
||||||
|
if (input === this._focused) {
|
||||||
|
this._focused = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resetInputs() {
|
||||||
|
this._focused = null;
|
||||||
|
|
||||||
|
for (let i = 0, ii = this._inputs.length; i < ii; i++) {
|
||||||
|
if (!this._focused && this._inputs[i].hasFocus) {
|
||||||
|
this._focused = this._inputs[i];
|
||||||
|
this._focused.tabIndex = ACTIVE_FOCUS_TAB_INDEX;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this._inputs[i].tabIndex = NORMAL_FOCUS_TAB_INDEX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._temp) {
|
||||||
|
this._temp.tabIndex = NO_FOCUS_TAB_INDEX;
|
||||||
|
|
||||||
|
if (this._focused) {
|
||||||
|
// there's a focused input
|
||||||
|
this._prev.tabIndex = PREV_TAB_INDEX;
|
||||||
|
this._next.tabIndex = NEXT_TAB_INDEX;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this._prev.tabIndex = this._next.tabIndex = NO_FOCUS_TAB_INDEX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focuses the previous input element, if it exists.
|
||||||
|
*/
|
||||||
|
focusPrevious() {
|
||||||
|
console.debug('focusPrevious');
|
||||||
|
this.focusMove(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focuses the next input element, if it exists.
|
||||||
|
*/
|
||||||
|
focusNext() {
|
||||||
|
console.debug('focusNext');
|
||||||
|
this.focusMove(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Number} inc TODO
|
||||||
|
*/
|
||||||
|
focusMove(inc) {
|
||||||
|
let input = this._focused;
|
||||||
|
if (input) {
|
||||||
|
let index = this._inputs.indexOf(input);
|
||||||
|
if (index > -1 && (index + inc) < this._inputs.length) {
|
||||||
|
let siblingInput = this._inputs[index + inc];
|
||||||
|
if (siblingInput) {
|
||||||
|
siblingInput.initFocus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._focused.initFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const NO_FOCUS_TAB_INDEX = -1;
|
||||||
|
const NORMAL_FOCUS_TAB_INDEX = 0;
|
||||||
|
const PREV_TAB_INDEX = 999;
|
||||||
|
const ACTIVE_FOCUS_TAB_INDEX = 1000;
|
||||||
|
const NEXT_TAB_INDEX = 1001;
|
||||||
|
const TEMP_TAB_INDEX = 2000;
|
||||||
|
|
@ -1,87 +0,0 @@
|
|||||||
import {Directive, ElementRef, Query, QueryList} from 'angular2/angular2';
|
|
||||||
|
|
||||||
import {Ion} from '../ion';
|
|
||||||
import {IonicApp} from '../app/app';
|
|
||||||
import {IonicConfig} from '../../config/config';
|
|
||||||
|
|
||||||
|
|
||||||
let inputRegistry = [];
|
|
||||||
let inputItemIds = -1;
|
|
||||||
let activeInput = null;
|
|
||||||
let lastInput = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Input component is used to focus text input elements.
|
|
||||||
*
|
|
||||||
* The `focusNext()` and `focusPrevious()` methods make it easy to focus input elements across all devices.
|
|
||||||
*
|
|
||||||
* @usage
|
|
||||||
* ```html
|
|
||||||
* <ion-input>
|
|
||||||
* <ion-label>Name</ion-label>
|
|
||||||
* <input value="Name" type="text">
|
|
||||||
* </ion-input>
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
export class IonInput extends Ion {
|
|
||||||
/**
|
|
||||||
* @param {TODO} input TODO
|
|
||||||
*/
|
|
||||||
static registerInput(input) {
|
|
||||||
inputRegistry.push(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {TODO} input TODO
|
|
||||||
*/
|
|
||||||
static setAsLastInput(input) {
|
|
||||||
lastInput = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Focuses the previous input element, if it exists.
|
|
||||||
*/
|
|
||||||
static focusPrevious() {
|
|
||||||
this.focusMove(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Focuses the next input element, if it exists.
|
|
||||||
*/
|
|
||||||
static focusNext() {
|
|
||||||
this.focusMove(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Number} inc TODO
|
|
||||||
*/
|
|
||||||
static focusMove(inc) {
|
|
||||||
let input = activeInput || lastInput;
|
|
||||||
if (input) {
|
|
||||||
|
|
||||||
let index = inputRegistry.indexOf(input);
|
|
||||||
if (index > -1 && (index + inc) < inputRegistry.length) {
|
|
||||||
let siblingInput = inputRegistry[index + inc];
|
|
||||||
siblingInput && siblingInput.initFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Number} The ID of the next input element.
|
|
||||||
*/
|
|
||||||
static nextId() {
|
|
||||||
return ++inputItemIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
static clearTabIndexes() {
|
|
||||||
for (let i = 0; i < inputRegistry.length; i++) {
|
|
||||||
inputRegistry[i].tabIndex = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -10,8 +10,7 @@ import {
|
|||||||
forwardRef
|
forwardRef
|
||||||
} from 'angular2/angular2';
|
} from 'angular2/angular2';
|
||||||
|
|
||||||
import {Ion} from '../ion';
|
import {IonicForm} from '../form/form';
|
||||||
import {IonInput} from '../form/input';
|
|
||||||
import {IonicConfig} from '../../config/config';
|
import {IonicConfig} from '../../config/config';
|
||||||
import {pointerCoord} from '../../util/dom';
|
import {pointerCoord} from '../../util/dom';
|
||||||
|
|
||||||
@ -109,7 +108,7 @@ class MediaSwitch {
|
|||||||
'</media-switch>',
|
'</media-switch>',
|
||||||
directives: [MediaSwitch]
|
directives: [MediaSwitch]
|
||||||
})
|
})
|
||||||
export class Switch extends Ion {
|
export class Switch {
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
* @param {ElementRef} elementRef TODO
|
* @param {ElementRef} elementRef TODO
|
||||||
@ -117,24 +116,24 @@ export class Switch extends Ion {
|
|||||||
* @param {NgControl=} ngControl TODO
|
* @param {NgControl=} ngControl TODO
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
|
form: IonicForm,
|
||||||
elementRef: ElementRef,
|
elementRef: ElementRef,
|
||||||
config: IonicConfig,
|
config: IonicConfig,
|
||||||
@Optional() private ngControl: NgControl
|
@Optional() private ngControl: NgControl
|
||||||
) {
|
) {
|
||||||
super(elementRef, config);
|
this.form = form;
|
||||||
let self = this;
|
form.register(this);
|
||||||
|
|
||||||
self.id = IonInput.nextId();
|
this.lastTouch = 0;
|
||||||
self.tabIndex = 0;
|
this.mode = config.get('mode');
|
||||||
self.lastTouch = 0;
|
|
||||||
self.mode = config.get('mode');
|
|
||||||
|
|
||||||
self.onChange = (_) => {};
|
this.onChange = (_) => {};
|
||||||
self.onTouched = (_) => {};
|
this.onTouched = (_) => {};
|
||||||
|
|
||||||
if (ngControl) ngControl.valueAccessor = this;
|
if (ngControl) ngControl.valueAccessor = this;
|
||||||
|
|
||||||
|
|
||||||
|
let self = this;
|
||||||
function pointerMove(ev) {
|
function pointerMove(ev) {
|
||||||
let currentX = pointerCoord(ev).x;
|
let currentX = pointerCoord(ev).x;
|
||||||
|
|
||||||
@ -156,21 +155,20 @@ export class Switch extends Ion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.addMoveListener = function() {
|
this.addMoveListener = function() {
|
||||||
this.switchEle.addEventListener('touchmove', pointerMove);
|
self.switchEle.addEventListener('touchmove', pointerMove);
|
||||||
this.switchEle.addEventListener('mousemove', pointerMove);
|
self.switchEle.addEventListener('mousemove', pointerMove);
|
||||||
elementRef.nativeElement.addEventListener('mouseout', pointerOut);
|
elementRef.nativeElement.addEventListener('mouseout', pointerOut);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.removeMoveListener = function() {
|
this.removeMoveListener = function() {
|
||||||
this.switchEle.removeEventListener('touchmove', pointerMove);
|
self.switchEle.removeEventListener('touchmove', pointerMove);
|
||||||
this.switchEle.removeEventListener('mousemove', pointerMove);
|
self.switchEle.removeEventListener('mousemove', pointerMove);
|
||||||
elementRef.nativeElement.removeEventListener('mouseout', pointerOut);
|
elementRef.nativeElement.removeEventListener('mouseout', pointerOut);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onInit() {
|
onInit() {
|
||||||
super.onInit();
|
this.labelId = 'label-' + this.inputId;
|
||||||
this.labelId = 'label-' + this.id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,6 +232,7 @@ export class Switch extends Ion {
|
|||||||
onDestroy() {
|
onDestroy() {
|
||||||
this.removeMoveListener();
|
this.removeMoveListener();
|
||||||
this.switchEle = this.addMoveListener = this.removeMoveListener = null;
|
this.switchEle = this.addMoveListener = this.removeMoveListener = null;
|
||||||
|
this.form.deregister(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
isDisabled(ev) {
|
isDisabled(ev) {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import {Directive, View, Host, Optional, ElementRef, Attribute, Query, QueryList, NgZone} from 'angular2/angular2';
|
import {Directive, View, Host, Optional, ElementRef, Attribute, Query, QueryList, NgZone} from 'angular2/angular2';
|
||||||
|
|
||||||
import {IonicConfig} from '../../config/config';
|
import {IonicConfig} from '../../config/config';
|
||||||
import {IonInput} from '../form/input';
|
import {IonicForm} from '../form/form';
|
||||||
import {Label} from './label';
|
import {Label} from './label';
|
||||||
import {Ion} from '../ion';
|
|
||||||
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 * as dom from '../../util/dom';
|
||||||
@ -15,22 +14,18 @@ import {IonicPlatform} from '../../platform/platform';
|
|||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'ion-input',
|
selector: 'ion-input',
|
||||||
inputs: [
|
|
||||||
'tabIndex'
|
|
||||||
],
|
|
||||||
host: {
|
host: {
|
||||||
'(focus)': 'receivedFocus(true)',
|
'(focus)': 'receivedFocus(true)',
|
||||||
'(blur)': 'receivedFocus(false)',
|
'(blur)': 'receivedFocus(false)',
|
||||||
'(touchstart)': 'pointerStart($event)',
|
'(touchstart)': 'pointerStart($event)',
|
||||||
'(touchend)': 'pointerEnd($event)',
|
'(touchend)': 'pointerEnd($event)',
|
||||||
'(mouseup)': 'pointerEnd($event)',
|
'(mouseup)': 'pointerEnd($event)',
|
||||||
'[class.has-focus]': 'inputHasFocus',
|
'[class.has-focus]': 'hasFocus',
|
||||||
'[class.has-value]': 'inputHasValue',
|
'[class.has-value]': 'hasValue',
|
||||||
'[tabIndex]': 'activeTabIndex',
|
|
||||||
'class': 'item'
|
'class': 'item'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class TextInput extends Ion {
|
export class TextInput {
|
||||||
/**
|
/**
|
||||||
* TODO
|
* TODO
|
||||||
* @param {ElementRef} elementRef TODO
|
* @param {ElementRef} elementRef TODO
|
||||||
@ -42,31 +37,30 @@ export class TextInput extends Ion {
|
|||||||
* @param {QueryList<Label>} labelQry TODO
|
* @param {QueryList<Label>} labelQry TODO
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
|
form: IonicForm,
|
||||||
elementRef: ElementRef,
|
elementRef: ElementRef,
|
||||||
config: IonicConfig,
|
config: IonicConfig,
|
||||||
app: IonicApp,
|
app: IonicApp,
|
||||||
ngZone: NgZone,
|
zone: NgZone,
|
||||||
platform: IonicPlatform,
|
platform: IonicPlatform,
|
||||||
@Optional() @Host() scrollView: Content
|
@Optional() @Host() scrollView: Content
|
||||||
) {
|
) {
|
||||||
super(elementRef, config);
|
this.form = form;
|
||||||
|
form.register(this);
|
||||||
|
|
||||||
|
this.app = app;
|
||||||
|
this.elementRef = elementRef;
|
||||||
|
this.zone = zone;
|
||||||
|
this.platform = platform;
|
||||||
|
|
||||||
this.scrollView = scrollView;
|
this.scrollView = scrollView;
|
||||||
this.scrollAssist = config.get('keyboardScrollAssist');
|
this.scrollAssist = config.get('keyboardScrollAssist');
|
||||||
this.id = IonInput.nextId();
|
this.keyboardHeight = config.get('keyboardHeight');
|
||||||
IonInput.registerInput(this);
|
|
||||||
|
|
||||||
this.app = app;
|
|
||||||
this.zone = ngZone;
|
|
||||||
this.platform = platform;
|
|
||||||
|
|
||||||
this.keyboardHeight = this.config.get('keyboardHeight');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerInputElement(textInputElement) {
|
registerInput(textInputElement) {
|
||||||
this.input = textInputElement;
|
this.input = textInputElement;
|
||||||
this.type = textInputElement.type;
|
this.type = textInputElement.type;
|
||||||
textInputElement.tabIndex = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerLabel(label) {
|
registerLabel(label) {
|
||||||
@ -77,17 +71,15 @@ export class TextInput extends Ion {
|
|||||||
* TODO
|
* TODO
|
||||||
*/
|
*/
|
||||||
onInit() {
|
onInit() {
|
||||||
super.onInit();
|
|
||||||
|
|
||||||
if (this.input && this.label) {
|
if (this.input && this.label) {
|
||||||
this.input.labelledBy = this.label.id = (this.label.id || 'label-' + this.id);
|
this.input.labelledBy = this.label.id = (this.label.id || 'label-' + this.inputId);
|
||||||
}
|
}
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
self.scrollMove = (ev) => {
|
self.scrollMove = (ev) => {
|
||||||
self.deregListeners();
|
self.deregListeners();
|
||||||
|
|
||||||
if (self.inputHasFocus) {
|
if (self.hasFocus) {
|
||||||
self.tempFocusMove();
|
self.tempFocusMove();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -119,7 +111,7 @@ export class TextInput extends Ion {
|
|||||||
|
|
||||||
// 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.inputHasFocus) {
|
if (!dom.hasPointerMoved(8, this.startCoord, endCoord) && !this.hasFocus) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
@ -149,7 +141,6 @@ export class TextInput extends Ion {
|
|||||||
* TODO
|
* TODO
|
||||||
* @returns {TODO} TODO
|
* @returns {TODO} TODO
|
||||||
*/
|
*/
|
||||||
//TODO inconsistent return value, sometimes undefined
|
|
||||||
initFocus() {
|
initFocus() {
|
||||||
let scrollView = this.scrollView;
|
let scrollView = this.scrollView;
|
||||||
|
|
||||||
@ -327,12 +318,12 @@ export class TextInput extends Ion {
|
|||||||
setFocus() {
|
setFocus() {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
// set focus on the input element
|
// set focus on the input element
|
||||||
this.input && this.input.setFocus();
|
this.input && this.input.initFocus();
|
||||||
|
|
||||||
// ensure the body hasn't scrolled down
|
// ensure the body hasn't scrolled down
|
||||||
document.body.scrollTop = 0;
|
document.body.scrollTop = 0;
|
||||||
|
|
||||||
IonInput.setAsLastInput(this);
|
this.form.resetInputs();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.scrollAssist && this.scrollView) {
|
if (this.scrollAssist && this.scrollView) {
|
||||||
@ -343,25 +334,26 @@ export class TextInput extends Ion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
*/
|
|
||||||
tempFocusMove() {
|
tempFocusMove() {
|
||||||
let focusHolder = this.app.focusHolder();
|
this.form.setFocusHolder(this.type);
|
||||||
focusHolder.setFocusHolder(this.type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get inputHasFocus() {
|
get hasFocus() {
|
||||||
return !!this.input && this.input.hasFocus;
|
return !!this.input && this.input.hasFocus;
|
||||||
}
|
}
|
||||||
|
|
||||||
get inputHasValue() {
|
get hasValue() {
|
||||||
return !!this.input && this.input.hasValue;
|
return !!this.input && this.input.hasValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeTabIndex() {
|
get tabIndex() {
|
||||||
this.input.tabIndex = (this.inputHasFocus ? 1000 : -1);
|
return this.input && this.input.tabIndex;
|
||||||
return -1;
|
}
|
||||||
|
|
||||||
|
set tabIndex(val) {
|
||||||
|
if (this.input) {
|
||||||
|
this.input.tabIndex = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -369,7 +361,7 @@ export class TextInput extends Ion {
|
|||||||
* @param {boolean} receivedFocus TODO
|
* @param {boolean} receivedFocus TODO
|
||||||
*/
|
*/
|
||||||
receivedFocus(receivedFocus) {
|
receivedFocus(receivedFocus) {
|
||||||
if (receivedFocus && !this.inputHasFocus) {
|
if (receivedFocus && !this.hasFocus) {
|
||||||
this.initFocus();
|
this.initFocus();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -377,6 +369,11 @@ export class TextInput extends Ion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDestroy() {
|
||||||
|
this.deregListeners();
|
||||||
|
this.form.deregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -391,31 +388,33 @@ export class TextInput extends Ion {
|
|||||||
host: {
|
host: {
|
||||||
'[tabIndex]': 'tabIndex',
|
'[tabIndex]': 'tabIndex',
|
||||||
'[attr.aria-labelledby]': 'labelledBy',
|
'[attr.aria-labelledby]': 'labelledBy',
|
||||||
'class': 'text-input input'
|
'class': 'text-input'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class TextInputElement {
|
export class TextInputElement {
|
||||||
/**
|
|
||||||
* TODO
|
|
||||||
* @param {string} type The value of the underlying element's type attribute.
|
|
||||||
* @param {ElementRef} elementRef TODO
|
|
||||||
* @param {IonicConfig} config TODO
|
|
||||||
*/
|
|
||||||
constructor(
|
constructor(
|
||||||
|
form: IonicForm,
|
||||||
@Attribute('type') type: string,
|
@Attribute('type') type: string,
|
||||||
elementRef: ElementRef,
|
elementRef: ElementRef,
|
||||||
@Optional() textInput: TextInput
|
@Optional() textInputWrapper: TextInput
|
||||||
) {
|
) {
|
||||||
|
this.form = form;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.elementRef = elementRef;
|
this.elementRef = elementRef;
|
||||||
this.tabIndex = this.tabIndex || '';
|
this.tabIndex = 0;
|
||||||
textInput && textInput.registerInputElement(this);
|
|
||||||
|
if (textInputWrapper) {
|
||||||
|
// it's within ionic's ion-input, let ion-input handle what's up
|
||||||
|
textInputWrapper.registerInput(this);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// not within ion-input
|
||||||
|
form.register(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
initFocus() {
|
||||||
* Focus the input.
|
|
||||||
*/
|
|
||||||
setFocus() {
|
|
||||||
this.elementRef.nativeElement.focus();
|
this.elementRef.nativeElement.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,7 +423,7 @@ export class TextInputElement {
|
|||||||
* @returns {boolean} true if the input has focus, otherwise false.
|
* @returns {boolean} true if the input has focus, otherwise false.
|
||||||
*/
|
*/
|
||||||
get hasFocus() {
|
get hasFocus() {
|
||||||
return dom.hasFocus(this.elementRef);
|
return dom.hasFocus(this.elementRef.nativeElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -434,6 +433,10 @@ export class TextInputElement {
|
|||||||
get hasValue() {
|
get hasValue() {
|
||||||
return (this.elementRef.nativeElement.value !== '');
|
return (this.elementRef.nativeElement.value !== '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDestroy() {
|
||||||
|
this.form.deregister(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import {IonicApp} from '../components/app/app';
|
|||||||
import {IonicConfig} from './config';
|
import {IonicConfig} from './config';
|
||||||
import {IonicPlatform} from '../platform/platform';
|
import {IonicPlatform} from '../platform/platform';
|
||||||
import {OverlayController} from '../components/overlay/overlay-controller';
|
import {OverlayController} from '../components/overlay/overlay-controller';
|
||||||
|
import {IonicForm} from '../components/form/form';
|
||||||
import {ActionSheet} from '../components/action-sheet/action-sheet';
|
import {ActionSheet} from '../components/action-sheet/action-sheet';
|
||||||
import {Modal} from '../components/modal/modal';
|
import {Modal} from '../components/modal/modal';
|
||||||
import {Popup} from '../components/popup/popup';
|
import {Popup} from '../components/popup/popup';
|
||||||
@ -45,6 +46,7 @@ export function ionicBindings(rootCmp, config) {
|
|||||||
bind(IonicPlatform).toValue(platform),
|
bind(IonicPlatform).toValue(platform),
|
||||||
bind(TapClick).toValue(tapClick),
|
bind(TapClick).toValue(tapClick),
|
||||||
bind(Events).toValue(events),
|
bind(Events).toValue(events),
|
||||||
|
IonicForm,
|
||||||
OverlayController,
|
OverlayController,
|
||||||
ActionSheet,
|
ActionSheet,
|
||||||
Modal,
|
Modal,
|
||||||
@ -92,10 +94,6 @@ function setupDom(window, document, config, platform) {
|
|||||||
bodyEle.classList.add('enable-hover');
|
bodyEle.classList.add('enable-hover');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.get('keyboardScrollAssist')) {
|
|
||||||
// create focus holder
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hairline Shim
|
* Hairline Shim
|
||||||
* Add the "hairline" CSS class name to the body tag
|
* Add the "hairline" CSS class name to the body tag
|
||||||
|
@ -14,7 +14,6 @@ export * from './platform/plugins'
|
|||||||
export * from './platform/storage'
|
export * from './platform/storage'
|
||||||
|
|
||||||
export * from './util/click-block'
|
export * from './util/click-block'
|
||||||
export * from './util/focus'
|
|
||||||
export * from './util/events'
|
export * from './util/events'
|
||||||
|
|
||||||
export * from './animations/animation'
|
export * from './animations/animation'
|
||||||
|
@ -176,8 +176,12 @@ export function hasPointerMoved(threshold, startCoord, endCoord) {
|
|||||||
(Math.abs(startCoord.x - endCoord.x) > threshold || Math.abs(startCoord.y - endCoord.y) > threshold);
|
(Math.abs(startCoord.x - endCoord.x) > threshold || Math.abs(startCoord.y - endCoord.y) > threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isActive(ele) {
|
||||||
|
return !!(ele && (document.activeElement === ele));
|
||||||
|
}
|
||||||
|
|
||||||
export function hasFocus(ele) {
|
export function hasFocus(ele) {
|
||||||
return !!(ele && (document.activeElement === ele.nativeElement || document.activeElement === ele));
|
return isActive(ele) && (ele.parentElement.querySelector(':focus') === ele);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isTextInput(ele) {
|
export function isTextInput(ele) {
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
import {raf, ready} from './dom'
|
|
||||||
|
|
||||||
/* Focus Outline
|
|
||||||
* --------------------------------------------------
|
|
||||||
* When a keydown event happens, from a tab key, then the
|
|
||||||
* 'key-input' class is added to the body element so focusable
|
|
||||||
* elements have an outline. On a mousedown or touchstart
|
|
||||||
* event then the 'key-input' class is removed.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
let isKeyInputEnabled = false
|
|
||||||
|
|
||||||
function keyDown(ev) {
|
|
||||||
if (!isKeyInputEnabled && ev.keyCode == 9) {
|
|
||||||
isKeyInputEnabled = true
|
|
||||||
raf(enableKeyInput)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function enableKeyInput() {
|
|
||||||
document.body.classList[isKeyInputEnabled ? 'add' : 'remove']('key-input')
|
|
||||||
|
|
||||||
if (isKeyInputEnabled) {
|
|
||||||
document.addEventListener('mousedown', pointerDown)
|
|
||||||
document.addEventListener('touchstart', pointerDown)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
document.removeEventListener('mousedown', pointerDown)
|
|
||||||
document.removeEventListener('touchstart', pointerDown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function pointerDown() {
|
|
||||||
isKeyInputEnabled = false
|
|
||||||
raf(enableKeyInput)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ready().then(function() {
|
|
||||||
document.addEventListener('keydown', keyDown)
|
|
||||||
})
|
|
Reference in New Issue
Block a user