index-be190feb.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. import { d as doc } from './index-a5d50daf.js';
  5. import { q as pointerCoord } from './helpers-d94bc8ad.js';
  6. import './index-cfd9c1f2.js';
  7. const startTapClick = (config) => {
  8. if (doc === undefined) {
  9. return;
  10. }
  11. let lastActivated = 0;
  12. let activatableEle;
  13. let activeRipple;
  14. let activeDefer;
  15. const useRippleEffect = config.getBoolean('animated', true) && config.getBoolean('rippleEffect', true);
  16. const clearDefers = new WeakMap();
  17. const cancelActive = () => {
  18. if (activeDefer)
  19. clearTimeout(activeDefer);
  20. activeDefer = undefined;
  21. if (activatableEle) {
  22. removeActivated(false);
  23. activatableEle = undefined;
  24. }
  25. };
  26. const pointerDown = (ev) => {
  27. // Ignore right clicks
  28. if (activatableEle || ev.button === 2) {
  29. return;
  30. }
  31. setActivatedElement(getActivatableTarget(ev), ev);
  32. };
  33. const pointerUp = (ev) => {
  34. setActivatedElement(undefined, ev);
  35. };
  36. const setActivatedElement = (el, ev) => {
  37. // do nothing
  38. if (el && el === activatableEle) {
  39. return;
  40. }
  41. if (activeDefer)
  42. clearTimeout(activeDefer);
  43. activeDefer = undefined;
  44. const { x, y } = pointerCoord(ev);
  45. // deactivate selected
  46. if (activatableEle) {
  47. if (clearDefers.has(activatableEle)) {
  48. throw new Error('internal error');
  49. }
  50. if (!activatableEle.classList.contains(ACTIVATED)) {
  51. addActivated(activatableEle, x, y);
  52. }
  53. removeActivated(true);
  54. }
  55. // activate
  56. if (el) {
  57. const deferId = clearDefers.get(el);
  58. if (deferId) {
  59. clearTimeout(deferId);
  60. clearDefers.delete(el);
  61. }
  62. el.classList.remove(ACTIVATED);
  63. const callback = () => {
  64. addActivated(el, x, y);
  65. activeDefer = undefined;
  66. };
  67. if (isInstant(el)) {
  68. callback();
  69. }
  70. else {
  71. activeDefer = setTimeout(callback, ADD_ACTIVATED_DEFERS);
  72. }
  73. }
  74. activatableEle = el;
  75. };
  76. const addActivated = (el, x, y) => {
  77. lastActivated = Date.now();
  78. el.classList.add(ACTIVATED);
  79. if (!useRippleEffect)
  80. return;
  81. const rippleEffect = getRippleEffect(el);
  82. if (rippleEffect !== null) {
  83. removeRipple();
  84. activeRipple = rippleEffect.addRipple(x, y);
  85. }
  86. };
  87. const removeRipple = () => {
  88. if (activeRipple !== undefined) {
  89. activeRipple.then((remove) => remove());
  90. activeRipple = undefined;
  91. }
  92. };
  93. const removeActivated = (smooth) => {
  94. removeRipple();
  95. const active = activatableEle;
  96. if (!active) {
  97. return;
  98. }
  99. const time = CLEAR_STATE_DEFERS - Date.now() + lastActivated;
  100. if (smooth && time > 0 && !isInstant(active)) {
  101. const deferId = setTimeout(() => {
  102. active.classList.remove(ACTIVATED);
  103. clearDefers.delete(active);
  104. }, CLEAR_STATE_DEFERS);
  105. clearDefers.set(active, deferId);
  106. }
  107. else {
  108. active.classList.remove(ACTIVATED);
  109. }
  110. };
  111. doc.addEventListener('ionGestureCaptured', cancelActive);
  112. doc.addEventListener('pointerdown', pointerDown, true);
  113. doc.addEventListener('pointerup', pointerUp, true);
  114. /**
  115. * Tap click effects such as the ripple effect should
  116. * not happen when scrolling. For example, if a user scrolls
  117. * the page but also happens to do a touchstart on a button
  118. * as part of the scroll, the ripple effect should not
  119. * be dispatched. The ripple effect should only happen
  120. * if the button is activated and the page is not scrolling.
  121. *
  122. * pointercancel is dispatched on a gesture when scrolling
  123. * starts, so this lets us avoid having to listen for
  124. * ion-content's scroll events.
  125. */
  126. doc.addEventListener('pointercancel', cancelActive, true);
  127. };
  128. // TODO(FW-2832): type
  129. const getActivatableTarget = (ev) => {
  130. if (ev.composedPath !== undefined) {
  131. /**
  132. * composedPath returns EventTarget[]. However,
  133. * objects other than Element can be targets too.
  134. * For example, AudioContext can be a target. In this
  135. * case, we know that the event is a UIEvent so we
  136. * can assume that the path will contain either Element
  137. * or ShadowRoot.
  138. */
  139. const path = ev.composedPath();
  140. for (let i = 0; i < path.length - 2; i++) {
  141. const el = path[i];
  142. if (!(el instanceof ShadowRoot) && el.classList.contains('ion-activatable')) {
  143. return el;
  144. }
  145. }
  146. }
  147. else {
  148. return ev.target.closest('.ion-activatable');
  149. }
  150. };
  151. const isInstant = (el) => {
  152. return el.classList.contains('ion-activatable-instant');
  153. };
  154. const getRippleEffect = (el) => {
  155. if (el.shadowRoot) {
  156. const ripple = el.shadowRoot.querySelector('ion-ripple-effect');
  157. if (ripple) {
  158. return ripple;
  159. }
  160. }
  161. return el.querySelector('ion-ripple-effect');
  162. };
  163. const ACTIVATED = 'ion-activated';
  164. const ADD_ACTIVATED_DEFERS = 100;
  165. const CLEAR_STATE_DEFERS = 150;
  166. export { startTapClick };