input-shims-415be7ee.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. 'use strict';
  5. const index = require('./index-c8d52405.js');
  6. const index$1 = require('./index-31b07b9c.js');
  7. const helpers = require('./helpers-8a48fdea.js');
  8. const keyboard = require('./keyboard-0272231f.js');
  9. require('./index-cc858e97.js');
  10. require('./capacitor-c04564bf.js');
  11. const cloneMap = new WeakMap();
  12. const relocateInput = (componentEl, inputEl, shouldRelocate, inputRelativeY = 0, disabledClonedInput = false) => {
  13. if (cloneMap.has(componentEl) === shouldRelocate) {
  14. return;
  15. }
  16. if (shouldRelocate) {
  17. addClone(componentEl, inputEl, inputRelativeY, disabledClonedInput);
  18. }
  19. else {
  20. removeClone(componentEl, inputEl);
  21. }
  22. };
  23. const isFocused = (input) => {
  24. /**
  25. * https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode
  26. * Calling getRootNode on an element in standard web page will return HTMLDocument.
  27. * Calling getRootNode on an element inside of the Shadow DOM will return the associated ShadowRoot.
  28. * Calling getRootNode on an element that is not attached to a document/shadow tree will return
  29. * the root of the DOM tree it belongs to.
  30. * isFocused is used for the hide-caret utility which only considers input/textarea elements
  31. * that are present in the DOM, so we don't set types for that final case since it does not apply.
  32. */
  33. return input === input.getRootNode().activeElement;
  34. };
  35. const addClone = (componentEl, inputEl, inputRelativeY, disabledClonedInput = false) => {
  36. // this allows for the actual input to receive the focus from
  37. // the user's touch event, but before it receives focus, it
  38. // moves the actual input to a location that will not screw
  39. // up the app's layout, and does not allow the native browser
  40. // to attempt to scroll the input into place (messing up headers/footers)
  41. // the cloned input fills the area of where native input should be
  42. // while the native input fakes out the browser by relocating itself
  43. // before it receives the actual focus event
  44. // We hide the focused input (with the visible caret) invisible by making it scale(0),
  45. const parentEl = inputEl.parentNode;
  46. // DOM WRITES
  47. const clonedEl = inputEl.cloneNode(false);
  48. clonedEl.classList.add('cloned-input');
  49. clonedEl.tabIndex = -1;
  50. /**
  51. * Making the cloned input disabled prevents
  52. * Chrome for Android from still scrolling
  53. * the entire page since this cloned input
  54. * will briefly be hidden by the keyboard
  55. * even though it is not focused.
  56. *
  57. * This is not needed on iOS. While this
  58. * does not cause functional issues on iOS,
  59. * the input still appears slightly dimmed even
  60. * if we set opacity: 1.
  61. */
  62. if (disabledClonedInput) {
  63. clonedEl.disabled = true;
  64. }
  65. parentEl.appendChild(clonedEl);
  66. cloneMap.set(componentEl, clonedEl);
  67. const doc = componentEl.ownerDocument;
  68. const tx = doc.dir === 'rtl' ? 9999 : -9999;
  69. componentEl.style.pointerEvents = 'none';
  70. inputEl.style.transform = `translate3d(${tx}px,${inputRelativeY}px,0) scale(0)`;
  71. };
  72. const removeClone = (componentEl, inputEl) => {
  73. const clone = cloneMap.get(componentEl);
  74. if (clone) {
  75. cloneMap.delete(componentEl);
  76. clone.remove();
  77. }
  78. componentEl.style.pointerEvents = '';
  79. inputEl.style.transform = '';
  80. };
  81. /**
  82. * Factoring in 50px gives us some room
  83. * in case the keyboard shows password/autofill bars
  84. * asynchronously.
  85. */
  86. const SCROLL_AMOUNT_PADDING = 50;
  87. const enableHideCaretOnScroll = (componentEl, inputEl, scrollEl) => {
  88. if (!scrollEl || !inputEl) {
  89. return () => {
  90. return;
  91. };
  92. }
  93. const scrollHideCaret = (shouldHideCaret) => {
  94. if (isFocused(inputEl)) {
  95. relocateInput(componentEl, inputEl, shouldHideCaret);
  96. }
  97. };
  98. const onBlur = () => relocateInput(componentEl, inputEl, false);
  99. const hideCaret = () => scrollHideCaret(true);
  100. const showCaret = () => scrollHideCaret(false);
  101. helpers.addEventListener(scrollEl, 'ionScrollStart', hideCaret);
  102. helpers.addEventListener(scrollEl, 'ionScrollEnd', showCaret);
  103. inputEl.addEventListener('blur', onBlur);
  104. return () => {
  105. helpers.removeEventListener(scrollEl, 'ionScrollStart', hideCaret);
  106. helpers.removeEventListener(scrollEl, 'ionScrollEnd', showCaret);
  107. inputEl.removeEventListener('blur', onBlur);
  108. };
  109. };
  110. const SKIP_SELECTOR = 'input, textarea, [no-blur], [contenteditable]';
  111. const enableInputBlurring = () => {
  112. let focused = true;
  113. let didScroll = false;
  114. const doc = document;
  115. const onScroll = () => {
  116. didScroll = true;
  117. };
  118. const onFocusin = () => {
  119. focused = true;
  120. };
  121. const onTouchend = (ev) => {
  122. // if app did scroll return early
  123. if (didScroll) {
  124. didScroll = false;
  125. return;
  126. }
  127. const active = doc.activeElement;
  128. if (!active) {
  129. return;
  130. }
  131. // only blur if the active element is a text-input or a textarea
  132. if (active.matches(SKIP_SELECTOR)) {
  133. return;
  134. }
  135. // if the selected target is the active element, do not blur
  136. const tapped = ev.target;
  137. if (tapped === active) {
  138. return;
  139. }
  140. if (tapped.matches(SKIP_SELECTOR) || tapped.closest(SKIP_SELECTOR)) {
  141. return;
  142. }
  143. focused = false;
  144. // TODO FW-2796: find a better way, why 50ms?
  145. setTimeout(() => {
  146. if (!focused) {
  147. active.blur();
  148. }
  149. }, 50);
  150. };
  151. helpers.addEventListener(doc, 'ionScrollStart', onScroll);
  152. doc.addEventListener('focusin', onFocusin, true);
  153. doc.addEventListener('touchend', onTouchend, false);
  154. return () => {
  155. helpers.removeEventListener(doc, 'ionScrollStart', onScroll, true);
  156. doc.removeEventListener('focusin', onFocusin, true);
  157. doc.removeEventListener('touchend', onTouchend, false);
  158. };
  159. };
  160. const SCROLL_ASSIST_SPEED = 0.3;
  161. const getScrollData = (componentEl, contentEl, keyboardHeight, platformHeight) => {
  162. var _a;
  163. const itemEl = (_a = componentEl.closest('ion-item,[ion-item]')) !== null && _a !== void 0 ? _a : componentEl;
  164. return calcScrollData(itemEl.getBoundingClientRect(), contentEl.getBoundingClientRect(), keyboardHeight, platformHeight);
  165. };
  166. const calcScrollData = (inputRect, contentRect, keyboardHeight, platformHeight) => {
  167. // compute input's Y values relative to the body
  168. const inputTop = inputRect.top;
  169. const inputBottom = inputRect.bottom;
  170. // compute visible area
  171. const visibleAreaTop = contentRect.top;
  172. const visibleAreaBottom = Math.min(contentRect.bottom, platformHeight - keyboardHeight);
  173. // compute safe area
  174. const safeAreaTop = visibleAreaTop + 15;
  175. const safeAreaBottom = visibleAreaBottom - SCROLL_AMOUNT_PADDING;
  176. // figure out if each edge of the input is within the safe area
  177. const distanceToBottom = safeAreaBottom - inputBottom;
  178. const distanceToTop = safeAreaTop - inputTop;
  179. // desiredScrollAmount is the negated distance to the safe area according to our calculations.
  180. const desiredScrollAmount = Math.round(distanceToBottom < 0 ? -distanceToBottom : distanceToTop > 0 ? -distanceToTop : 0);
  181. // our calculations make some assumptions that aren't always true, like the keyboard being closed when an input
  182. // gets focus, so make sure we don't scroll the input above the visible area
  183. const scrollAmount = Math.min(desiredScrollAmount, inputTop - visibleAreaTop);
  184. const distance = Math.abs(scrollAmount);
  185. const duration = distance / SCROLL_ASSIST_SPEED;
  186. const scrollDuration = Math.min(400, Math.max(150, duration));
  187. return {
  188. scrollAmount,
  189. scrollDuration,
  190. scrollPadding: keyboardHeight,
  191. inputSafeY: -(inputTop - safeAreaTop) + 4,
  192. };
  193. };
  194. const PADDING_TIMER_KEY = '$ionPaddingTimer';
  195. /**
  196. * Scroll padding adds additional padding to the bottom
  197. * of ion-content so that there is enough scroll space
  198. * for an input to be scrolled above the keyboard. This
  199. * is needed in environments where the webview does not
  200. * resize when the keyboard opens.
  201. *
  202. * Example: If an input at the bottom of ion-content is
  203. * focused, there is no additional scrolling space below
  204. * it, so the input cannot be scrolled above the keyboard.
  205. * Scroll padding fixes this by adding padding equal to the
  206. * height of the keyboard to the bottom of the content.
  207. *
  208. * Common environments where this is needed:
  209. * - Mobile Safari: The keyboard overlays the content
  210. * - Capacitor/Cordova on iOS: The keyboard overlays the content
  211. * when the KeyboardResize mode is set to 'none'.
  212. */
  213. const setScrollPadding = (contentEl, paddingAmount, clearCallback) => {
  214. const timer = contentEl[PADDING_TIMER_KEY];
  215. if (timer) {
  216. clearTimeout(timer);
  217. }
  218. if (paddingAmount > 0) {
  219. contentEl.style.setProperty('--keyboard-offset', `${paddingAmount}px`);
  220. }
  221. else {
  222. contentEl[PADDING_TIMER_KEY] = setTimeout(() => {
  223. contentEl.style.setProperty('--keyboard-offset', '0px');
  224. if (clearCallback) {
  225. clearCallback();
  226. }
  227. }, 120);
  228. }
  229. };
  230. /**
  231. * When an input is about to be focused,
  232. * set a timeout to clear any scroll padding
  233. * on the content. Note: The clearing
  234. * is done on a timeout so that if users
  235. * are moving focus from one input to the next
  236. * then re-adding scroll padding to the new
  237. * input with cancel the timeout to clear the
  238. * scroll padding.
  239. */
  240. const setClearScrollPaddingListener = (inputEl, contentEl, doneCallback) => {
  241. const clearScrollPadding = () => {
  242. if (contentEl) {
  243. setScrollPadding(contentEl, 0, doneCallback);
  244. }
  245. };
  246. inputEl.addEventListener('focusout', clearScrollPadding, { once: true });
  247. };
  248. let currentPadding = 0;
  249. const SKIP_SCROLL_ASSIST = 'data-ionic-skip-scroll-assist';
  250. const enableScrollAssist = (componentEl, inputEl, contentEl, footerEl, keyboardHeight, enableScrollPadding, keyboardResize, disableClonedInput = false) => {
  251. /**
  252. * Scroll padding should only be added if:
  253. * 1. The global scrollPadding config option
  254. * is set to true.
  255. * 2. The native keyboard resize mode is either "none"
  256. * (keyboard overlays webview) or undefined (resize
  257. * information unavailable)
  258. * Resize info is available on Capacitor 4+
  259. */
  260. const addScrollPadding = enableScrollPadding && (keyboardResize === undefined || keyboardResize.mode === keyboard.KeyboardResize.None);
  261. /**
  262. * This tracks whether or not the keyboard has been
  263. * presented for a single focused text field. Note
  264. * that it does not track if the keyboard is open
  265. * in general such as if the keyboard is open for
  266. * a different focused text field.
  267. */
  268. let hasKeyboardBeenPresentedForTextField = false;
  269. /**
  270. * When adding scroll padding we need to know
  271. * how much of the viewport the keyboard obscures.
  272. * We do this by subtracting the keyboard height
  273. * from the platform height.
  274. *
  275. * If we compute this value when switching between
  276. * inputs then the webview may already be resized.
  277. * At this point, `win.innerHeight` has already accounted
  278. * for the keyboard meaning we would then subtract
  279. * the keyboard height again. This will result in the input
  280. * being scrolled more than it needs to.
  281. */
  282. const platformHeight = index.win !== undefined ? index.win.innerHeight : 0;
  283. /**
  284. * Scroll assist is run when a text field
  285. * is focused. However, it may need to
  286. * re-run when the keyboard size changes
  287. * such that the text field is now hidden
  288. * underneath the keyboard.
  289. * This function re-runs scroll assist
  290. * when that happens.
  291. *
  292. * One limitation of this is on a web browser
  293. * where native keyboard APIs do not have cross-browser
  294. * support. `ionKeyboardDidShow` relies on the Visual Viewport API.
  295. * This means that if the keyboard changes but does not change
  296. * geometry, then scroll assist will not re-run even if
  297. * the user has scrolled the text field under the keyboard.
  298. * This is not a problem when running in Cordova/Capacitor
  299. * because `ionKeyboardDidShow` uses the native events
  300. * which fire every time the keyboard changes.
  301. */
  302. const keyboardShow = (ev) => {
  303. /**
  304. * If the keyboard has not yet been presented
  305. * for this text field then the text field has just
  306. * received focus. In that case, the focusin listener
  307. * will run scroll assist.
  308. */
  309. if (hasKeyboardBeenPresentedForTextField === false) {
  310. hasKeyboardBeenPresentedForTextField = true;
  311. return;
  312. }
  313. /**
  314. * Otherwise, the keyboard has already been presented
  315. * for the focused text field.
  316. * This means that the keyboard likely changed
  317. * geometry, and we need to re-run scroll assist.
  318. * This can happen when the user rotates their device
  319. * or when they switch keyboards.
  320. *
  321. * Make sure we pass in the computed keyboard height
  322. * rather than the estimated keyboard height.
  323. *
  324. * Since the keyboard is already open then we do not
  325. * need to wait for the webview to resize, so we pass
  326. * "waitForResize: false".
  327. */
  328. jsSetFocus(componentEl, inputEl, contentEl, footerEl, ev.detail.keyboardHeight, addScrollPadding, disableClonedInput, platformHeight, false);
  329. };
  330. /**
  331. * Reset the internal state when the text field loses focus.
  332. */
  333. const focusOut = () => {
  334. hasKeyboardBeenPresentedForTextField = false;
  335. index.win === null || index.win === void 0 ? void 0 : index.win.removeEventListener('ionKeyboardDidShow', keyboardShow);
  336. componentEl.removeEventListener('focusout', focusOut);
  337. };
  338. /**
  339. * When the input is about to receive
  340. * focus, we need to move it to prevent
  341. * mobile Safari from adjusting the viewport.
  342. */
  343. const focusIn = async () => {
  344. /**
  345. * Scroll assist should not run again
  346. * on inputs that have been manually
  347. * focused inside of the scroll assist
  348. * implementation.
  349. */
  350. if (inputEl.hasAttribute(SKIP_SCROLL_ASSIST)) {
  351. inputEl.removeAttribute(SKIP_SCROLL_ASSIST);
  352. return;
  353. }
  354. jsSetFocus(componentEl, inputEl, contentEl, footerEl, keyboardHeight, addScrollPadding, disableClonedInput, platformHeight);
  355. index.win === null || index.win === void 0 ? void 0 : index.win.addEventListener('ionKeyboardDidShow', keyboardShow);
  356. componentEl.addEventListener('focusout', focusOut);
  357. };
  358. componentEl.addEventListener('focusin', focusIn);
  359. return () => {
  360. componentEl.removeEventListener('focusin', focusIn);
  361. index.win === null || index.win === void 0 ? void 0 : index.win.removeEventListener('ionKeyboardDidShow', keyboardShow);
  362. componentEl.removeEventListener('focusout', focusOut);
  363. };
  364. };
  365. /**
  366. * Use this function when you want to manually
  367. * focus an input but not have scroll assist run again.
  368. */
  369. const setManualFocus = (el) => {
  370. /**
  371. * If element is already focused then
  372. * a new focusin event will not be dispatched
  373. * to remove the SKIL_SCROLL_ASSIST attribute.
  374. */
  375. if (document.activeElement === el) {
  376. return;
  377. }
  378. el.setAttribute(SKIP_SCROLL_ASSIST, 'true');
  379. el.focus();
  380. };
  381. const jsSetFocus = async (componentEl, inputEl, contentEl, footerEl, keyboardHeight, enableScrollPadding, disableClonedInput = false, platformHeight = 0, waitForResize = true) => {
  382. if (!contentEl && !footerEl) {
  383. return;
  384. }
  385. const scrollData = getScrollData(componentEl, (contentEl || footerEl), keyboardHeight, platformHeight);
  386. if (contentEl && Math.abs(scrollData.scrollAmount) < 4) {
  387. // the text input is in a safe position that doesn't
  388. // require it to be scrolled into view, just set focus now
  389. setManualFocus(inputEl);
  390. /**
  391. * Even though the input does not need
  392. * scroll assist, we should preserve the
  393. * the scroll padding as users could be moving
  394. * focus from an input that needs scroll padding
  395. * to an input that does not need scroll padding.
  396. * If we remove the scroll padding now, users will
  397. * see the page jump.
  398. */
  399. if (enableScrollPadding && contentEl !== null) {
  400. setScrollPadding(contentEl, currentPadding);
  401. setClearScrollPaddingListener(inputEl, contentEl, () => (currentPadding = 0));
  402. }
  403. return;
  404. }
  405. // temporarily move the focus to the focus holder so the browser
  406. // doesn't freak out while it's trying to get the input in place
  407. // at this point the native text input still does not have focus
  408. relocateInput(componentEl, inputEl, true, scrollData.inputSafeY, disableClonedInput);
  409. setManualFocus(inputEl);
  410. /**
  411. * Relocating/Focusing input causes the
  412. * click event to be cancelled, so
  413. * manually fire one here.
  414. */
  415. helpers.raf(() => componentEl.click());
  416. /**
  417. * If enabled, we can add scroll padding to
  418. * the bottom of the content so that scroll assist
  419. * has enough room to scroll the input above
  420. * the keyboard.
  421. */
  422. if (enableScrollPadding && contentEl) {
  423. currentPadding = scrollData.scrollPadding;
  424. setScrollPadding(contentEl, currentPadding);
  425. }
  426. if (typeof window !== 'undefined') {
  427. let scrollContentTimeout;
  428. const scrollContent = async () => {
  429. // clean up listeners and timeouts
  430. if (scrollContentTimeout !== undefined) {
  431. clearTimeout(scrollContentTimeout);
  432. }
  433. window.removeEventListener('ionKeyboardDidShow', doubleKeyboardEventListener);
  434. window.removeEventListener('ionKeyboardDidShow', scrollContent);
  435. // scroll the input into place
  436. if (contentEl) {
  437. await index$1.scrollByPoint(contentEl, 0, scrollData.scrollAmount, scrollData.scrollDuration);
  438. }
  439. // the scroll view is in the correct position now
  440. // give the native text input focus
  441. relocateInput(componentEl, inputEl, false, scrollData.inputSafeY);
  442. // ensure this is the focused input
  443. setManualFocus(inputEl);
  444. /**
  445. * When the input is about to be blurred
  446. * we should set a timeout to remove
  447. * any scroll padding.
  448. */
  449. if (enableScrollPadding) {
  450. setClearScrollPaddingListener(inputEl, contentEl, () => (currentPadding = 0));
  451. }
  452. };
  453. const doubleKeyboardEventListener = () => {
  454. window.removeEventListener('ionKeyboardDidShow', doubleKeyboardEventListener);
  455. window.addEventListener('ionKeyboardDidShow', scrollContent);
  456. };
  457. if (contentEl) {
  458. const scrollEl = await index$1.getScrollElement(contentEl);
  459. /**
  460. * scrollData will only consider the amount we need
  461. * to scroll in order to properly bring the input
  462. * into view. It will not consider the amount
  463. * we can scroll in the content element.
  464. * As a result, scrollData may request a greater
  465. * scroll position than is currently available
  466. * in the DOM. If this is the case, we need to
  467. * wait for the webview to resize/the keyboard
  468. * to show in order for additional scroll
  469. * bandwidth to become available.
  470. */
  471. const totalScrollAmount = scrollEl.scrollHeight - scrollEl.clientHeight;
  472. if (waitForResize && scrollData.scrollAmount > totalScrollAmount - scrollEl.scrollTop) {
  473. /**
  474. * On iOS devices, the system will show a "Passwords" bar above the keyboard
  475. * after the initial keyboard is shown. This prevents the webview from resizing
  476. * until the "Passwords" bar is shown, so we need to wait for that to happen first.
  477. */
  478. if (inputEl.type === 'password') {
  479. // Add 50px to account for the "Passwords" bar
  480. scrollData.scrollAmount += SCROLL_AMOUNT_PADDING;
  481. window.addEventListener('ionKeyboardDidShow', doubleKeyboardEventListener);
  482. }
  483. else {
  484. window.addEventListener('ionKeyboardDidShow', scrollContent);
  485. }
  486. /**
  487. * This should only fire in 2 instances:
  488. * 1. The app is very slow.
  489. * 2. The app is running in a browser on an old OS
  490. * that does not support Ionic Keyboard Events
  491. */
  492. scrollContentTimeout = setTimeout(scrollContent, 1000);
  493. return;
  494. }
  495. }
  496. scrollContent();
  497. }
  498. };
  499. const INPUT_BLURRING = true;
  500. const startInputShims = async (config, platform) => {
  501. /**
  502. * If doc is undefined then we are in an SSR environment
  503. * where input shims do not apply.
  504. */
  505. if (index.doc === undefined) {
  506. return;
  507. }
  508. const isIOS = platform === 'ios';
  509. const isAndroid = platform === 'android';
  510. /**
  511. * Hide Caret and Input Blurring are needed on iOS.
  512. * Scroll Assist and Scroll Padding are needed on iOS and Android
  513. * with Chrome web browser (not Chrome webview).
  514. */
  515. const keyboardHeight = config.getNumber('keyboardHeight', 290);
  516. const scrollAssist = config.getBoolean('scrollAssist', true);
  517. const hideCaret = config.getBoolean('hideCaretOnScroll', isIOS);
  518. /**
  519. * The team is evaluating if inputBlurring is still needed. As a result
  520. * this feature is disabled by default as of Ionic 8.0. Developers are
  521. * able to re-enable it temporarily. The team may remove this utility
  522. * if it is determined that doing so would not bring any adverse side effects.
  523. * TODO FW-6014 remove input blurring utility (including implementation)
  524. */
  525. const inputBlurring = config.getBoolean('inputBlurring', false);
  526. const scrollPadding = config.getBoolean('scrollPadding', true);
  527. const inputs = Array.from(index.doc.querySelectorAll('ion-input, ion-textarea'));
  528. const hideCaretMap = new WeakMap();
  529. const scrollAssistMap = new WeakMap();
  530. /**
  531. * Grab the native keyboard resize configuration
  532. * and pass it to scroll assist. Scroll assist requires
  533. * that we adjust the input right before the input
  534. * is about to be focused. If we called `Keyboard.getResizeMode`
  535. * on focusin in scroll assist, we could potentially adjust the
  536. * input too late since this call is async.
  537. */
  538. const keyboardResizeMode = await keyboard.Keyboard.getResizeMode();
  539. const registerInput = async (componentEl) => {
  540. await new Promise((resolve) => helpers.componentOnReady(componentEl, resolve));
  541. const inputRoot = componentEl.shadowRoot || componentEl;
  542. const inputEl = inputRoot.querySelector('input') || inputRoot.querySelector('textarea');
  543. const scrollEl = index$1.findClosestIonContent(componentEl);
  544. const footerEl = !scrollEl ? componentEl.closest('ion-footer') : null;
  545. if (!inputEl) {
  546. return;
  547. }
  548. if (!!scrollEl && hideCaret && !hideCaretMap.has(componentEl)) {
  549. const rmFn = enableHideCaretOnScroll(componentEl, inputEl, scrollEl);
  550. hideCaretMap.set(componentEl, rmFn);
  551. }
  552. /**
  553. * date/datetime-locale inputs on mobile devices show date picker
  554. * overlays instead of keyboards. As a result, scroll assist is
  555. * not needed. This also works around a bug in iOS <16 where
  556. * scroll assist causes the browser to lock up. See FW-1997.
  557. */
  558. const isDateInput = inputEl.type === 'date' || inputEl.type === 'datetime-local';
  559. if (!isDateInput &&
  560. (!!scrollEl || !!footerEl) &&
  561. scrollAssist &&
  562. !scrollAssistMap.has(componentEl)) {
  563. const rmFn = enableScrollAssist(componentEl, inputEl, scrollEl, footerEl, keyboardHeight, scrollPadding, keyboardResizeMode, isAndroid);
  564. scrollAssistMap.set(componentEl, rmFn);
  565. }
  566. };
  567. const unregisterInput = (componentEl) => {
  568. if (hideCaret) {
  569. const fn = hideCaretMap.get(componentEl);
  570. if (fn) {
  571. fn();
  572. }
  573. hideCaretMap.delete(componentEl);
  574. }
  575. if (scrollAssist) {
  576. const fn = scrollAssistMap.get(componentEl);
  577. if (fn) {
  578. fn();
  579. }
  580. scrollAssistMap.delete(componentEl);
  581. }
  582. };
  583. if (inputBlurring && INPUT_BLURRING) {
  584. enableInputBlurring();
  585. }
  586. // Input might be already loaded in the DOM before ion-device-hacks did.
  587. // At this point we need to look for all of the inputs not registered yet
  588. // and register them.
  589. for (const input of inputs) {
  590. registerInput(input);
  591. }
  592. index.doc.addEventListener('ionInputDidLoad', (ev) => {
  593. registerInput(ev.detail);
  594. });
  595. index.doc.addEventListener('ionInputDidUnload', (ev) => {
  596. unregisterInput(ev.detail);
  597. });
  598. };
  599. exports.startInputShims = startInputShims;