UNPKG

@ionic/core

Version:

Base components for Ionic

128 lines (127 loc) • 5.13 kB
import { pointerCoord, raf } from '../../helpers'; import { isFocused, relocateInput } from './common'; import { getScrollData } from './scroll-data'; export const enableScrollAssist = (componentEl, inputEl, contentEl, footerEl, keyboardHeight) => { let coord; const touchStart = (ev) => { coord = pointerCoord(ev); }; const touchEnd = (ev) => { // input cover touchend/mouseup if (!coord) { return; } // get where the touchend/mouseup ended const endCoord = pointerCoord(ev); // focus this input if the pointer hasn't moved XX pixels // and the input doesn't already have focus if (!hasPointerMoved(6, coord, endCoord) && !isFocused(inputEl)) { ev.stopPropagation(); // begin the input focus process jsSetFocus(componentEl, inputEl, contentEl, footerEl, keyboardHeight); } }; componentEl.addEventListener('touchstart', touchStart, true); componentEl.addEventListener('touchend', touchEnd, true); return () => { componentEl.removeEventListener('touchstart', touchStart, true); componentEl.removeEventListener('touchend', touchEnd, true); }; }; const jsSetFocus = async (componentEl, inputEl, contentEl, footerEl, keyboardHeight) => { if (!contentEl && !footerEl) { return; } const scrollData = getScrollData(componentEl, (contentEl || footerEl), keyboardHeight); if (contentEl && Math.abs(scrollData.scrollAmount) < 4) { // the text input is in a safe position that doesn't // require it to be scrolled into view, just set focus now inputEl.focus(); return; } // 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 // at this point the native text input still does not have focus relocateInput(componentEl, inputEl, true, scrollData.inputSafeY); inputEl.focus(); /** * Relocating/Focusing input causes the * click event to be cancelled, so * manually fire one here. */ raf(() => componentEl.click()); /* tslint:disable-next-line */ if (typeof window !== 'undefined') { let scrollContentTimeout; const scrollContent = async () => { // clean up listeners and timeouts if (scrollContentTimeout !== undefined) { clearTimeout(scrollContentTimeout); } window.removeEventListener('ionKeyboardDidShow', doubleKeyboardEventListener); window.removeEventListener('ionKeyboardDidShow', scrollContent); // scroll the input into place if (contentEl) { await contentEl.scrollByPoint(0, scrollData.scrollAmount, scrollData.scrollDuration); } // the scroll view is in the correct position now // give the native text input focus relocateInput(componentEl, inputEl, false, scrollData.inputSafeY); // ensure this is the focused input inputEl.focus(); }; const doubleKeyboardEventListener = () => { window.removeEventListener('ionKeyboardDidShow', doubleKeyboardEventListener); window.addEventListener('ionKeyboardDidShow', scrollContent); }; if (contentEl) { const scrollEl = await contentEl.getScrollElement(); /** * scrollData will only consider the amount we need * to scroll in order to properly bring the input * into view. It will not consider the amount * we can scroll in the content element. * As a result, scrollData may request a greater * scroll position than is currently available * in the DOM. If this is the case, we need to * wait for the webview to resize/the keyboard * to show in order for additional scroll * bandwidth to become available. */ const totalScrollAmount = scrollEl.scrollHeight - scrollEl.clientHeight; if (scrollData.scrollAmount > (totalScrollAmount - scrollEl.scrollTop)) { /** * On iOS devices, the system will show a "Passwords" bar above the keyboard * after the initial keyboard is shown. This prevents the webview from resizing * until the "Passwords" bar is shown, so we need to wait for that to happen first. */ if (inputEl.type === 'password') { // Add 50px to account for the "Passwords" bar scrollData.scrollAmount += 50; window.addEventListener('ionKeyboardDidShow', doubleKeyboardEventListener); } else { window.addEventListener('ionKeyboardDidShow', scrollContent); } /** * This should only fire in 2 instances: * 1. The app is very slow. * 2. The app is running in a browser on an old OS * that does not support Ionic Keyboard Events */ scrollContentTimeout = setTimeout(scrollContent, 1000); return; } } scrollContent(); } }; const hasPointerMoved = (threshold, startCoord, endCoord) => { if (startCoord && endCoord) { const deltaX = (startCoord.x - endCoord.x); const deltaY = (startCoord.y - endCoord.y); const distance = deltaX * deltaX + deltaY * deltaY; return distance > (threshold * threshold); } return false; };