index.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import { context } from './context';
  2. import { TouchMixin } from '../touch';
  3. import { on, off, preventDefault } from '../../utils/event';
  4. import { openOverlay, closeOverlay, updateOverlay } from './overlay';
  5. import { getScrollEventTarget } from '../../utils/scroll';
  6. export const PopupMixin = {
  7. mixins: [TouchMixin],
  8. props: {
  9. // whether to show popup
  10. value: Boolean,
  11. // whether to show overlay
  12. overlay: Boolean,
  13. // overlay custom style
  14. overlayStyle: Object,
  15. // overlay custom class name
  16. overlayClass: String,
  17. // whether to close popup when click overlay
  18. closeOnClickOverlay: Boolean,
  19. // z-index
  20. zIndex: [String, Number],
  21. // return the mount node for popup
  22. getContainer: [String, Function],
  23. // prevent body scroll
  24. lockScroll: {
  25. type: Boolean,
  26. default: true
  27. },
  28. // whether to lazy render
  29. lazyRender: {
  30. type: Boolean,
  31. default: true
  32. }
  33. },
  34. data() {
  35. return {
  36. inited: this.value
  37. };
  38. },
  39. computed: {
  40. shouldRender() {
  41. return this.inited || !this.lazyRender;
  42. }
  43. },
  44. watch: {
  45. value(val) {
  46. const type = val ? 'open' : 'close';
  47. this.inited = this.inited || this.value;
  48. this[type]();
  49. this.$emit(type);
  50. },
  51. getContainer() {
  52. this.move();
  53. },
  54. overlay() {
  55. this.renderOverlay();
  56. }
  57. },
  58. mounted() {
  59. if (this.getContainer) {
  60. this.move();
  61. }
  62. if (this.value) {
  63. this.open();
  64. }
  65. },
  66. activated() {
  67. /* istanbul ignore next */
  68. if (this.value) {
  69. this.open();
  70. }
  71. },
  72. beforeDestroy() {
  73. this.close();
  74. if (this.getContainer && this.$parent && this.$parent.$el) {
  75. this.$parent.$el.appendChild(this.$el);
  76. }
  77. },
  78. deactivated() {
  79. /* istanbul ignore next */
  80. this.close();
  81. },
  82. methods: {
  83. open() {
  84. /* istanbul ignore next */
  85. if (this.$isServer || this.opened) {
  86. return;
  87. }
  88. // cover default zIndex
  89. if (this.zIndex !== undefined) {
  90. context.zIndex = this.zIndex;
  91. }
  92. this.opened = true;
  93. this.renderOverlay();
  94. if (this.lockScroll) {
  95. on(document, 'touchstart', this.touchStart);
  96. on(document, 'touchmove', this.onTouchMove);
  97. if (!context.lockCount) {
  98. document.body.classList.add('van-overflow-hidden');
  99. }
  100. context.lockCount++;
  101. }
  102. },
  103. close() {
  104. if (!this.opened) {
  105. return;
  106. }
  107. if (this.lockScroll) {
  108. context.lockCount--;
  109. off(document, 'touchstart', this.touchStart);
  110. off(document, 'touchmove', this.onTouchMove);
  111. if (!context.lockCount) {
  112. document.body.classList.remove('van-overflow-hidden');
  113. }
  114. }
  115. this.opened = false;
  116. closeOverlay(this);
  117. this.$emit('input', false);
  118. },
  119. move() {
  120. let container;
  121. const { getContainer } = this;
  122. if (getContainer) {
  123. container =
  124. typeof getContainer === 'string'
  125. ? document.querySelector(getContainer)
  126. : getContainer();
  127. } else if (this.$parent) {
  128. container = this.$parent.$el;
  129. }
  130. if (container && container !== this.$el.parentNode) {
  131. container.appendChild(this.$el);
  132. }
  133. if (this.overlay) {
  134. updateOverlay();
  135. }
  136. },
  137. onTouchMove(event) {
  138. this.touchMove(event);
  139. const direction = this.deltaY > 0 ? '10' : '01';
  140. const el = getScrollEventTarget(event.target, this.$el);
  141. const { scrollHeight, offsetHeight, scrollTop } = el;
  142. let status = '11';
  143. /* istanbul ignore next */
  144. if (scrollTop === 0) {
  145. status = offsetHeight >= scrollHeight ? '00' : '01';
  146. } else if (scrollTop + offsetHeight >= scrollHeight) {
  147. status = '10';
  148. }
  149. /* istanbul ignore next */
  150. if (
  151. status !== '11' &&
  152. this.direction === 'vertical' &&
  153. !(parseInt(status, 2) & parseInt(direction, 2))
  154. ) {
  155. preventDefault(event, true);
  156. }
  157. },
  158. renderOverlay() {
  159. if (this.$isServer || !this.value) {
  160. return;
  161. }
  162. if (this.overlay) {
  163. openOverlay(this, {
  164. zIndex: context.zIndex++,
  165. className: this.overlayClass,
  166. customStyle: this.overlayStyle
  167. });
  168. } else {
  169. closeOverlay(this);
  170. }
  171. this.updateZIndex();
  172. },
  173. updateZIndex() {
  174. this.$nextTick(() => {
  175. this.$el.style.zIndex = context.zIndex++;
  176. });
  177. }
  178. }
  179. };