popover-transitions.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import { Animation } from '../../animations/animation';
  2. import { PageTransition } from '../../transitions/page-transition';
  3. /**
  4. * Animations for popover
  5. */
  6. export class PopoverTransition extends PageTransition {
  7. mdPositionView(nativeEle, ev) {
  8. let originY = 'top';
  9. let originX = 'left';
  10. let popoverWrapperEle = nativeEle.querySelector('.popover-wrapper');
  11. // Popover content width and height
  12. let popoverEle = nativeEle.querySelector('.popover-content');
  13. let popoverDim = popoverEle.getBoundingClientRect();
  14. let popoverWidth = popoverDim.width;
  15. let popoverHeight = popoverDim.height;
  16. // Window body width and height
  17. let bodyWidth = this.plt.width();
  18. let bodyHeight = this.plt.height();
  19. // If ev was passed, use that for target element
  20. let targetDim = ev && ev.target && ev.target.getBoundingClientRect();
  21. let targetTop = (targetDim && 'top' in targetDim) ? targetDim.top : (bodyHeight / 2) - (popoverHeight / 2);
  22. let targetLeft = (targetDim && 'left' in targetDim) ? targetDim.left : (bodyWidth / 2) - (popoverWidth / 2);
  23. let targetHeight = targetDim && targetDim.height || 0;
  24. let popoverCSS = {
  25. top: targetTop,
  26. left: targetLeft
  27. };
  28. // If the popover left is less than the padding it is off screen
  29. // to the left so adjust it, else if the width of the popover
  30. // exceeds the body width it is off screen to the right so adjust
  31. if (popoverCSS.left < POPOVER_MD_BODY_PADDING) {
  32. popoverCSS.left = POPOVER_MD_BODY_PADDING;
  33. }
  34. else if (popoverWidth + POPOVER_MD_BODY_PADDING + popoverCSS.left > bodyWidth) {
  35. popoverCSS.left = bodyWidth - popoverWidth - POPOVER_MD_BODY_PADDING;
  36. originX = 'right';
  37. }
  38. // If the popover when popped down stretches past bottom of screen,
  39. // make it pop up if there's room above
  40. if (targetTop + targetHeight + popoverHeight > bodyHeight && targetTop - popoverHeight > 0) {
  41. popoverCSS.top = targetTop - popoverHeight;
  42. nativeEle.className = nativeEle.className + ' popover-bottom';
  43. originY = 'bottom';
  44. // If there isn't room for it to pop up above the target cut it off
  45. }
  46. else if (targetTop + targetHeight + popoverHeight > bodyHeight) {
  47. popoverEle.style.bottom = POPOVER_MD_BODY_PADDING + 'px';
  48. }
  49. popoverEle.style.top = popoverCSS.top + 'px';
  50. popoverEle.style.left = popoverCSS.left + 'px';
  51. popoverEle.style[this.plt.Css.transformOrigin] = originY + ' ' + originX;
  52. // Since the transition starts before styling is done we
  53. // want to wait for the styles to apply before showing the wrapper
  54. popoverWrapperEle.style.opacity = '1';
  55. }
  56. iosPositionView(nativeEle, ev) {
  57. let originY = 'top';
  58. let originX = 'left';
  59. let popoverWrapperEle = nativeEle.querySelector('.popover-wrapper');
  60. // Popover content width and height
  61. let popoverEle = nativeEle.querySelector('.popover-content');
  62. let popoverDim = popoverEle.getBoundingClientRect();
  63. let popoverWidth = popoverDim.width;
  64. let popoverHeight = popoverDim.height;
  65. // Window body width and height
  66. let bodyWidth = this.plt.width();
  67. let bodyHeight = this.plt.height();
  68. // If ev was passed, use that for target element
  69. let targetDim = ev && ev.target && ev.target.getBoundingClientRect();
  70. let targetTop = (targetDim && 'top' in targetDim) ? targetDim.top : (bodyHeight / 2) - (popoverHeight / 2);
  71. let targetLeft = (targetDim && 'left' in targetDim) ? targetDim.left : (bodyWidth / 2);
  72. let targetWidth = targetDim && targetDim.width || 0;
  73. let targetHeight = targetDim && targetDim.height || 0;
  74. // The arrow that shows above the popover on iOS
  75. var arrowEle = nativeEle.querySelector('.popover-arrow');
  76. let arrowDim = arrowEle.getBoundingClientRect();
  77. var arrowWidth = arrowDim.width;
  78. var arrowHeight = arrowDim.height;
  79. // If no ev was passed, hide the arrow
  80. if (!targetDim) {
  81. arrowEle.style.display = 'none';
  82. }
  83. let arrowCSS = {
  84. top: targetTop + targetHeight,
  85. left: targetLeft + (targetWidth / 2) - (arrowWidth / 2)
  86. };
  87. let popoverCSS = {
  88. top: targetTop + targetHeight + (arrowHeight - 1),
  89. left: targetLeft + (targetWidth / 2) - (popoverWidth / 2)
  90. };
  91. // If the popover left is less than the padding it is off screen
  92. // to the left so adjust it, else if the width of the popover
  93. // exceeds the body width it is off screen to the right so adjust
  94. //
  95. let checkSafeAreaLeft = false;
  96. let checkSafeAreaRight = false;
  97. // If the popover left is less than the padding it is off screen
  98. // to the left so adjust it, else if the width of the popover
  99. // exceeds the body width it is off screen to the right so adjust
  100. // 25 is a random/arbitrary number. It seems to work fine for ios11
  101. // and iPhoneX. Is it perfect? No. Does it work? Yes.
  102. if (popoverCSS.left < (POPOVER_IOS_BODY_PADDING + 25)) {
  103. checkSafeAreaLeft = true;
  104. popoverCSS.left = POPOVER_IOS_BODY_PADDING;
  105. }
  106. else if ((popoverWidth + POPOVER_IOS_BODY_PADDING + popoverCSS.left + 25) > bodyWidth) {
  107. // Ok, so we're on the right side of the screen,
  108. // but now we need to make sure we're still a bit further right
  109. // cus....notchurally... Again, 25 is random. It works tho
  110. checkSafeAreaRight = true;
  111. popoverCSS.left = bodyWidth - popoverWidth - POPOVER_IOS_BODY_PADDING;
  112. originX = 'right';
  113. }
  114. // make it pop up if there's room above
  115. if (targetTop + targetHeight + popoverHeight > bodyHeight && targetTop - popoverHeight > 0) {
  116. arrowCSS.top = targetTop - (arrowHeight + 1);
  117. popoverCSS.top = targetTop - popoverHeight - (arrowHeight - 1);
  118. nativeEle.className = nativeEle.className + ' popover-bottom';
  119. originY = 'bottom';
  120. // If there isn't room for it to pop up above the target cut it off
  121. }
  122. else if (targetTop + targetHeight + popoverHeight > bodyHeight) {
  123. popoverEle.style.bottom = POPOVER_IOS_BODY_PADDING + '%';
  124. }
  125. arrowEle.style.top = arrowCSS.top + 'px';
  126. arrowEle.style.left = arrowCSS.left + 'px';
  127. popoverEle.style.top = popoverCSS.top + 'px';
  128. popoverEle.style.left = popoverCSS.left + 'px';
  129. if (checkSafeAreaLeft) {
  130. if (CSS.supports('left', 'constant(safe-area-inset-left)')) {
  131. popoverEle.style.left = `calc(${popoverCSS.left}px + constant(safe-area-inset-left)`;
  132. }
  133. else if (CSS.supports('left', 'env(safe-area-inset-left)')) {
  134. popoverEle.style.left = `calc(${popoverCSS.left}px + env(safe-area-inset-left)`;
  135. }
  136. }
  137. if (checkSafeAreaRight) {
  138. if (CSS.supports('right', 'constant(safe-area-inset-right)')) {
  139. popoverEle.style.left = `calc(${popoverCSS.left}px - constant(safe-area-inset-right)`;
  140. }
  141. else if (CSS.supports('right', 'env(safe-area-inset-right)')) {
  142. popoverEle.style.left = `calc(${popoverCSS.left}px - env(safe-area-inset-right)`;
  143. }
  144. }
  145. popoverEle.style[this.plt.Css.transformOrigin] = originY + ' ' + originX;
  146. // Since the transition starts before styling is done we
  147. // want to wait for the styles to apply before showing the wrapper
  148. popoverWrapperEle.style.opacity = '1';
  149. }
  150. }
  151. export class PopoverPopIn extends PopoverTransition {
  152. init() {
  153. let ele = this.enteringView.pageRef().nativeElement;
  154. let backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop'));
  155. let wrapper = new Animation(this.plt, ele.querySelector('.popover-wrapper'));
  156. wrapper.fromTo('opacity', 0.01, 1);
  157. backdrop.fromTo('opacity', 0.01, 0.08);
  158. this
  159. .easing('ease')
  160. .duration(100)
  161. .add(backdrop)
  162. .add(wrapper);
  163. }
  164. play() {
  165. this.plt.raf(() => {
  166. this.iosPositionView(this.enteringView.pageRef().nativeElement, this.opts.ev);
  167. super.play();
  168. });
  169. }
  170. }
  171. export class PopoverPopOut extends PopoverTransition {
  172. init() {
  173. let ele = this.leavingView.pageRef().nativeElement;
  174. let backdrop = new Animation(this.plt, ele.querySelector('ion-backdrop'));
  175. let wrapper = new Animation(this.plt, ele.querySelector('.popover-wrapper'));
  176. wrapper.fromTo('opacity', 0.99, 0);
  177. backdrop.fromTo('opacity', 0.08, 0);
  178. this
  179. .easing('ease')
  180. .duration(500)
  181. .add(backdrop)
  182. .add(wrapper);
  183. }
  184. }
  185. export class PopoverMdPopIn extends PopoverTransition {
  186. init() {
  187. let ele = this.enteringView.pageRef().nativeElement;
  188. let content = new Animation(this.plt, ele.querySelector('.popover-content'));
  189. let viewport = new Animation(this.plt, ele.querySelector('.popover-viewport'));
  190. content.fromTo('scale', 0.001, 1);
  191. viewport.fromTo('opacity', 0.01, 1);
  192. this
  193. .easing('cubic-bezier(0.36,0.66,0.04,1)')
  194. .duration(300)
  195. .add(content)
  196. .add(viewport);
  197. }
  198. play() {
  199. this.plt.raf(() => {
  200. this.mdPositionView(this.enteringView.pageRef().nativeElement, this.opts.ev);
  201. super.play();
  202. });
  203. }
  204. }
  205. export class PopoverMdPopOut extends PopoverTransition {
  206. init() {
  207. let ele = this.leavingView.pageRef().nativeElement;
  208. let wrapper = new Animation(this.plt, ele.querySelector('.popover-wrapper'));
  209. wrapper.fromTo('opacity', 0.99, 0);
  210. this
  211. .easing('ease')
  212. .duration(500)
  213. .fromTo('opacity', 0.01, 1)
  214. .add(wrapper);
  215. }
  216. }
  217. const POPOVER_IOS_BODY_PADDING = 2;
  218. const POPOVER_MD_BODY_PADDING = 12;
  219. //# sourceMappingURL=popover-transitions.js.map