Skip to content

Commit

Permalink
fix(input): improve scroll to input and focusing
Browse files Browse the repository at this point in the history
Related #6228
  • Loading branch information
adamdbradley committed Nov 21, 2016
1 parent 1d245ec commit 3b30497
Show file tree
Hide file tree
Showing 10 changed files with 373 additions and 324 deletions.
253 changes: 133 additions & 120 deletions src/components/input/input-base.ts

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions src/components/input/input.ios.scss
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,6 @@ $text-input-ios-highlight-color-invalid: $text-input-highlight-color-invalid !
padding-left: 0;
}

.item-label-floating .text-input-ios.cloned-input,
.item-label-stacked .text-input-ios.cloned-input {
top: 30px;
}


// iOS Clear Input Icon
// --------------------------------------------------
Expand Down
8 changes: 0 additions & 8 deletions src/components/input/input.md.scss
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,6 @@ $text-input-md-highlight-color-invalid: $text-input-highlight-color-invalid
padding-left: 0;
}

.item-label-floating .text-input-md.cloned-input {
top: 32px;
}

.item-label-stacked .text-input-md.cloned-input {
top: 27px;
}


// Material Design Clear Input Icon
// --------------------------------------------------
Expand Down
17 changes: 1 addition & 16 deletions src/components/input/input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ input.text-input:-webkit-autofill {

[next-input] {
position: absolute;
bottom: 1px;
bottom: 20px;

padding: 0;

Expand Down Expand Up @@ -153,18 +153,3 @@ input.text-input:-webkit-autofill {
.input-has-focus.input-has-value .text-input-clear-icon {
display: block;
}


// Cloned Input
// --------------------------------------------------

.text-input.cloned-input {
position: relative;
top: 0;

pointer-events: none;
}

.item-input:not(.item-label-floating) .text-input.cloned-active {
display: none;
}
8 changes: 0 additions & 8 deletions src/components/input/input.wp.scss
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,6 @@ $text-input-wp-highlight-color-invalid: $text-input-highlight-color-invalid
width: calc(100% - #{$text-input-wp-margin-right});
}

.item-label-floating .text-input-wp.cloned-input {
top: 32px;
}

.item-label-stacked .text-input-wp.cloned-input {
top: 27px;
}

.item-wp.item-label-stacked [item-right],
.item-wp.item-label-floating [item-right] {
align-self: flex-end;
Expand Down
91 changes: 55 additions & 36 deletions src/components/input/native-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ export class NativeInput {
// automatically blur input if:
// 1) this input has focus
// 2) the newly tapped document element is not an input
console.debug('input blurring enabled');
console.debug(`native-input, blurring enabled`);

document.addEventListener('touchend', docTouchEnd, true);
self._unrefBlur = function() {
console.debug('input blurring disabled');
console.debug(`native-input, blurring disabled`);
document.removeEventListener('touchend', docTouchEnd, true);
};
}
Expand Down Expand Up @@ -99,7 +99,7 @@ export class NativeInput {

beginFocus(shouldFocus: boolean, inputRelativeY: number) {
if (this._relocated !== shouldFocus) {
var focusedInputEle = this.element();
const focusedInputEle = this.element();
if (shouldFocus) {
// we should focus into this element

Expand All @@ -113,8 +113,7 @@ export class NativeInput {
// the cloned input fills the area of where native input should be
// while the native input fakes out the browser by relocating itself
// before it receives the actual focus event
var clonedInputEle = cloneInput(focusedInputEle, 'cloned-focus');
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
cloneInputComponent(focusedInputEle);

// move the native input to a location safe to receive focus
// according to the browser, the native input receives focus in an
Expand All @@ -128,18 +127,11 @@ export class NativeInput {
// to scroll the input into view itself (screwing up headers/footers)
this.setFocus();

if (this._clone) {
focusedInputEle.classList.add('cloned-active');
}

} else {
// should remove the focus
if (this._clone) {
// should remove the cloned node
focusedInputEle.classList.remove('cloned-active');
(<any>focusedInputEle.style)[CSS.transform] = '';
focusedInputEle.style.opacity = '';
removeClone(focusedInputEle, 'cloned-focus');
removeClone(focusedInputEle);
}
}

Expand All @@ -150,17 +142,14 @@ export class NativeInput {
hideFocus(shouldHideFocus: boolean) {
let focusedInputEle = this.element();

console.debug(`native input hideFocus, shouldHideFocus: ${shouldHideFocus}, input value: ${focusedInputEle.value}`);
console.debug(`native-input, hideFocus, shouldHideFocus: ${shouldHideFocus}, input value: ${focusedInputEle.value}`);

if (shouldHideFocus) {
let clonedInputEle = cloneInput(focusedInputEle, 'cloned-move');

focusedInputEle.classList.add('cloned-active');
focusedInputEle.parentNode.insertBefore(clonedInputEle, focusedInputEle);
cloneInputComponent(focusedInputEle);
focusedInputEle.style.transform = 'scale(0)';

} else {
focusedInputEle.classList.remove('cloned-active');
removeClone(focusedInputEle, 'cloned-move');
removeClone(focusedInputEle);
}
}

Expand All @@ -186,28 +175,58 @@ export class NativeInput {

}

function cloneInput(focusedInputEle: any, addCssClass: string) {
let clonedInputEle = focusedInputEle.cloneNode(true);
clonedInputEle.classList.add('cloned-input');
clonedInputEle.classList.add(addCssClass);
clonedInputEle.setAttribute('aria-hidden', true);
clonedInputEle.removeAttribute('aria-labelledby');
clonedInputEle.tabIndex = -1;
clonedInputEle.style.width = (focusedInputEle.offsetWidth + 10) + 'px';
clonedInputEle.style.height = focusedInputEle.offsetHeight + 'px';
clonedInputEle.value = focusedInputEle.value;
return clonedInputEle;
function cloneInputComponent(srcNativeInputEle: HTMLInputElement) {
// given a native <input> or <textarea> element
// find its parent wrapping component like <ion-input> or <ion-textarea>
// then clone the entire component
const srcComponentEle = <HTMLElement>srcNativeInputEle.closest('ion-input,ion-textarea');
if (srcComponentEle) {
// DOM READ
const srcTop = srcComponentEle.offsetTop;
const srcLeft = srcComponentEle.offsetLeft;
const srcWidth = srcComponentEle.offsetWidth;
const srcHeight = srcComponentEle.offsetHeight;

// DOM WRITE
// not using deep clone so we don't pull in unnecessary nodes
const clonedComponentEle = <HTMLElement>srcComponentEle.cloneNode(false);
clonedComponentEle.classList.add('cloned-input');
clonedComponentEle.setAttribute('aria-hidden', 'true');
clonedComponentEle.style.pointerEvents = 'none';
clonedComponentEle.style.position = 'absolute';
clonedComponentEle.style.top = srcTop + 'px';
clonedComponentEle.style.left = srcLeft + 'px';
clonedComponentEle.style.width = srcWidth + 'px';
clonedComponentEle.style.height = srcHeight + 'px';

const clonedNativeInputEle = <HTMLInputElement>srcNativeInputEle.cloneNode(false);
clonedNativeInputEle.value = srcNativeInputEle.value;
clonedNativeInputEle.tabIndex = -1;

clonedComponentEle.appendChild(clonedNativeInputEle);
srcComponentEle.parentNode.appendChild(clonedComponentEle);

srcComponentEle.style.pointerEvents = 'none';
}

srcNativeInputEle.style.transform = 'scale(0)';
}

function removeClone(focusedInputEle: any, queryCssClass: string) {
let clonedInputEle = focusedInputEle.parentElement.querySelector('.' + queryCssClass);
if (clonedInputEle) {
clonedInputEle.parentNode.removeChild(clonedInputEle);
function removeClone(srcNativeInputEle: HTMLElement) {
const srcComponentEle = <HTMLElement>srcNativeInputEle.closest('ion-input,ion-textarea');
if (srcComponentEle && srcComponentEle.parentElement) {
const clonedInputEles = srcComponentEle.parentElement.querySelectorAll('.cloned-input');
for (var i = 0; i < clonedInputEles.length; i++) {
clonedInputEles[i].parentNode.removeChild(clonedInputEles[i]);
}

srcComponentEle.style.pointerEvents = '';
}
srcNativeInputEle.style.transform = '';
srcNativeInputEle.style.opacity = '';
}



/**
* @private
*/
Expand Down
Loading

0 comments on commit 3b30497

Please sign in to comment.