-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(input): scroll assist works in with shadow-dom (#16206)
- Loading branch information
1 parent
0abf992
commit d817cc3
Showing
9 changed files
with
45 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,94 +1,57 @@ | ||
const RELOCATED_KEY = '$ionRelocated'; | ||
const cloneMap = new WeakMap<HTMLElement, HTMLElement>(); | ||
|
||
export function relocateInput( | ||
componentEl: HTMLElement, | ||
inputEl: HTMLInputElement, | ||
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 <input> or <textarea> element | ||
// find its parent wrapping component like <ion-input> or <ion-textarea> | ||
// then clone the entire component | ||
const parentElement = componentEl.parentElement; | ||
const doc = componentEl.ownerDocument!; | ||
if (componentEl && parentElement) { | ||
// DOM READ | ||
const srcTop = componentEl.offsetTop; | ||
const srcLeft = componentEl.offsetLeft; | ||
const srcWidth = componentEl.offsetWidth; | ||
const srcHeight = componentEl.offsetHeight; | ||
|
||
// DOM WRITE | ||
// not using deep clone so we don't pull in unnecessary nodes | ||
const clonedComponentEle = doc.createElement('div'); | ||
const clonedStyle = clonedComponentEle.style; | ||
clonedComponentEle.classList.add(...Array.from(componentEl.classList)); | ||
clonedComponentEle.classList.add('cloned-input'); | ||
clonedComponentEle.setAttribute('aria-hidden', 'true'); | ||
clonedStyle.pointerEvents = 'none'; | ||
clonedStyle.position = 'absolute'; | ||
clonedStyle.top = srcTop + 'px'; | ||
clonedStyle.left = srcLeft + 'px'; | ||
clonedStyle.width = srcWidth + 'px'; | ||
clonedStyle.height = srcHeight + 'px'; | ||
|
||
const clonedInputEl = doc.createElement('input'); | ||
clonedInputEl.classList.add(...Array.from(inputEl.classList)); | ||
clonedInputEl.value = inputEl.value; | ||
clonedInputEl.type = inputEl.type; | ||
clonedInputEl.placeholder = inputEl.placeholder; | ||
|
||
clonedInputEl.tabIndex = -1; | ||
|
||
clonedComponentEle.appendChild(clonedInputEl); | ||
parentElement.appendChild(clonedComponentEle); | ||
const tx = doc.dir === 'rtl' ? 9999 : -9999; | ||
componentEl.style.pointerEvents = 'none'; | ||
inputEl.style.transform = `translate3d(${tx}px,${inputRelativeY}px,0) scale(0)`; | ||
} | ||
|
||
componentEl.style.pointerEvents = 'none'; | ||
function removeClone(componentEl: HTMLElement, inputEl: HTMLElement) { | ||
const clone = cloneMap.get(componentEl); | ||
if (clone) { | ||
cloneMap.delete(componentEl); | ||
clone.remove(); | ||
} | ||
inputEl.style.transform = 'scale(0)'; | ||
componentEl.style.pointerEvents = ''; | ||
inputEl.style.transform = ''; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters