mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
text input keyboard focus
This commit is contained in:
@ -20,7 +20,7 @@ export class ParallaxEffect {
|
|||||||
elementRef: ElementRef
|
elementRef: ElementRef
|
||||||
) {
|
) {
|
||||||
this.ele = elementRef.nativeElement;
|
this.ele = elementRef.nativeElement;
|
||||||
this.scroller = this.ele.querySelector('.scroll-content');
|
this.scroller = this.ele.querySelector('scroll-content');
|
||||||
this.scroller.addEventListener('scroll', (e) => {
|
this.scroller.addEventListener('scroll', (e) => {
|
||||||
//this.counter.innerHTML = e.target.scrollTop;
|
//this.counter.innerHTML = e.target.scrollTop;
|
||||||
dom.raf(() => {
|
dom.raf(() => {
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
ion-content {
|
ion-content {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
.scroll-content {
|
scroll-content {
|
||||||
padding-top: 300px;
|
padding-top: 300px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -10,17 +10,17 @@ ion-content {
|
|||||||
display: block;
|
display: block;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background-color: $content-background-color;
|
background-color: $content-background-color;
|
||||||
|
}
|
||||||
.scroll-content {
|
|
||||||
position: absolute;
|
scroll-content {
|
||||||
top: 0;
|
position: absolute;
|
||||||
right: 0;
|
top: 0;
|
||||||
bottom: 0;
|
right: 0;
|
||||||
left: 0;
|
bottom: 0;
|
||||||
overflow-y: auto;
|
left: 0;
|
||||||
overflow-x: hidden;
|
display: block;
|
||||||
-webkit-overflow-scrolling: touch;
|
overflow-y: scroll; // has to be scroll for momentum scrolling, not auto
|
||||||
will-change: scroll-position;
|
overflow-x: hidden;
|
||||||
}
|
-webkit-overflow-scrolling: touch;
|
||||||
|
will-change: scroll-position;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ 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';
|
import {ScrollTo} from '../../animations/scroll-to';
|
||||||
import {Platform} from '../../platform/platform';
|
import {hasFocusedTextInput} from '../../util/dom';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -14,11 +14,12 @@ import {Platform} from '../../platform/platform';
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
template: '<div class="scroll-content"><ng-content></ng-content></div>'
|
template: '<scroll-content><ng-content></ng-content></scroll-content>'
|
||||||
})
|
})
|
||||||
export class Content extends Ion {
|
export class Content extends Ion {
|
||||||
constructor(elementRef: ElementRef, config: IonicConfig) {
|
constructor(elementRef: ElementRef, config: IonicConfig) {
|
||||||
super(elementRef, config);
|
super(elementRef, config);
|
||||||
|
this.scrollPadding = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
onIonInit() {
|
onIonInit() {
|
||||||
@ -66,13 +67,13 @@ export class Content extends Ion {
|
|||||||
let parentElement = scrollElement.parentElement;
|
let parentElement = scrollElement.parentElement;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
height: parentElement.offsetHeight,
|
contentHeight: parentElement.offsetHeight,
|
||||||
top: parentElement.offsetTop,
|
contentTop: parentElement.offsetTop,
|
||||||
bottom: parentElement.offsetTop + parentElement.offsetHeight,
|
contentBottom: parentElement.offsetTop + parentElement.offsetHeight,
|
||||||
|
|
||||||
width: parentElement.offsetWidth,
|
contentWidth: parentElement.offsetWidth,
|
||||||
left: parentElement.offsetLeft,
|
contentLeft: parentElement.offsetLeft,
|
||||||
right: parentElement.offsetLeft + parentElement.offsetWidth,
|
contentRight: parentElement.offsetLeft + parentElement.offsetWidth,
|
||||||
|
|
||||||
scrollHeight: scrollElement.scrollHeight,
|
scrollHeight: scrollElement.scrollHeight,
|
||||||
scrollTop: scrollElement.scrollTop,
|
scrollTop: scrollElement.scrollTop,
|
||||||
@ -81,8 +82,31 @@ export class Content extends Ion {
|
|||||||
scrollWidth: scrollElement.scrollWidth,
|
scrollWidth: scrollElement.scrollWidth,
|
||||||
scrollLeft: scrollElement.scrollLeft,
|
scrollLeft: scrollElement.scrollLeft,
|
||||||
scrollRight: scrollElement.scrollLeft + scrollElement.scrollWidth,
|
scrollRight: scrollElement.scrollLeft + scrollElement.scrollWidth,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
keyboardTop: (Platform.height() * 0.6)
|
addKeyboardPadding(addPadding) {
|
||||||
|
if (addPadding > this.scrollPadding) {
|
||||||
|
this.scrollPadding = addPadding;
|
||||||
|
this.scrollElement.style.paddingBottom = addPadding + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pollFocus() {
|
||||||
|
if (hasFocusedTextInput()) {
|
||||||
|
this.isPollingFocus = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.pollFocus();
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.isPollingFocus = false;
|
||||||
|
|
||||||
|
if (this.scrollPadding) {
|
||||||
|
this.scrollPadding = 0;
|
||||||
|
this.scrollElement.style.paddingBottom = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ ion-refresher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.scroll-content.overscroll {
|
scroll-content.overscroll {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
ion-scroll {
|
ion-scroll {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
&.scroll-x .scroll-content {
|
&.scroll-x scroll-content {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
&.scroll-y .scroll-content {
|
&.scroll-y scroll-content {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scroll-content {
|
scroll-content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -20,7 +20,7 @@ import {IonicComponent} from '../../config/annotations';
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
template: '<div class="scroll-content"><ng-content></ng-content></div>'
|
template: '<scroll-content><ng-content></ng-content></scroll-content>'
|
||||||
})
|
})
|
||||||
export class Scroll extends Ion {
|
export class Scroll extends Ion {
|
||||||
constructor(elementRef: ElementRef, ionicConfig: IonicConfig) {
|
constructor(elementRef: ElementRef, ionicConfig: IonicConfig) {
|
||||||
|
@ -2,8 +2,129 @@ import {TextInput} from 'ionic/ionic';
|
|||||||
|
|
||||||
export function run() {
|
export function run() {
|
||||||
|
|
||||||
it('should not scroll', () => {
|
it('should scroll, top and bottom below safe area, no room to scroll', () => {
|
||||||
let input = new TextInput();
|
|
||||||
|
let inputOffsetTop = 350;
|
||||||
|
let inputOffsetHeight = 35;
|
||||||
|
let scrollViewDimensions = {
|
||||||
|
contentTop: 100,
|
||||||
|
contentHeight: 700,
|
||||||
|
scrollTop: 30,
|
||||||
|
scrollHeight: 700
|
||||||
|
};
|
||||||
|
let keyboardHeight = 400;
|
||||||
|
|
||||||
|
let scrollData = TextInput.getScollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight);
|
||||||
|
|
||||||
|
expect(scrollData.scrollAmount).toBe(-178.5);
|
||||||
|
expect(scrollData.scrollTo).toBe(208.5);
|
||||||
|
expect(scrollData.scrollPadding).toBe(523.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scroll, top and bottom below safe area, room to scroll', () => {
|
||||||
|
|
||||||
|
let inputOffsetTop = 350;
|
||||||
|
let inputOffsetHeight = 35;
|
||||||
|
let scrollViewDimensions = {
|
||||||
|
contentTop: 100,
|
||||||
|
contentHeight: 700,
|
||||||
|
scrollTop: 30,
|
||||||
|
scrollHeight: 1000
|
||||||
|
};
|
||||||
|
let keyboardHeight = 400;
|
||||||
|
|
||||||
|
let scrollData = TextInput.getScollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight);
|
||||||
|
|
||||||
|
expect(scrollData.scrollAmount).toBe(-178.5);
|
||||||
|
expect(scrollData.scrollTo).toBe(208.5);
|
||||||
|
expect(scrollData.scrollPadding).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scroll, top above safe', () => {
|
||||||
|
// Input top within safe area, bottom below safe area, room to scroll
|
||||||
|
let inputOffsetTop = 100;
|
||||||
|
let inputOffsetHeight = 33;
|
||||||
|
let scrollViewDimensions = {
|
||||||
|
contentTop: 100,
|
||||||
|
contentHeight: 700,
|
||||||
|
scrollTop: 250,
|
||||||
|
scrollHeight: 700
|
||||||
|
};
|
||||||
|
let keyboardHeight = 400;
|
||||||
|
|
||||||
|
let scrollData = TextInput.getScollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight);
|
||||||
|
|
||||||
|
expect(scrollData.scrollAmount).toBe(150);
|
||||||
|
expect(scrollData.scrollTo).toBe(100);
|
||||||
|
expect(scrollData.scrollPadding).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scroll, top in safe, bottom below safe, below more than top in, not enough padding', () => {
|
||||||
|
// Input top within safe area, bottom below safe area, room to scroll
|
||||||
|
let inputOffsetTop = 100;
|
||||||
|
let inputOffsetHeight = 320;
|
||||||
|
let scrollViewDimensions = {
|
||||||
|
contentTop: 100,
|
||||||
|
contentHeight: 700,
|
||||||
|
scrollTop: 20,
|
||||||
|
scrollHeight: 700
|
||||||
|
};
|
||||||
|
let keyboardHeight = 400;
|
||||||
|
|
||||||
|
let scrollData = TextInput.getScollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight);
|
||||||
|
|
||||||
|
expect(scrollData.scrollAmount).toBe(-80);
|
||||||
|
expect(scrollData.scrollTo).toBe(100);
|
||||||
|
expect(scrollData.scrollPadding).toBe(523.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scroll, top in safe, bottom below safe, below more than top in, enough padding', () => {
|
||||||
|
// Input top within safe area, bottom below safe area, room to scroll
|
||||||
|
let inputOffsetTop = 20;
|
||||||
|
let inputOffsetHeight = 330;
|
||||||
|
let scrollViewDimensions = {
|
||||||
|
contentTop: 100,
|
||||||
|
scrollTop: 0,
|
||||||
|
};
|
||||||
|
let keyboardHeight = 400;
|
||||||
|
|
||||||
|
let scrollData = TextInput.getScollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight);
|
||||||
|
|
||||||
|
expect(scrollData.scrollAmount).toBe(-20);
|
||||||
|
expect(scrollData.scrollTo).toBe(20);
|
||||||
|
expect(scrollData.scrollPadding).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scroll, top in safe, bottom below safe, below less than top in, enough padding', () => {
|
||||||
|
// Input top within safe area, bottom below safe area, room to scroll
|
||||||
|
let inputOffsetTop = 250;
|
||||||
|
let inputOffsetHeight = 80; // goes 30px below safe area
|
||||||
|
let scrollViewDimensions = {
|
||||||
|
contentTop: 100,
|
||||||
|
scrollTop: 0,
|
||||||
|
};
|
||||||
|
let keyboardHeight = 400;
|
||||||
|
|
||||||
|
let scrollData = TextInput.getScollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight);
|
||||||
|
|
||||||
|
expect(scrollData.scrollAmount).toBe(-153.5);
|
||||||
|
expect(scrollData.scrollTo).toBe(153.5);
|
||||||
|
expect(scrollData.scrollPadding).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not scroll, top in safe, bottom in safe', () => {
|
||||||
|
// Input top within safe area, bottom within safe area
|
||||||
|
let inputOffsetTop = 100;
|
||||||
|
let inputOffsetHeight = 50;
|
||||||
|
let scrollViewDimensions = {
|
||||||
|
contentTop: 100,
|
||||||
|
scrollTop: 0,
|
||||||
|
keyboardTop: 400
|
||||||
|
};
|
||||||
|
let keyboardHeight = 400;
|
||||||
|
|
||||||
|
let scrollData = TextInput.getScollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight);
|
||||||
|
expect(scrollData.noScroll).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import {IonicApp} from '../app/app';
|
|||||||
import {Content} from '../content/content';
|
import {Content} from '../content/content';
|
||||||
import {ClickBlock} from '../../util/click-block';
|
import {ClickBlock} from '../../util/click-block';
|
||||||
import * as dom from '../../util/dom';
|
import * as dom from '../../util/dom';
|
||||||
|
import {Platform} from '../../platform/platform';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -161,16 +162,21 @@ export class TextInput extends Ion {
|
|||||||
// 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 = this.getScollData(ele.offsetTop, ele.offsetHeight, scrollView.getDimensions());
|
let keyboardHeight = this.config.setting('keyboardHeight');
|
||||||
|
|
||||||
|
let scrollData = TextInput.getScollData(ele.offsetTop, ele.offsetHeight, scrollView.getDimensions(), keyboardHeight);
|
||||||
if (scrollData.noScroll) {
|
if (scrollData.noScroll) {
|
||||||
// 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();
|
return this.setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add padding to the bottom of the scroll view (if needed)
|
||||||
|
scrollView.addKeyboardPadding(scrollData.scrollPadding);
|
||||||
|
|
||||||
// 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
|
||||||
ClickBlock(true, SCROLL_INTO_VIEW_DURATION + 200);
|
ClickBlock(true, SCROLL_INTO_VIEW_DURATION + 100);
|
||||||
|
|
||||||
// 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
|
||||||
@ -178,7 +184,7 @@ export class TextInput extends Ion {
|
|||||||
this.tempFocusMove();
|
this.tempFocusMove();
|
||||||
|
|
||||||
// scroll the input into place
|
// scroll the input into place
|
||||||
scrollView.scrollTo(0, scrollData.scrollTo, SCROLL_INTO_VIEW_DURATION, 8).then(() => {
|
scrollView.scrollTo(0, scrollData.scrollTo, SCROLL_INTO_VIEW_DURATION, 6).then(() => {
|
||||||
// the scroll view is in the correct position now
|
// the scroll view is in the correct position now
|
||||||
// give the native text input focus
|
// give the native text input focus
|
||||||
this.setFocus();
|
this.setFocus();
|
||||||
@ -194,21 +200,22 @@ export class TextInput extends Ion {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getScollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions) {
|
static getScollData(inputOffsetTop, inputOffsetHeight, scrollViewDimensions, keyboardHeight) {
|
||||||
// compute input's Y values relative to the body
|
// compute input's Y values relative to the body
|
||||||
let inputTop = (inputOffsetTop + scrollViewDimensions.top - scrollViewDimensions.scrollTop);
|
let inputTop = (inputOffsetTop + scrollViewDimensions.contentTop - scrollViewDimensions.scrollTop);
|
||||||
let inputBottom = (inputTop + inputOffsetHeight);
|
let inputBottom = (inputTop + inputOffsetHeight);
|
||||||
|
|
||||||
// compute the safe area which is the viewable content area when the soft keyboard is up
|
// compute the safe area which is the viewable content area when the soft keyboard is up
|
||||||
let safeAreaTop = (scrollViewDimensions.top - 1);
|
let safeAreaTop = scrollViewDimensions.contentTop;
|
||||||
let safeAreaBottom = scrollViewDimensions.keyboardTop;
|
let safeAreaHeight = Platform.height() - keyboardHeight - safeAreaTop;
|
||||||
let safeAreaHeight = (safeAreaBottom - safeAreaTop);
|
safeAreaHeight /= 2;
|
||||||
|
let safeAreaBottom = safeAreaTop + safeAreaHeight;
|
||||||
|
|
||||||
let inputTopWithinSafeArea = (inputTop >= safeAreaTop && inputTop <= safeAreaBottom);
|
let inputTopWithinSafeArea = (inputTop >= safeAreaTop && inputTop <= safeAreaBottom);
|
||||||
|
let inputTopAboveSafeArea = (inputTop < safeAreaTop);
|
||||||
|
let inputTopBelowSafeArea = (inputTop > safeAreaBottom);
|
||||||
let inputBottomWithinSafeArea = (inputBottom >= safeAreaTop && inputBottom <= safeAreaBottom);
|
let inputBottomWithinSafeArea = (inputBottom >= safeAreaTop && inputBottom <= safeAreaBottom);
|
||||||
let inputBottomBelowSafeArea = (inputBottom > safeAreaBottom);
|
let inputBottomBelowSafeArea = (inputBottom > safeAreaBottom);
|
||||||
let inputFitsWithinSafeArea = (inputOffsetHeight <= safeAreaHeight);
|
|
||||||
let distanceInputBottomBelowSafeArea = (safeAreaBottom - inputBottom);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Text Input Scroll To Scenarios
|
Text Input Scroll To Scenarios
|
||||||
@ -231,34 +238,74 @@ export class TextInput extends Ion {
|
|||||||
// looks like we'll have to do some auto-scrolling
|
// looks like we'll have to do some auto-scrolling
|
||||||
let scrollData = {
|
let scrollData = {
|
||||||
scrollAmount: 0,
|
scrollAmount: 0,
|
||||||
scrollTo: inputOffsetTop,
|
scrollTo: 0,
|
||||||
scrollPadding: 0,
|
scrollPadding: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (inputTopWithinSafeArea && inputBottomBelowSafeArea) {
|
if (inputTopBelowSafeArea || inputBottomBelowSafeArea) {
|
||||||
// Input top within safe area, bottom below safe area
|
// Input top and bottom below safe area
|
||||||
let distanceInputTopIntoSafeArea = (safeAreaTop - inputTop);
|
// auto scroll the input up so at least the top of it shows
|
||||||
let distanceInputBottomBelowSafeArea = (inputBottom - safeAreaBottom);
|
|
||||||
|
|
||||||
if (distanceInputBottomBelowSafeArea < distanceInputTopIntoSafeArea) {
|
if (safeAreaHeight > inputOffsetHeight) {
|
||||||
// the input's top is farther into the safe area then the bottom is out of it
|
// safe area height is taller than the input height, so we
|
||||||
// this means we can scroll it up a little bit and the top will still be
|
// can bring it up the input just enough to show the input bottom
|
||||||
// within the safe area
|
scrollData.scrollAmount = (safeAreaBottom - inputBottom);
|
||||||
scrollData.scrollAmount = distanceInputTopIntoSafeArea * -1;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// the input's top is less below the safe area top than the
|
// safe area height is smaller than the input height, so we can
|
||||||
// input's bottom is below the safe area bottom. So scroll the input
|
// only scroll it up so the input top is at the top of the safe area
|
||||||
// to be at the top of the safe area, knowing that the bottom will go below
|
// however the input bottom will be below the safe area
|
||||||
scrollData.scrollAmount = distanceInputTopIntoSafeArea * -1;
|
scrollData.scrollAmount = (safeAreaTop - inputTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else if (inputTopAboveSafeArea) {
|
||||||
|
// Input top above safe area
|
||||||
|
// auto scroll the input down so at least the top of it shows
|
||||||
|
scrollData.scrollAmount = (safeAreaTop - inputTop);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollData.scrollTo = (inputOffsetTop + scrollData.scrollAmount);
|
// figure out where it should scroll to for the best position to the input
|
||||||
|
scrollData.scrollTo = (scrollViewDimensions.scrollTop - scrollData.scrollAmount);
|
||||||
|
|
||||||
|
if (scrollData.scrollAmount < 0) {
|
||||||
|
// when auto-scrolling up, there also needs to be enough
|
||||||
|
// content padding at the bottom of the scroll view
|
||||||
|
// manually add it if there isn't enough scrollable area
|
||||||
|
|
||||||
|
// figure out how many scrollable area is left to scroll up
|
||||||
|
let availablePadding = (scrollViewDimensions.scrollHeight - scrollViewDimensions.scrollTop) - scrollViewDimensions.contentHeight;
|
||||||
|
|
||||||
|
let paddingSpace = availablePadding + scrollData.scrollAmount;
|
||||||
|
if (paddingSpace < 0) {
|
||||||
|
// there's not enough scrollable area at the bottom, so manually add more
|
||||||
|
scrollData.scrollPadding = (scrollViewDimensions.contentHeight - safeAreaHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!window.safeAreaEle) {
|
||||||
|
// window.safeAreaEle = document.createElement('div');
|
||||||
|
// window.safeAreaEle.style.position = 'absolute';
|
||||||
|
// window.safeAreaEle.style.background = 'rgba(0, 128, 0, 0.3)';
|
||||||
|
// window.safeAreaEle.style.padding = '10px';
|
||||||
|
// window.safeAreaEle.style.textShadow = '2px 2px white';
|
||||||
|
// window.safeAreaEle.style.left = '0px';
|
||||||
|
// window.safeAreaEle.style.right = '0px';
|
||||||
|
// window.safeAreaEle.style.pointerEvents = 'none';
|
||||||
|
// document.body.appendChild(window.safeAreaEle);
|
||||||
|
// }
|
||||||
|
// window.safeAreaEle.style.top = safeAreaTop + 'px';
|
||||||
|
// window.safeAreaEle.style.height = safeAreaHeight + 'px';
|
||||||
|
// window.safeAreaEle.innerHTML = `
|
||||||
|
// <div>scrollTo: ${scrollData.scrollTo}</div>
|
||||||
|
// <div>scrollAmount: ${scrollData.scrollAmount}</div>
|
||||||
|
// <div>scrollPadding: ${scrollData.scrollPadding}</div>
|
||||||
|
// <div>scrollHeight: ${scrollViewDimensions.scrollHeight}</div>
|
||||||
|
// <div>scrollTop: ${scrollViewDimensions.scrollTop}</div>
|
||||||
|
// <div>contentHeight: ${scrollViewDimensions.contentHeight}</div>
|
||||||
|
// `;
|
||||||
|
|
||||||
// fallback for whatever reason
|
|
||||||
return scrollData;
|
return scrollData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,12 +315,21 @@ export class TextInput extends Ion {
|
|||||||
|
|
||||||
setFocus() {
|
setFocus() {
|
||||||
this.zone.run(() => {
|
this.zone.run(() => {
|
||||||
|
// set focus on the input element
|
||||||
this.input && this.input.setFocus();
|
this.input && this.input.setFocus();
|
||||||
|
|
||||||
|
// ensure the body hasn't scrolled down
|
||||||
|
document.body.scrollTop = 0;
|
||||||
|
|
||||||
IonInput.setAsLastInput(this);
|
IonInput.setAsLastInput(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.scrollAssist && this.scrollView) {
|
if (this.scrollAssist && this.scrollView) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
if (!this.scrollView.isPollingFocus) {
|
||||||
|
this.scrollView.pollFocus();
|
||||||
|
}
|
||||||
|
|
||||||
this.deregListeners();
|
this.deregListeners();
|
||||||
this.deregScroll = this.scrollView.addScrollEventListener(this.scrollMove);
|
this.deregScroll = this.scrollView.addScrollEventListener(this.scrollMove);
|
||||||
}, 100);
|
}, 100);
|
||||||
@ -306,4 +362,4 @@ export class TextInput extends Ion {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SCROLL_INTO_VIEW_DURATION = 500;
|
const SCROLL_INTO_VIEW_DURATION = 400;
|
||||||
|
@ -16,9 +16,6 @@ IonicConfig.modeConfig('ios', {
|
|||||||
iconForward: 'ion-ios-arrow-forward',
|
iconForward: 'ion-ios-arrow-forward',
|
||||||
iconMode: 'ios',
|
iconMode: 'ios',
|
||||||
|
|
||||||
keyboardScrollAssist: true,
|
|
||||||
tapPolyfill: false,
|
|
||||||
|
|
||||||
navTitleAlign: 'center',
|
navTitleAlign: 'center',
|
||||||
tabBarPlacement: 'bottom',
|
tabBarPlacement: 'bottom',
|
||||||
viewTransition: 'ios',
|
viewTransition: 'ios',
|
||||||
@ -40,9 +37,6 @@ IonicConfig.modeConfig('md', {
|
|||||||
iconForward: '',
|
iconForward: '',
|
||||||
iconMode: 'md',
|
iconMode: 'md',
|
||||||
|
|
||||||
keyboardScrollAssist: true,
|
|
||||||
tapPolyfill: false,
|
|
||||||
|
|
||||||
navTitleAlign: 'left',
|
navTitleAlign: 'left',
|
||||||
tabBarPlacement: 'top',
|
tabBarPlacement: 'top',
|
||||||
viewTransition: 'md',
|
viewTransition: 'md',
|
||||||
|
@ -5,6 +5,7 @@ Platform.register({
|
|||||||
name: 'core',
|
name: 'core',
|
||||||
settings: {
|
settings: {
|
||||||
mode: 'ios',
|
mode: 'ios',
|
||||||
|
keyboardHeight: 290,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Platform.setDefault('core');
|
Platform.setDefault('core');
|
||||||
@ -20,7 +21,6 @@ Platform.register({
|
|||||||
isMatch(p) {
|
isMatch(p) {
|
||||||
let smallest = Math.min(p.width(), p.height());
|
let smallest = Math.min(p.width(), p.height());
|
||||||
let largest = Math.max(p.width(), p.height());
|
let largest = Math.max(p.width(), p.height());
|
||||||
// http://www.mydevice.io/devices/
|
|
||||||
return (smallest > 390 && smallest < 520) &&
|
return (smallest > 390 && smallest < 520) &&
|
||||||
(largest > 620 && largest < 800);
|
(largest > 620 && largest < 800);
|
||||||
}
|
}
|
||||||
@ -32,7 +32,6 @@ Platform.register({
|
|||||||
isMatch(p) {
|
isMatch(p) {
|
||||||
let smallest = Math.min(p.width(), p.height());
|
let smallest = Math.min(p.width(), p.height());
|
||||||
let largest = Math.max(p.width(), p.height());
|
let largest = Math.max(p.width(), p.height());
|
||||||
// http://www.mydevice.io/devices/
|
|
||||||
return (smallest > 460 && smallest < 820) &&
|
return (smallest > 460 && smallest < 820) &&
|
||||||
(largest > 780 && largest < 1400);
|
(largest > 780 && largest < 1400);
|
||||||
}
|
}
|
||||||
@ -48,10 +47,11 @@ Platform.register({
|
|||||||
],
|
],
|
||||||
settings: {
|
settings: {
|
||||||
mode: 'md',
|
mode: 'md',
|
||||||
|
keyboardHeight: 290,
|
||||||
|
keyboardScrollAssist: true,
|
||||||
},
|
},
|
||||||
isMatch(p) {
|
isMatch(p) {
|
||||||
// "silk" is kindle fire
|
return p.isPlatform('android', 'android|silk');
|
||||||
return p.isPlatform('android', 'android| silk');
|
|
||||||
},
|
},
|
||||||
versionParser(p) {
|
versionParser(p) {
|
||||||
return p.matchUserAgentVersion(/Android (\d+).(\d+)?/);
|
return p.matchUserAgentVersion(/Android (\d+).(\d+)?/);
|
||||||
@ -70,10 +70,12 @@ Platform.register({
|
|||||||
settings: {
|
settings: {
|
||||||
mode: 'ios',
|
mode: 'ios',
|
||||||
tapPolyfill: function() {
|
tapPolyfill: function() {
|
||||||
// this ensures it's actually a physical iOS device
|
|
||||||
// and not just an a spoofed user-agent string
|
|
||||||
return /iphone|ipad|ipod/i.test(Platform.navigatorPlatform());
|
return /iphone|ipad|ipod/i.test(Platform.navigatorPlatform());
|
||||||
},
|
},
|
||||||
|
keyboardScrollAssist: function() {
|
||||||
|
return /iphone|ipad|ipod/i.test(Platform.navigatorPlatform());
|
||||||
|
},
|
||||||
|
keyboardHeight: 290,
|
||||||
},
|
},
|
||||||
isMatch(p) {
|
isMatch(p) {
|
||||||
return p.isPlatform('ios', 'iphone|ipad|ipod');
|
return p.isPlatform('ios', 'iphone|ipad|ipod');
|
||||||
@ -87,6 +89,9 @@ Platform.register({
|
|||||||
Platform.register({
|
Platform.register({
|
||||||
name: 'ipad',
|
name: 'ipad',
|
||||||
superset: 'tablet',
|
superset: 'tablet',
|
||||||
|
settings: {
|
||||||
|
keyboardHeight: 500,
|
||||||
|
},
|
||||||
isMatch(p) {
|
isMatch(p) {
|
||||||
return p.isPlatform('ipad');
|
return p.isPlatform('ipad');
|
||||||
}
|
}
|
||||||
|
@ -179,3 +179,18 @@ export function hasPointerMoved(threshold, startCoord, endCoord) {
|
|||||||
export function hasFocus(ele) {
|
export function hasFocus(ele) {
|
||||||
return !!(ele && (document.activeElement === ele.nativeElement || document.activeElement === ele));
|
return !!(ele && (document.activeElement === ele.nativeElement || document.activeElement === ele));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isTextInput(ele) {
|
||||||
|
return !!ele &&
|
||||||
|
(ele.tagName == 'TEXTAREA' ||
|
||||||
|
ele.contentEditable === 'true' ||
|
||||||
|
(ele.tagName == 'INPUT' && !(/^(radio|checkbox|range|file|submit|reset|color|image|button)$/i).test(ele.type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasFocusedTextInput() {
|
||||||
|
let ele = document.activeElement;
|
||||||
|
if (isTextInput(ele)) {
|
||||||
|
return (ele.parentElement.querySelector(':focus') === ele);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -31,9 +31,10 @@
|
|||||||
$content-padding: 10px !default;
|
$content-padding: 10px !default;
|
||||||
|
|
||||||
|
|
||||||
[padding], .padding,
|
.padding,
|
||||||
.padding > .scroll-content,
|
[padding],
|
||||||
[padding] > .scroll-content {
|
.padding > scroll-content,
|
||||||
|
[padding] > scroll-content {
|
||||||
padding: $content-padding;
|
padding: $content-padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user