keyboard-controller.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. import { w as win, d as doc } from './index6.js';
  5. import { K as Keyboard, a as KeyboardResize } from './keyboard.js';
  6. /**
  7. * The element that resizes when the keyboard opens
  8. * is going to depend on the resize mode
  9. * which is why we check that here.
  10. */
  11. const getResizeContainer = (resizeMode) => {
  12. /**
  13. * If doc is undefined then we are
  14. * in an SSR environment, so the keyboard
  15. * adjustment does not apply.
  16. * If the webview does not resize then there
  17. * is no container to resize.
  18. */
  19. if (doc === undefined || resizeMode === KeyboardResize.None || resizeMode === undefined) {
  20. return null;
  21. }
  22. /**
  23. * The three remaining resize modes: Native, Ionic, and Body
  24. * all cause `ion-app` to resize, so we can listen for changes
  25. * on that. In the event `ion-app` is not available then
  26. * we can fall back to `body`.
  27. */
  28. const ionApp = doc.querySelector('ion-app');
  29. return ionApp !== null && ionApp !== void 0 ? ionApp : doc.body;
  30. };
  31. /**
  32. * Get the height of ion-app or body.
  33. * This is used for determining if the webview
  34. * has resized before the keyboard closed.
  35. * */
  36. const getResizeContainerHeight = (resizeMode) => {
  37. const containerElement = getResizeContainer(resizeMode);
  38. return containerElement === null ? 0 : containerElement.clientHeight;
  39. };
  40. /**
  41. * Creates a controller that tracks and reacts to opening or closing the keyboard.
  42. *
  43. * @internal
  44. * @param keyboardChangeCallback A function to call when the keyboard opens or closes.
  45. */
  46. const createKeyboardController = async (keyboardChangeCallback) => {
  47. let keyboardWillShowHandler;
  48. let keyboardWillHideHandler;
  49. let keyboardVisible;
  50. /**
  51. * This lets us determine if the webview content
  52. * has resized as a result of the keyboard.
  53. */
  54. let initialResizeContainerHeight;
  55. const init = async () => {
  56. const resizeOptions = await Keyboard.getResizeMode();
  57. const resizeMode = resizeOptions === undefined ? undefined : resizeOptions.mode;
  58. keyboardWillShowHandler = () => {
  59. /**
  60. * We need to compute initialResizeContainerHeight right before
  61. * the keyboard opens to guarantee the resize container is visible.
  62. * The resize container may not be visible if we compute this
  63. * as soon as the keyboard controller is created.
  64. * We should only need to do this once to avoid additional clientHeight
  65. * computations.
  66. */
  67. if (initialResizeContainerHeight === undefined) {
  68. initialResizeContainerHeight = getResizeContainerHeight(resizeMode);
  69. }
  70. keyboardVisible = true;
  71. fireChangeCallback(keyboardVisible, resizeMode);
  72. };
  73. keyboardWillHideHandler = () => {
  74. keyboardVisible = false;
  75. fireChangeCallback(keyboardVisible, resizeMode);
  76. };
  77. win === null || win === void 0 ? void 0 : win.addEventListener('keyboardWillShow', keyboardWillShowHandler);
  78. win === null || win === void 0 ? void 0 : win.addEventListener('keyboardWillHide', keyboardWillHideHandler);
  79. };
  80. const fireChangeCallback = (state, resizeMode) => {
  81. if (keyboardChangeCallback) {
  82. keyboardChangeCallback(state, createResizePromiseIfNeeded(resizeMode));
  83. }
  84. };
  85. /**
  86. * Code responding to keyboard lifecycles may need
  87. * to show/hide content once the webview has
  88. * resized as a result of the keyboard showing/hiding.
  89. * createResizePromiseIfNeeded provides a way for code to wait for the
  90. * resize event that was triggered as a result of the keyboard.
  91. */
  92. const createResizePromiseIfNeeded = (resizeMode) => {
  93. if (
  94. /**
  95. * If we are in an SSR environment then there is
  96. * no window to resize. Additionally, if there
  97. * is no resize mode or the resize mode is "None"
  98. * then initialResizeContainerHeight will be 0
  99. */
  100. initialResizeContainerHeight === 0 ||
  101. /**
  102. * If the keyboard is closed before the webview resizes initially
  103. * then the webview will never resize.
  104. */
  105. initialResizeContainerHeight === getResizeContainerHeight(resizeMode)) {
  106. return;
  107. }
  108. /**
  109. * Get the resize container so we can
  110. * attach the ResizeObserver below to
  111. * the correct element.
  112. */
  113. const containerElement = getResizeContainer(resizeMode);
  114. if (containerElement === null) {
  115. return;
  116. }
  117. /**
  118. * Some part of the web content should resize,
  119. * and we need to listen for a resize.
  120. */
  121. return new Promise((resolve) => {
  122. const callback = () => {
  123. /**
  124. * As per the spec, the ResizeObserver
  125. * will fire when observation starts if
  126. * the observed element is rendered and does not
  127. * have a size of 0 x 0. However, the watched element
  128. * may or may not have resized by the time this first
  129. * callback is fired. As a result, we need to check
  130. * the dimensions of the element.
  131. *
  132. * https://www.w3.org/TR/resize-observer/#intro
  133. */
  134. if (containerElement.clientHeight === initialResizeContainerHeight) {
  135. /**
  136. * The resize happened, so stop listening
  137. * for resize on this element.
  138. */
  139. ro.disconnect();
  140. resolve();
  141. }
  142. };
  143. /**
  144. * In Capacitor there can be delay between when the window
  145. * resizes and when the container element resizes, so we cannot
  146. * rely on a 'resize' event listener on the window.
  147. * Instead, we need to determine when the container
  148. * element resizes using a ResizeObserver.
  149. */
  150. const ro = new ResizeObserver(callback);
  151. ro.observe(containerElement);
  152. });
  153. };
  154. const destroy = () => {
  155. win === null || win === void 0 ? void 0 : win.removeEventListener('keyboardWillShow', keyboardWillShowHandler);
  156. win === null || win === void 0 ? void 0 : win.removeEventListener('keyboardWillHide', keyboardWillHideHandler);
  157. keyboardWillShowHandler = keyboardWillHideHandler = undefined;
  158. };
  159. const isKeyboardVisible = () => keyboardVisible;
  160. await init();
  161. return { init, destroy, isKeyboardVisible };
  162. };
  163. export { createKeyboardController as c };