fix(textInput): hide cursor during scroll

This commit is contained in:
Adam Bradley
2015-11-16 22:46:56 -06:00
parent f11ca076da
commit 64376c2304
3 changed files with 92 additions and 46 deletions

View File

@ -2,6 +2,7 @@ import {Component, ElementRef, Optional, NgZone} from 'angular2/angular2';
import {Ion} from '../ion'; import {Ion} from '../ion';
import {Config} from '../../config/config'; import {Config} from '../../config/config';
import {raf} from '../../util/dom';
import {Keyboard} from '../../util/keyboard'; import {Keyboard} from '../../util/keyboard';
import {ViewController} from '../nav/view-controller'; import {ViewController} from '../nav/view-controller';
import {Animation} from '../../animations/animation'; import {Animation} from '../../animations/animation';
@ -71,19 +72,34 @@ export class Content extends Ion {
} }
} }
onScrollEnd(callback, debounceWait=400) { onScrollEnd(callback) {
let timerId, deregister; let lastScrollTop = null;
let framesUnchanged = 0;
let scrollElement = this.scrollElement;
function debounce() { function next() {
console.debug('onScroll') let currentScrollTop = scrollElement.scrollTop;
clearTimeout(timerId); if (lastScrollTop !== null) {
timerId = setTimeout(() => {
deregister(); if (Math.round(lastScrollTop) === Math.round(currentScrollTop)) {
callback(); framesUnchanged++;
}, debounceWait); } else {
framesUnchanged = 0;
}
if (framesUnchanged > 9) {
return callback();
}
}
lastScrollTop = currentScrollTop;
raf(() => {
raf(next);
});
} }
deregister = this.addScrollEventListener(debounce); setTimeout(next, 100);
} }
/** /**

View File

@ -83,11 +83,19 @@ export class TextInput {
let self = this; let self = this;
self.scrollMove = (ev) => { self.scrollMove = (ev) => {
console.debug('content scrollMove'); if (!self.app.isTransitioning()) {
self.deregListeners(); self.deregMove();
if (self.hasFocus) { if (self.hasFocus) {
//self.input.hideFocus(); self.input.hideFocus(true);
this.scrollView.onScrollEnd(() => {
self.input.hideFocus(false);
if (self.hasFocus) {
self.regMove();
}
});
}
} }
}; };
} }
@ -131,6 +139,7 @@ export class TextInput {
ev.stopPropagation(); ev.stopPropagation();
this.setFocus(); this.setFocus();
this.regMove();
} }
} }
@ -149,10 +158,12 @@ export class TextInput {
let ele = this.elementRef.nativeElement; let ele = this.elementRef.nativeElement;
let scrollData = TextInput.getScollData(ele.offsetTop, ele.offsetHeight, scrollView.getDimensions(), this.keyboardHeight, this.platform.height()); let scrollData = TextInput.getScollData(ele.offsetTop, ele.offsetHeight, scrollView.getDimensions(), this.keyboardHeight, this.platform.height());
if (scrollData.noScroll) { 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
return this.setFocus(); this.setFocus();
this.regMove();
return;
} }
// add padding to the bottom of the scroll view (if needed) // add padding to the bottom of the scroll view (if needed)
@ -178,11 +189,13 @@ export class TextInput {
// all good, allow clicks again // all good, allow clicks again
this.app.setEnabled(true); this.app.setEnabled(true);
this.app.setTransitioning(false); this.app.setTransitioning(false);
this.regMove();
}); });
} else { } else {
// not inside of a scroll view, just focus it // not inside of a scroll view, just focus it
this.setFocus(); this.setFocus();
this.regMove();
} }
} }
@ -224,13 +237,6 @@ export class TextInput {
7) Input top below safe area, no room to scroll, input larger than safe area 7) Input top below safe area, no room to scroll, input larger than safe area
*/ */
if (inputTopWithinSafeArea && inputBottomWithinSafeArea) {
// Input top within safe area, bottom within safe area
// no need to scroll to a position, it's good as-is
return { noScroll: true };
}
// looks like we'll have to do some auto-scrolling
let scrollData = { let scrollData = {
scrollAmount: 0, scrollAmount: 0,
scrollTo: 0, scrollTo: 0,
@ -238,6 +244,13 @@ export class TextInput {
inputSafeY: 0 inputSafeY: 0
}; };
if (inputTopWithinSafeArea && inputBottomWithinSafeArea) {
// Input top within safe area, bottom within safe area
// no need to scroll to a position, it's good as-is
return scrollData;
}
// looks like we'll have to do some auto-scrolling
if (inputTopBelowSafeArea || inputBottomBelowSafeArea) { if (inputTopBelowSafeArea || inputBottomBelowSafeArea) {
// Input top and bottom below safe area // Input top and bottom below safe area
// auto scroll the input up so at least the top of it shows // auto scroll the input up so at least the top of it shows
@ -245,13 +258,13 @@ export class TextInput {
if (safeAreaHeight > inputOffsetHeight) { if (safeAreaHeight > inputOffsetHeight) {
// safe area height is taller than the input height, so we // safe area height is taller than the input height, so we
// can bring it up the input just enough to show the input bottom // can bring it up the input just enough to show the input bottom
scrollData.scrollAmount = (safeAreaBottom - inputBottom); scrollData.scrollAmount = Math.round(safeAreaBottom - inputBottom);
} else { } else {
// safe area height is smaller than the input height, so we can // safe area height is smaller than the input height, so we can
// only scroll it up so the input top is at the top of the safe area // only scroll it up so the input top is at the top of the safe area
// however the input bottom will be below the safe area // however the input bottom will be below the safe area
scrollData.scrollAmount = (safeAreaTop - inputTop); scrollData.scrollAmount = Math.round(safeAreaTop - inputTop);
} }
scrollData.inputSafeY = -(inputTop - safeAreaTop) + 4; scrollData.inputSafeY = -(inputTop - safeAreaTop) + 4;
@ -259,10 +272,9 @@ export class TextInput {
} else if (inputTopAboveSafeArea) { } else if (inputTopAboveSafeArea) {
// Input top above safe area // Input top above safe area
// auto scroll the input down so at least the top of it shows // auto scroll the input down so at least the top of it shows
scrollData.scrollAmount = (safeAreaTop - inputTop); scrollData.scrollAmount = Math.round(safeAreaTop - inputTop);
scrollData.inputSafeY = (safeAreaTop - inputTop) + 4; scrollData.inputSafeY = (safeAreaTop - inputTop) + 4;
} }
// figure out where it should scroll to for the best position to the input // figure out where it should scroll to for the best position to the input
@ -287,7 +299,7 @@ export class TextInput {
// window.safeAreaEle = document.createElement('div'); // window.safeAreaEle = document.createElement('div');
// window.safeAreaEle.style.position = 'absolute'; // window.safeAreaEle.style.position = 'absolute';
// window.safeAreaEle.style.background = 'rgba(0, 128, 0, 0.7)'; // window.safeAreaEle.style.background = 'rgba(0, 128, 0, 0.7)';
// window.safeAreaEle.style.padding = '10px'; // window.safeAreaEle.style.padding = '2px 5px';
// window.safeAreaEle.style.textShadow = '1px 1px white'; // window.safeAreaEle.style.textShadow = '1px 1px white';
// window.safeAreaEle.style.left = '0px'; // window.safeAreaEle.style.left = '0px';
// window.safeAreaEle.style.right = '0px'; // window.safeAreaEle.style.right = '0px';
@ -316,7 +328,8 @@ export class TextInput {
focusChange(hasFocus) { focusChange(hasFocus) {
this.renderer.setElementClass(this.elementRef, 'has-focus', hasFocus); this.renderer.setElementClass(this.elementRef, 'has-focus', hasFocus);
if (!hasFocus) { if (!hasFocus) {
this.deregListeners(); this.deregMove();
this.input.hideFocus(false);
} }
} }
@ -340,17 +353,24 @@ export class TextInput {
// ensure the body hasn't scrolled down // ensure the body hasn't scrolled down
document.body.scrollTop = 0; document.body.scrollTop = 0;
} }
}
/**
* @private
*/
regMove() {
if (this.scrollAssist && this.scrollView) { if (this.scrollAssist && this.scrollView) {
this.deregListeners(); setTimeout(() => {
this.deregScroll = this.scrollView.addScrollEventListener(this.scrollMove); this.deregMove();
this.deregScroll = this.scrollView.addScrollEventListener(this.scrollMove);
}, 80);
} }
} }
/** /**
* @private * @private
*/ */
deregListeners() { deregMove() {
this.deregScroll && this.deregScroll(); this.deregScroll && this.deregScroll();
} }
@ -365,7 +385,7 @@ export class TextInput {
* @private * @private
*/ */
onDestroy() { onDestroy() {
this.deregListeners(); this.deregMove();
this.form.deregister(this); this.form.deregister(this);
} }
@ -428,12 +448,14 @@ export class TextInputElement {
if (shouldRelocate) { if (shouldRelocate) {
let clonedInputEle = focusedInputEle.cloneNode(true); let clonedInputEle = focusedInputEle.cloneNode(true);
clonedInputEle.classList.add('cloned-input'); clonedInputEle.classList.add('cloned-input');
clonedInputEle.classList.remove('hide-focused-input');
clonedInputEle.setAttribute('aria-hidden', true); clonedInputEle.setAttribute('aria-hidden', true);
clonedInputEle.tabIndex = -1; clonedInputEle.tabIndex = -1;
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
focusedInputEle.classList.add('hide-focused-input'); focusedInputEle.classList.add('hide-focused-input');
focusedInputEle.style[dom.CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`; focusedInputEle.style[dom.CSS.transform] = `translate3d(-9999px,${inputRelativeY}px,0)`;
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
this.wrapper.setFocus(); this.wrapper.setFocus();
} else { } else {
@ -449,19 +471,27 @@ export class TextInputElement {
} }
} }
hideFocus() { hideFocus(shouldHideFocus) {
console.debug('hideFocus');
let focusedInputEle = this.getNativeElement(); let focusedInputEle = this.getNativeElement();
let hiddenInputEle = focusedInputEle.parentNode.querySelector('.cloned-hidden'); if (shouldHideFocus) {
if (!hiddenInputEle) { let clonedInputEle = focusedInputEle.cloneNode(true);
hiddenInputEle = focusedInputEle.cloneNode(true); clonedInputEle.classList.add('cloned-hidden');
hiddenInputEle.classList.add('cloned-hidden'); clonedInputEle.setAttribute('aria-hidden', true);
hiddenInputEle.setAttribute('aria-hidden', true); clonedInputEle.tabIndex = -1;
hiddenInputEle.tabIndex = -1;
focusedInputEle.parentNode.appendChild(hiddenInputEle); 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);
}
} }
hiddenInputEle.focus();
} }
get hasFocus() { get hasFocus() {

View File

@ -48,7 +48,7 @@ Platform.register({
], ],
settings: { settings: {
hoverCSS: false, hoverCSS: false,
keyboardHeight: 290, keyboardHeight: 300,
mode: 'md', mode: 'md',
scrollAssist: true, scrollAssist: true,
}, },
@ -71,7 +71,7 @@ Platform.register({
], ],
settings: { settings: {
hoverCSS: false, hoverCSS: false,
keyboardHeight: 290, keyboardHeight: 300,
mode: 'ios', mode: 'ios',
scrollAssist: isIOSDevice, scrollAssist: isIOSDevice,
swipeBackEnabled: isIOSDevice, swipeBackEnabled: isIOSDevice,