From d817cc3b30ae648730b063120b8d55b9ec09a53d Mon Sep 17 00:00:00 2001 From: Manu MA Date: Sat, 3 Nov 2018 01:35:38 +0100 Subject: [PATCH] fix(input): scroll assist works in with shadow-dom (#16206) fixes #15888 fixes #15294 fixes #15895 --- core/src/components.d.ts | 4 +- core/src/components/input/input.scss | 14 +-- core/src/components/input/input.tsx | 2 +- core/src/components/input/readme.md | 2 +- .../components/input/test/basic/index.html | 2 +- core/src/utils/input-shims/hacks/common.ts | 103 ++++++------------ .../src/utils/input-shims/hacks/hide-caret.ts | 1 - .../utils/input-shims/hacks/input-blurring.ts | 5 - .../utils/input-shims/hacks/scroll-data.ts | 10 +- 9 files changed, 45 insertions(+), 98 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 84c254da119..268a175f08d 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -1700,7 +1700,7 @@ export namespace Components { */ 'autocomplete': 'on' | 'off'; /** - * Whether autocorrection should be enabled when the user is entering/editing the text value. + * Whether auto correction should be enabled when the user is entering/editing the text value. */ 'autocorrect': 'on' | 'off'; /** @@ -1818,7 +1818,7 @@ export namespace Components { */ 'autocomplete'?: 'on' | 'off'; /** - * Whether autocorrection should be enabled when the user is entering/editing the text value. + * Whether auto correction should be enabled when the user is entering/editing the text value. */ 'autocorrect'?: 'on' | 'off'; /** diff --git a/core/src/components/input/input.scss b/core/src/components/input/input.scss index ab25ca89544..d3b82917905 100644 --- a/core/src/components/input/input.scss +++ b/core/src/components/input/input.scss @@ -25,7 +25,7 @@ --padding-bottom: 0; --padding-start: 0; --background: transparent; - --color: inherit; + --color: initial; display: flex; position: relative; @@ -108,17 +108,11 @@ // This will only show when the scroll assist is configured // otherwise the .input-cover will not be rendered at all // The input cover is not clickable when the input is disabled - -.input-cover { +.cloned-input { @include position(0, null, null, 0); position: absolute; - width: 100%; - height: 100%; -} - -:host([disabled]) .input-cover { pointer-events: none; } @@ -151,10 +145,6 @@ // -------------------------------------------------- // When the input has focus, then the input cover should be hidden -:host(.has-focus) .input-cover { - display: none; -} - :host(.has-focus) { pointer-events: none; } diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 3d40f3271ed..eace659a375 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -50,7 +50,7 @@ export class Input implements ComponentInterface { @Prop() autocomplete: 'on' | 'off' = 'off'; /** - * Whether autocorrection should be enabled when the user is entering/editing the text value. + * Whether auto correction should be enabled when the user is entering/editing the text value. */ @Prop() autocorrect: 'on' | 'off' = 'off'; diff --git a/core/src/components/input/readme.md b/core/src/components/input/readme.md index f811703ae46..612f1f2ce15 100644 --- a/core/src/components/input/readme.md +++ b/core/src/components/input/readme.md @@ -15,7 +15,7 @@ It is meant for text `type` inputs only, such as `"text"`, `"password"`, `"email | `accept` | `accept` | If the value of the type attribute is `"file"`, then this attribute will indicate the types of files that the server accepts, otherwise it will be ignored. The value must be a comma-separated list of unique content type specifiers. | `string \| undefined` | `undefined` | | `autocapitalize` | `autocapitalize` | Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. | `"characters" \| "off" \| "on" \| "words"` | `'off'` | | `autocomplete` | `autocomplete` | Indicates whether the value of the control can be automatically completed by the browser. | `"off" \| "on"` | `'off'` | -| `autocorrect` | `autocorrect` | Whether autocorrection should be enabled when the user is entering/editing the text value. | `"off" \| "on"` | `'off'` | +| `autocorrect` | `autocorrect` | Whether auto correction should be enabled when the user is entering/editing the text value. | `"off" \| "on"` | `'off'` | | `autofocus` | `autofocus` | This Boolean attribute lets you specify that a form control should have input focus when the page loads. | `boolean` | `false` | | `clearInput` | `clear-input` | If `true`, a clear icon will appear in the input when there is a value. Clicking it clears the input. | `boolean` | `false` | | `clearOnEdit` | `clear-on-edit` | If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types. | `boolean \| undefined` | `undefined` | diff --git a/core/src/components/input/test/basic/index.html b/core/src/components/input/test/basic/index.html index 7297277474b..c020fcfe347 100644 --- a/core/src/components/input/test/basic/index.html +++ b/core/src/components/input/test/basic/index.html @@ -51,7 +51,7 @@ - + Floating diff --git a/core/src/utils/input-shims/hacks/common.ts b/core/src/utils/input-shims/hacks/common.ts index b3eb21a18f1..91351e85fb7 100644 --- a/core/src/utils/input-shims/hacks/common.ts +++ b/core/src/utils/input-shims/hacks/common.ts @@ -1,4 +1,4 @@ -const RELOCATED_KEY = '$ionRelocated'; +const cloneMap = new WeakMap(); export function relocateInput( componentEl: HTMLElement, @@ -6,89 +6,52 @@ export function relocateInput( shouldRelocate: boolean, inputRelativeY = 0 ) { - if ((componentEl as any)[RELOCATED_KEY] === shouldRelocate) { + if (cloneMap.has(componentEl) === shouldRelocate) { return; } console.debug(`native-input, hideCaret, shouldHideCaret: ${shouldRelocate}, input value: ${inputEl.value}`); if (shouldRelocate) { - // this allows for the actual input to receive the focus from - // the user's touch event, but before it receives focus, it - // moves the actual input to a location that will not screw - // up the app's layout, and does not allow the native browser - // to attempt to scroll the input into place (messing up headers/footers) - // 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 - // We hide the focused input (with the visible caret) invisiable by making it scale(0), - cloneInputComponent(componentEl, inputEl); - const doc = componentEl.ownerDocument!; - const tx = doc.dir === 'rtl' ? 9999 : -9999; - inputEl.style.transform = `translate3d(${tx}px,${inputRelativeY}px,0)`; - // TODO - // inputEle.style.opacity = '0'; + addClone(componentEl, inputEl, inputRelativeY); } else { removeClone(componentEl, inputEl); } - (componentEl as any)[RELOCATED_KEY] = shouldRelocate; } export function isFocused(input: HTMLInputElement): boolean { - return input === input.ownerDocument!.activeElement; + return input === (input as any).getRootNode().activeElement; } -function removeClone(componentEl: HTMLElement, inputEl: HTMLElement) { - if (componentEl && componentEl.parentElement) { - Array.from(componentEl.parentElement.querySelectorAll('.cloned-input')) - .forEach(clon => clon.remove()); +function addClone(componentEl: HTMLElement, inputEl: HTMLInputElement, inputRelativeY: number) { + // this allows for the actual input to receive the focus from + // the user's touch event, but before it receives focus, it + // moves the actual input to a location that will not screw + // up the app's layout, and does not allow the native browser + // to attempt to scroll the input into place (messing up headers/footers) + // 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 + // We hide the focused input (with the visible caret) invisible by making it scale(0), + const parentEl = inputEl.parentNode!; + + // DOM WRITES + const clonedEl = inputEl.cloneNode(false) as HTMLInputElement; + clonedEl.classList.add('cloned-input'); + clonedEl.tabIndex = -1; + parentEl.appendChild(clonedEl); + cloneMap.set(componentEl, clonedEl); - componentEl.style.pointerEvents = ''; - } - (inputEl.style as any)['transform'] = ''; - inputEl.style.opacity = ''; -} - -function cloneInputComponent(componentEl: HTMLElement, inputEl: HTMLInputElement) { - // Make sure we kill all the clones before creating new ones - // It is a defensive, removeClone() should do nothing - // removeClone(plt, srcComponentEle, srcNativeInputEle); - // given a native or