mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
wip
This commit is contained in:
106
ionic/animations/scroll-to.ts
Normal file
106
ionic/animations/scroll-to.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import {raf} from '../util/dom';
|
||||||
|
|
||||||
|
|
||||||
|
export class ScrollTo {
|
||||||
|
|
||||||
|
constructor(ele, x, y, duration) {
|
||||||
|
if (typeof ele === 'string') {
|
||||||
|
// string query selector
|
||||||
|
ele = document.querySelector(ele);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ele) {
|
||||||
|
if (ele.nativeElement) {
|
||||||
|
// angular ElementRef
|
||||||
|
ele = ele.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ele.nodeType === 1) {
|
||||||
|
this._el = ele;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start(x, y, duration, tolerance) {
|
||||||
|
// scroll animation loop w/ easing
|
||||||
|
// credit https://gist.github.com/dezinezync/5487119
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
if (!self._el) {
|
||||||
|
// invalid element
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
x = x || 0;
|
||||||
|
y = y || 0;
|
||||||
|
tolerance = tolerance || 0;
|
||||||
|
|
||||||
|
let ele = self._el;
|
||||||
|
let fromY = ele.scrollTop;
|
||||||
|
let fromX = ele.scrollLeft;
|
||||||
|
|
||||||
|
let xDistance = Math.abs(x - fromX);
|
||||||
|
let yDistance = Math.abs(y - fromY);
|
||||||
|
|
||||||
|
if (yDistance <= tolerance && xDistance <= tolerance) {
|
||||||
|
// prevent scrolling if already close to there
|
||||||
|
this._el = ele = null;
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
let start = Date.now();
|
||||||
|
|
||||||
|
// start scroll loop
|
||||||
|
self.isPlaying = true;
|
||||||
|
raf(step);
|
||||||
|
|
||||||
|
// decelerating to zero velocity
|
||||||
|
function easeOutCubic(t) {
|
||||||
|
return (--t) * t * t + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll loop
|
||||||
|
function step() {
|
||||||
|
let time = Math.min(1, ((Date.now() - start) / duration));
|
||||||
|
|
||||||
|
// where .5 would be 50% of time on a linear scale easedT gives a
|
||||||
|
// fraction based on the easing method
|
||||||
|
let easedT = easeOutCubic(time);
|
||||||
|
|
||||||
|
if (fromY != y) {
|
||||||
|
ele.scrollTop = parseInt((easedT * (y - fromY)) + fromY, 10);
|
||||||
|
}
|
||||||
|
if (fromX != x) {
|
||||||
|
ele.scrollLeft = parseInt((easedT * (x - fromX)) + fromX, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time < 1 && self.isPlaying) {
|
||||||
|
raf(step);
|
||||||
|
|
||||||
|
} else if (!self.isPlaying) {
|
||||||
|
// stopped
|
||||||
|
this._el = ele = null;
|
||||||
|
reject();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// done
|
||||||
|
this._el = ele = null;
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.isPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.stop();
|
||||||
|
this._el = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,8 +8,8 @@ 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'
|
||||||
export * from 'ionic/components/form/form'
|
export * from 'ionic/components/form/form'
|
||||||
export * from 'ionic/components/form/input/input'
|
export * from 'ionic/components/form/input'
|
||||||
export * from 'ionic/components/form/label/label'
|
export * from 'ionic/components/form/label'
|
||||||
export * from 'ionic/components/list/list'
|
export * from 'ionic/components/list/list'
|
||||||
export * from 'ionic/components/modal/modal'
|
export * from 'ionic/components/modal/modal'
|
||||||
export * from 'ionic/components/nav/nav'
|
export * from 'ionic/components/nav/nav'
|
||||||
|
@ -11,6 +11,7 @@ import * as util from '../../util/util';
|
|||||||
// injectables
|
// injectables
|
||||||
import {ActionMenu} from '../action-menu/action-menu';
|
import {ActionMenu} from '../action-menu/action-menu';
|
||||||
import {Modal} from '../modal/modal';
|
import {Modal} from '../modal/modal';
|
||||||
|
import {FocusHolder} from '../form/focus-holder';
|
||||||
|
|
||||||
|
|
||||||
export class IonicApp {
|
export class IonicApp {
|
||||||
@ -29,6 +30,13 @@ export class IonicApp {
|
|||||||
this._zone = this.injector().get(NgZone);
|
this._zone = this.injector().get(NgZone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focusHolder(val) {
|
||||||
|
if (arguments.length) {
|
||||||
|
this._focusHolder = val;
|
||||||
|
}
|
||||||
|
return this._focusHolder;
|
||||||
|
}
|
||||||
|
|
||||||
title(val) {
|
title(val) {
|
||||||
document.title = val;
|
document.title = val;
|
||||||
}
|
}
|
||||||
@ -206,6 +214,13 @@ export function ionicBootstrap(component, config, router) {
|
|||||||
bootstrap(component, injectableBindings).then(appRef => {
|
bootstrap(component, injectableBindings).then(appRef => {
|
||||||
app.load(appRef);
|
app.load(appRef);
|
||||||
|
|
||||||
|
// append the focus holder if its needed
|
||||||
|
if (config.setting('keyboardScrollAssist')) {
|
||||||
|
app.appendComponent(FocusHolder).then(ref => {
|
||||||
|
app.focusHolder(ref.instance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
router.load(window, app, config).then(() => {
|
router.load(window, app, config).then(() => {
|
||||||
// resolve that the app has loaded
|
// resolve that the app has loaded
|
||||||
resolve(app);
|
resolve(app);
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
position: relative;
|
position: fixed;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
|
@ -90,7 +90,7 @@ $content-padding: 10px !default;
|
|||||||
left: 0;
|
left: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
z-index: $z-index-click-block;
|
z-index: $z-index-click-block;
|
||||||
transform: translate3d(-9999px, 0px, 0px);
|
transform: translate3d(-9999px, 0px, 0px);;
|
||||||
|
|
||||||
//background: red;
|
//background: red;
|
||||||
//opacity: .3;
|
//opacity: .3;
|
||||||
|
@ -3,13 +3,17 @@ import {Component, View, ElementRef} from 'angular2/angular2';
|
|||||||
import {Ion} from '../ion';
|
import {Ion} from '../ion';
|
||||||
import {IonicConfig} from '../../config/config';
|
import {IonicConfig} from '../../config/config';
|
||||||
import {IonicComponent} from '../../config/annotations';
|
import {IonicComponent} from '../../config/annotations';
|
||||||
|
import {ScrollTo} from '../../animations/scroll-to';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ion-content',
|
selector: 'ion-content',
|
||||||
properties: [
|
properties: [
|
||||||
'parallax'
|
'parallax'
|
||||||
]
|
],
|
||||||
|
host: {
|
||||||
|
['[class.scroll-padding]']: 'scrollPadding'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
template: '<div class="scroll-content"><content></content></div>'
|
template: '<div class="scroll-content"><content></content></div>'
|
||||||
@ -17,6 +21,7 @@ import {IonicComponent} from '../../config/annotations';
|
|||||||
export class Content extends Ion {
|
export class Content extends Ion {
|
||||||
constructor(elementRef: ElementRef, ionicConfig: IonicConfig) {
|
constructor(elementRef: ElementRef, ionicConfig: IonicConfig) {
|
||||||
super(elementRef, ionicConfig);
|
super(elementRef, ionicConfig);
|
||||||
|
this.scrollPadding = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onIonInit() {
|
onIonInit() {
|
||||||
@ -32,4 +37,33 @@ export class Content extends Ion {
|
|||||||
this.scrollElement.removeEventListener('scroll', handler);
|
this.scrollElement.removeEventListener('scroll', handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addTouchMoveListener(handler) {
|
||||||
|
if(!this.scrollElement) { return; }
|
||||||
|
|
||||||
|
this.scrollElement.addEventListener('touchmove', handler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
this.scrollElement.removeEventListener('touchmove', handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTo(x, y, duration, tolerance) {
|
||||||
|
if (this._scrollTo) {
|
||||||
|
this._scrollTo.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._scrollTo = new ScrollTo(this.scrollElement);
|
||||||
|
|
||||||
|
return this._scrollTo.start(x, y, duration, tolerance);
|
||||||
|
}
|
||||||
|
|
||||||
|
get scrollPadding() {
|
||||||
|
return this._sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
set scrollPadding(val) {
|
||||||
|
this._sp = val;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
108
ionic/components/form/focus-holder.ts
Normal file
108
ionic/components/form/focus-holder.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import {Component, Directive, View, Parent, ElementRef, forwardRef} from 'angular2/angular2';
|
||||||
|
|
||||||
|
import {IonicConfig} from '../../config/config';
|
||||||
|
import * as dom from '../../util/dom';
|
||||||
|
import {Platform} from '../../platform/platform';
|
||||||
|
import {IonInput} from './form';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'focus-holder'
|
||||||
|
})
|
||||||
|
@View({
|
||||||
|
template: '<input><input><input>',
|
||||||
|
directives: [forwardRef(() => FocusInput)]
|
||||||
|
})
|
||||||
|
export class FocusHolder {
|
||||||
|
constructor() {
|
||||||
|
this.i = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocusHolder(inputType) {
|
||||||
|
IonInput.clearTabIndexes();
|
||||||
|
|
||||||
|
this.i[1].tabIndex = ACTIVE_TAB_INDEX;
|
||||||
|
this.i[1].type = inputType;
|
||||||
|
this.i[1].focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveInput(input) {
|
||||||
|
IonInput.clearTabIndexes();
|
||||||
|
|
||||||
|
this.i[1].tabIndex = -1;
|
||||||
|
|
||||||
|
input.tabIndex = ACTIVE_TAB_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
receivedFocus(tabIndex) {
|
||||||
|
if (tabIndex === PREVIOUS_TAB_INDEX) {
|
||||||
|
// they tabbed back one input
|
||||||
|
// reset the focus to the center focus holder
|
||||||
|
this.i[1].focus();
|
||||||
|
|
||||||
|
// focus on the previous input
|
||||||
|
IonInput.focusPrevious();
|
||||||
|
|
||||||
|
} else if (tabIndex === NEXT_TAB_INDEX) {
|
||||||
|
// they tabbed to the next input
|
||||||
|
// reset the focus to the center focus holder
|
||||||
|
this.i[1].focus();
|
||||||
|
|
||||||
|
// focus on the next input
|
||||||
|
IonInput.focusNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register(input) {
|
||||||
|
// register each of the focus holder inputs
|
||||||
|
// assign them their correct tab indexes
|
||||||
|
input.tabIndex = PREVIOUS_TAB_INDEX + this.i.length;
|
||||||
|
this.i.push(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: 'input',
|
||||||
|
properties: [
|
||||||
|
'tabIndex'
|
||||||
|
],
|
||||||
|
host: {
|
||||||
|
'[tabIndex]': 'tabIndex',
|
||||||
|
'[type]': 'type',
|
||||||
|
'(focus)': 'holder.receivedFocus(tabIndex)',
|
||||||
|
'(keydown)': 'keydown($event)'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
class FocusInput {
|
||||||
|
constructor(elementRef: ElementRef, @Parent() holder: FocusHolder) {
|
||||||
|
this.elementRef = elementRef;
|
||||||
|
holder.register(this);
|
||||||
|
this.holder = holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.elementRef.nativeElement.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
keydown(ev) {
|
||||||
|
// prevent any keyboard typing when a holder has focus
|
||||||
|
if (ev.keyCode !== 9) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get type() {
|
||||||
|
// default to text type if unknown
|
||||||
|
return this._t || 'text';
|
||||||
|
}
|
||||||
|
|
||||||
|
set type(val) {
|
||||||
|
this._t = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PREVIOUS_TAB_INDEX = 999;
|
||||||
|
const ACTIVE_TAB_INDEX = 1000;
|
||||||
|
const NEXT_TAB_INDEX = 1001;
|
@ -1 +1,87 @@
|
|||||||
// form
|
import {IonicConfig} from '../../config/config';
|
||||||
|
import * as dom from '../../util/dom';
|
||||||
|
|
||||||
|
let inputRegistry = [];
|
||||||
|
let activeInput = null;
|
||||||
|
let lastInput = null;
|
||||||
|
|
||||||
|
|
||||||
|
export class IonInput {
|
||||||
|
constructor(
|
||||||
|
elementRef: ElementRef,
|
||||||
|
app: IonicApp,
|
||||||
|
scrollView: Content
|
||||||
|
) {
|
||||||
|
this.elementRef = elementRef;
|
||||||
|
this.app = app;
|
||||||
|
this.scrollView = scrollView;
|
||||||
|
|
||||||
|
inputRegistry.push(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasFocus() {
|
||||||
|
return dom.hasFocus(this.elementRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocus() {
|
||||||
|
// TODO: How do you do this w/ NG2?
|
||||||
|
this.elementRef.nativeElement.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocusHolder(type) {
|
||||||
|
let focusHolder = this.app.focusHolder();
|
||||||
|
focusHolder && focusHolder.setFocusHolder(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
isActiveInput(shouldBeActive) {
|
||||||
|
if (shouldBeActive) {
|
||||||
|
if (activeInput && activeInput !== lastInput) {
|
||||||
|
lastInput = activeInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
activeInput = this;
|
||||||
|
|
||||||
|
let focusHolder = this.app.focusHolder();
|
||||||
|
focusHolder && focusHolder.setActiveInput(activeInput);
|
||||||
|
|
||||||
|
} else if (activeInput === this) {
|
||||||
|
lastInput = activeInput;
|
||||||
|
activeInput = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sibling(inc) {
|
||||||
|
let index = inputRegistry.indexOf(this);
|
||||||
|
if (index > -1) {
|
||||||
|
return inputRegistry[index + inc];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static focusPrevious() {
|
||||||
|
this.focusMove(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static focusNext() {
|
||||||
|
this.focusMove(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static focusMove(inc) {
|
||||||
|
let input = activeInput || lastInput;
|
||||||
|
if (input) {
|
||||||
|
let siblingInput = input.sibling(inc);
|
||||||
|
siblingInput && siblingInput.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static clearTabIndexes() {
|
||||||
|
for (let i = 0; i < inputRegistry.length; i++) {
|
||||||
|
inputRegistry[i].tabIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,28 @@ $item-input-padding: 6px 0 5px 0px;
|
|||||||
|
|
||||||
ion-input {
|
ion-input {
|
||||||
display: block;
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.disable-focus,
|
||||||
|
textarea.disable-focus,
|
||||||
|
select.disable-focus {
|
||||||
|
//pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus-holder input {
|
||||||
|
position: fixed;
|
||||||
|
top: 1px;
|
||||||
|
width: 9px;
|
||||||
|
left: -9999px;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-padding.scroll-padding .scroll-content {
|
||||||
|
padding-bottom: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*ion-input {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -28,3 +50,4 @@ ion-input {
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
198
ionic/components/form/input.ts
Normal file
198
ionic/components/form/input.ts
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
import {Directive, View, Parent, Ancestor, Optional, ElementRef, Attribute, forwardRef} from 'angular2/angular2';
|
||||||
|
|
||||||
|
import {IonicDirective} from '../../config/annotations';
|
||||||
|
import {IonicConfig} from '../../config/config';
|
||||||
|
import {IonInput} from './form';
|
||||||
|
import {Ion} from '../ion';
|
||||||
|
import {IonicApp} from '../app/app';
|
||||||
|
import {Content} from '../content/content';
|
||||||
|
import {ClickBlock} from '../../util/click-block';
|
||||||
|
import * as dom from '../../util/dom';
|
||||||
|
import {Platform} from '../../platform/platform';
|
||||||
|
|
||||||
|
|
||||||
|
@IonicDirective({
|
||||||
|
selector: 'ion-input'
|
||||||
|
})
|
||||||
|
export class Input extends Ion {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
elementRef: ElementRef,
|
||||||
|
ionicConfig: IonicConfig
|
||||||
|
) {
|
||||||
|
super(elementRef, ionicConfig);
|
||||||
|
this.id = ++inputIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
onInit() {
|
||||||
|
if (this.input) {
|
||||||
|
this.input.id = 'input-' + this.id;
|
||||||
|
}
|
||||||
|
if (this.label) {
|
||||||
|
this.label.id = 'label-' + this.id;
|
||||||
|
this.input.labelledBy = this.label.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerInput(directive) {
|
||||||
|
this.input = directive;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerLabel(directive) {
|
||||||
|
this.label = directive;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: 'textarea,input[type=text],input[type=password],input[type=number],input[type=search],input[type=email],input[type=url]',
|
||||||
|
property: [
|
||||||
|
'tabIndex'
|
||||||
|
],
|
||||||
|
host: {
|
||||||
|
'[tabIndex]': 'tabIndex',
|
||||||
|
'(focus)': 'receivedFocus(true)',
|
||||||
|
'(blur)': 'receivedFocus(false)',
|
||||||
|
'(touchstart)': 'pointerStart($event)',
|
||||||
|
'(touchend)': 'pointerEnd($event)',
|
||||||
|
'(mousedown)': 'pointerStart($event)',
|
||||||
|
'(mouseup)': 'pointerEnd($event)',
|
||||||
|
'[attr.id]': 'id',
|
||||||
|
'[attr.aria-labelledby]': 'labelledBy',
|
||||||
|
'[class.disable-focus]': 'disableFocus'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class TextInput extends IonInput {
|
||||||
|
constructor(
|
||||||
|
@Optional() @Parent() container: Input,
|
||||||
|
@Optional() @Ancestor() scrollView: Content,
|
||||||
|
@Attribute('type') type: string,
|
||||||
|
elementRef: ElementRef,
|
||||||
|
app: IonicApp,
|
||||||
|
config: IonicConfig
|
||||||
|
) {
|
||||||
|
super(elementRef, app, scrollView);
|
||||||
|
|
||||||
|
if (container) {
|
||||||
|
container.registerInput(this);
|
||||||
|
this.container = container;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.type = type;
|
||||||
|
this.elementRef = elementRef;
|
||||||
|
this.tabIndex = this.tabIndex || '';
|
||||||
|
|
||||||
|
this.scrollAssist = config.setting('keyboardScrollAssist');
|
||||||
|
}
|
||||||
|
|
||||||
|
pointerStart(ev) {
|
||||||
|
if (this.scrollAssist) {
|
||||||
|
this.startCoord = dom.pointerCoord(ev);
|
||||||
|
this.disableFocus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pointerEnd(ev) {
|
||||||
|
if (this.scrollAssist) {
|
||||||
|
let endCoord = dom.pointerCoord(ev);
|
||||||
|
|
||||||
|
if (this.startCoord && !dom.hasPointerMoved(20, this.startCoord, endCoord) && !this.hasFocus()) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
this.focus();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.disableFocus = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startCoord = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
let scrollView = this.scrollView;
|
||||||
|
|
||||||
|
if (scrollView && this.scrollAssist) {
|
||||||
|
this.disableFocus = true;
|
||||||
|
|
||||||
|
// this input is inside of a scroll view
|
||||||
|
// scroll the input to the top
|
||||||
|
let inputY = this.elementRef.nativeElement.offsetTop - 8;
|
||||||
|
|
||||||
|
// do not allow any clicks while it's scrolling
|
||||||
|
ClickBlock(true, SCROLL_INTO_VIEW_DURATION + 200);
|
||||||
|
|
||||||
|
// used to put a lot of padding on the bottom of the scroll view
|
||||||
|
scrollView.scrollPadding = true;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
this.setFocusHolder(this.type);
|
||||||
|
|
||||||
|
// scroll the input into place
|
||||||
|
scrollView.scrollTo(0, inputY, SCROLL_INTO_VIEW_DURATION, 8).then(() => {
|
||||||
|
// the scroll view is in the correct position now
|
||||||
|
// give the native input the focus
|
||||||
|
this.setFocus();
|
||||||
|
|
||||||
|
// all good, allow clicks again
|
||||||
|
ClickBlock(false);
|
||||||
|
|
||||||
|
this.disableFocus = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// not inside of a scroll view, just focus it
|
||||||
|
this.setFocus();
|
||||||
|
this.disableFocus = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
receivedFocus(receivedFocus) {
|
||||||
|
let self = this;
|
||||||
|
let scrollView = self.scrollView;
|
||||||
|
|
||||||
|
self.isActiveInput(receivedFocus);
|
||||||
|
|
||||||
|
function touchMove(ev) {
|
||||||
|
self.setFocusHolder(self.type);
|
||||||
|
self.deregTouchMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scrollView && this.scrollAssist) {
|
||||||
|
if (receivedFocus) {
|
||||||
|
// when the input has focus, then the focus holder
|
||||||
|
// should not be able to be focused
|
||||||
|
self.deregTouchMove = scrollView && scrollView.addTouchMoveListener(touchMove);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// the input no longer has focus
|
||||||
|
self.deregTouchMove && self.deregTouchMove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: 'label',
|
||||||
|
host: {
|
||||||
|
'[attr.id]': 'id'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export class InputLabel {
|
||||||
|
constructor(@Optional() @Parent() container: Input) {
|
||||||
|
if (container) {
|
||||||
|
container.registerLabel(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SCROLL_INTO_VIEW_DURATION = 500;
|
||||||
|
|
||||||
|
let inputIds = -1;
|
@ -1,12 +0,0 @@
|
|||||||
import {Component, Directive} from 'angular2/angular2';
|
|
||||||
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: 'ion-input'
|
|
||||||
})
|
|
||||||
export class Input {
|
|
||||||
constructor() {
|
|
||||||
//this.config = Button.config.invoke(this)
|
|
||||||
console.log('INPUT');
|
|
||||||
}
|
|
||||||
}
|
|
7
ionic/components/form/label.ts
Normal file
7
ionic/components/form/label.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import {Directive} from 'angular2/angular2';
|
||||||
|
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: 'ion-label'
|
||||||
|
})
|
||||||
|
export class Label {}
|
@ -1,10 +0,0 @@
|
|||||||
import {Component, Directive} from 'angular2/angular2';
|
|
||||||
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: 'ion-label'
|
|
||||||
})
|
|
||||||
export class Label {
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
}
|
|
1
ionic/components/form/test/input/e2e.ts
Normal file
1
ionic/components/form/test/input/e2e.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
7
ionic/components/form/test/input/index.ts
Normal file
7
ionic/components/form/test/input/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import {App} from 'ionic/ionic';
|
||||||
|
|
||||||
|
|
||||||
|
@App({
|
||||||
|
templateUrl: 'main.html'
|
||||||
|
})
|
||||||
|
class IonicApp {}
|
92
ionic/components/form/test/input/main.html
Normal file
92
ionic/components/form/test/input/main.html
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
<ion-toolbar><ion-title>Header</ion-title></ion-toolbar>
|
||||||
|
|
||||||
|
|
||||||
|
<ion-content class="padding">
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<input value="1" type="text">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<input value="2" type="text">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<input value="3" type="number">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<textarea>4</textarea>
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<input value="5" type="text">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<input value="6" type="text">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<textarea>7</textarea>
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<input value="8" type="text">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<input value="9" type="number">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<textarea>10</textarea>
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<input value="11" type="text">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<input value="12" type="number">
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<textarea>13</textarea>
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
<ion-input>
|
||||||
|
<textarea>14</textarea>
|
||||||
|
</ion-input>
|
||||||
|
|
||||||
|
</ion-content>
|
||||||
|
|
||||||
|
|
||||||
|
<ion-toolbar><ion-title>Footer</ion-title></ion-toolbar>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
input, textarea {
|
||||||
|
border: 1px solid red !important;
|
||||||
|
display: inline-block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus-holder input:focus {
|
||||||
|
background: red !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus-holder input:nth-child(1) {
|
||||||
|
left: 200px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus-holder input:nth-child(2) {
|
||||||
|
left: 240px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
focus-holder input:nth-child(3) {
|
||||||
|
left: 280px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
@ -11,7 +11,8 @@ import {
|
|||||||
List, Item, ItemGroup, ItemGroupTitle,
|
List, Item, ItemGroup, ItemGroupTitle,
|
||||||
Toolbar,
|
Toolbar,
|
||||||
Icon,
|
Icon,
|
||||||
Checkbox, Switch, Label, Input,
|
Checkbox, Switch,
|
||||||
|
Input, TextInput, InputLabel,
|
||||||
Segment, SegmentButton, SegmentControlValueAccessor,
|
Segment, SegmentButton, SegmentControlValueAccessor,
|
||||||
RadioGroup, RadioButton, SearchBar,
|
RadioGroup, RadioButton, SearchBar,
|
||||||
Nav, NavbarTemplate, Navbar, NavPush, NavPop,
|
Nav, NavbarTemplate, Navbar, NavPush, NavPop,
|
||||||
@ -50,13 +51,18 @@ export const IonicDirectives = [
|
|||||||
// Media
|
// Media
|
||||||
forwardRef(() => Icon),
|
forwardRef(() => Icon),
|
||||||
|
|
||||||
// Form elements
|
// Form
|
||||||
forwardRef(() => Segment),
|
forwardRef(() => Segment),
|
||||||
forwardRef(() => SegmentButton),
|
forwardRef(() => SegmentButton),
|
||||||
forwardRef(() => SegmentControlValueAccessor),
|
forwardRef(() => SegmentControlValueAccessor),
|
||||||
//Checkbox, Switch, Label, Input
|
//Checkbox, Switch
|
||||||
//RadioGroup, RadioButton, SearchBar,
|
//RadioGroup, RadioButton, SearchBar,
|
||||||
|
|
||||||
|
// Input
|
||||||
|
forwardRef(() => Input),
|
||||||
|
forwardRef(() => TextInput),
|
||||||
|
forwardRef(() => InputLabel),
|
||||||
|
|
||||||
// Nav
|
// Nav
|
||||||
forwardRef(() => Nav),
|
forwardRef(() => Nav),
|
||||||
forwardRef(() => NavbarTemplate),
|
forwardRef(() => NavbarTemplate),
|
||||||
|
@ -33,8 +33,8 @@
|
|||||||
"components/content/content",
|
"components/content/content",
|
||||||
"components/item/item",
|
"components/item/item",
|
||||||
"components/form/form",
|
"components/form/form",
|
||||||
"components/form/label/label",
|
"components/form/label",
|
||||||
"components/form/input/input",
|
"components/form/input",
|
||||||
"components/layout/layout",
|
"components/layout/layout",
|
||||||
"components/list/list",
|
"components/list/list",
|
||||||
"components/modal/modal",
|
"components/modal/modal",
|
||||||
|
@ -71,7 +71,8 @@ Platform.register({
|
|||||||
settings: {
|
settings: {
|
||||||
mode: 'ios',
|
mode: 'ios',
|
||||||
viewTransition: 'ios',
|
viewTransition: 'ios',
|
||||||
tapPolyfill: true
|
tapPolyfill: true,
|
||||||
|
keyboardScrollAssist: true
|
||||||
},
|
},
|
||||||
isMatch(p) {
|
isMatch(p) {
|
||||||
return p.isPlatform('ios', 'iphone|ipad|ipod');
|
return p.isPlatform('ios', 'iphone|ipad|ipod');
|
||||||
|
@ -3,6 +3,10 @@ const DEFAULT_EXPIRE = 330;
|
|||||||
let cbEle, fallbackTimerId;
|
let cbEle, fallbackTimerId;
|
||||||
let isShowing = false;
|
let isShowing = false;
|
||||||
|
|
||||||
|
function disableInput(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
function show(expire) {
|
function show(expire) {
|
||||||
clearTimeout(fallbackTimerId);
|
clearTimeout(fallbackTimerId);
|
||||||
@ -18,6 +22,7 @@ function show(expire) {
|
|||||||
cbEle.className = 'click-block ' + CSS_CLICK_BLOCK;
|
cbEle.className = 'click-block ' + CSS_CLICK_BLOCK;
|
||||||
document.body.appendChild(cbEle);
|
document.body.appendChild(cbEle);
|
||||||
}
|
}
|
||||||
|
cbEle.addEventListener('touchmove', disableInput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,6 +31,7 @@ function hide() {
|
|||||||
if (isShowing) {
|
if (isShowing) {
|
||||||
cbEle.classList.remove(CSS_CLICK_BLOCK);
|
cbEle.classList.remove(CSS_CLICK_BLOCK);
|
||||||
isShowing = false;
|
isShowing = false;
|
||||||
|
cbEle.removeEventListener('touchmove', disableInput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,3 +158,27 @@ export function windowLoad(callback) {
|
|||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function pointerCoord(ev) {
|
||||||
|
// get coordinates for either a mouse click
|
||||||
|
// or a touch depending on the given event
|
||||||
|
let c = { x: 0, y: 0 };
|
||||||
|
if (ev) {
|
||||||
|
const touches = ev.touches && ev.touches.length ? ev.touches : [ev];
|
||||||
|
const e = (ev.changedTouches && ev.changedTouches[0]) || touches[0];
|
||||||
|
if (e) {
|
||||||
|
c.x = e.clientX || e.pageX || 0;
|
||||||
|
c.y = e.clientY || e.pageY || 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasPointerMoved(tolerance, startCoord, endCoord) {
|
||||||
|
return Math.abs(startCoord.x - endCoord.x) > tolerance ||
|
||||||
|
Math.abs(startCoord.y - endCoord.y) > tolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasFocus(ele) {
|
||||||
|
return !!(ele && (document.activeElement === ele.nativeElement || document.activeElement === ele));
|
||||||
|
}
|
||||||
|
@ -103,8 +103,8 @@ var tapEventListeners = {
|
|||||||
Platform.ready().then(config => {
|
Platform.ready().then(config => {
|
||||||
|
|
||||||
if (config.setting('tapPolyfill')) {
|
if (config.setting('tapPolyfill')) {
|
||||||
console.log('Tap.register, tapPolyfill')
|
// console.log('Tap.register, tapPolyfill')
|
||||||
Tap.register(document);
|
// Tap.register(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user