PortalWrapper.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.default = void 0;
  7. exports.getOpenCount = getOpenCount;
  8. var _vue = require("vue");
  9. var _vueTypes = _interopRequireDefault(require("./vue-types"));
  10. var _Portal = _interopRequireDefault(require("./Portal"));
  11. var _canUseDom = _interopRequireDefault(require("./canUseDom"));
  12. var _raf = _interopRequireDefault(require("./raf"));
  13. var _type = require("./type");
  14. var _useScrollLocker = _interopRequireDefault(require("./hooks/useScrollLocker"));
  15. let openCount = 0;
  16. const supportDom = (0, _canUseDom.default)();
  17. /** @private Test usage only */
  18. function getOpenCount() {
  19. return process.env.NODE_ENV === 'test' ? openCount : 0;
  20. }
  21. const getParent = getContainer => {
  22. if (!supportDom) {
  23. return null;
  24. }
  25. if (getContainer) {
  26. if (typeof getContainer === 'string') {
  27. return document.querySelectorAll(getContainer)[0];
  28. }
  29. if (typeof getContainer === 'function') {
  30. return getContainer();
  31. }
  32. if (typeof getContainer === 'object' && getContainer instanceof window.HTMLElement) {
  33. return getContainer;
  34. }
  35. }
  36. return document.body;
  37. };
  38. var _default = exports.default = (0, _vue.defineComponent)({
  39. compatConfig: {
  40. MODE: 3
  41. },
  42. name: 'PortalWrapper',
  43. inheritAttrs: false,
  44. props: {
  45. wrapperClassName: String,
  46. forceRender: {
  47. type: Boolean,
  48. default: undefined
  49. },
  50. getContainer: _vueTypes.default.any,
  51. visible: {
  52. type: Boolean,
  53. default: undefined
  54. },
  55. autoLock: (0, _type.booleanType)(),
  56. didUpdate: Function
  57. },
  58. setup(props, _ref) {
  59. let {
  60. slots
  61. } = _ref;
  62. const container = (0, _vue.shallowRef)();
  63. const componentRef = (0, _vue.shallowRef)();
  64. const rafId = (0, _vue.shallowRef)();
  65. const triggerUpdate = (0, _vue.shallowRef)(1);
  66. const defaultContainer = (0, _canUseDom.default)() && document.createElement('div');
  67. const removeCurrentContainer = () => {
  68. var _a, _b;
  69. // Portal will remove from `parentNode`.
  70. // Let's handle this again to avoid refactor issue.
  71. if (container.value === defaultContainer) {
  72. (_b = (_a = container.value) === null || _a === void 0 ? void 0 : _a.parentNode) === null || _b === void 0 ? void 0 : _b.removeChild(container.value);
  73. }
  74. container.value = null;
  75. };
  76. let parent = null;
  77. const attachToParent = function () {
  78. let force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  79. if (force || container.value && !container.value.parentNode) {
  80. parent = getParent(props.getContainer);
  81. if (parent) {
  82. parent.appendChild(container.value);
  83. return true;
  84. }
  85. return false;
  86. }
  87. return true;
  88. };
  89. const getContainer = () => {
  90. if (!supportDom) {
  91. return null;
  92. }
  93. if (!container.value) {
  94. container.value = defaultContainer;
  95. attachToParent(true);
  96. }
  97. setWrapperClassName();
  98. return container.value;
  99. };
  100. const setWrapperClassName = () => {
  101. const {
  102. wrapperClassName
  103. } = props;
  104. if (container.value && wrapperClassName && wrapperClassName !== container.value.className) {
  105. container.value.className = wrapperClassName;
  106. }
  107. };
  108. (0, _vue.onUpdated)(() => {
  109. setWrapperClassName();
  110. attachToParent();
  111. });
  112. (0, _useScrollLocker.default)((0, _vue.computed)(() => {
  113. return props.autoLock && props.visible && (0, _canUseDom.default)() && (container.value === document.body || container.value === defaultContainer);
  114. }));
  115. (0, _vue.onMounted)(() => {
  116. let init = false;
  117. (0, _vue.watch)([() => props.visible, () => props.getContainer], (_ref2, _ref3) => {
  118. let [visible, getContainer] = _ref2;
  119. let [prevVisible, prevGetContainer] = _ref3;
  120. // Update count
  121. if (supportDom) {
  122. parent = getParent(props.getContainer);
  123. if (parent === document.body) {
  124. if (visible && !prevVisible) {
  125. openCount += 1;
  126. } else if (init) {
  127. openCount -= 1;
  128. }
  129. }
  130. }
  131. if (init) {
  132. // Clean up container if needed
  133. const getContainerIsFunc = typeof getContainer === 'function' && typeof prevGetContainer === 'function';
  134. if (getContainerIsFunc ? getContainer.toString() !== prevGetContainer.toString() : getContainer !== prevGetContainer) {
  135. removeCurrentContainer();
  136. }
  137. }
  138. init = true;
  139. }, {
  140. immediate: true,
  141. flush: 'post'
  142. });
  143. (0, _vue.nextTick)(() => {
  144. if (!attachToParent()) {
  145. rafId.value = (0, _raf.default)(() => {
  146. triggerUpdate.value += 1;
  147. });
  148. }
  149. });
  150. });
  151. (0, _vue.onBeforeUnmount)(() => {
  152. const {
  153. visible
  154. } = props;
  155. if (supportDom && parent === document.body) {
  156. // 离开时不会 render, 导到离开时数值不变,改用 func 。。
  157. openCount = visible && openCount ? openCount - 1 : openCount;
  158. }
  159. removeCurrentContainer();
  160. _raf.default.cancel(rafId.value);
  161. });
  162. return () => {
  163. const {
  164. forceRender,
  165. visible
  166. } = props;
  167. let portal = null;
  168. const childProps = {
  169. getOpenCount: () => openCount,
  170. getContainer
  171. };
  172. if (triggerUpdate.value && (forceRender || visible || componentRef.value)) {
  173. portal = (0, _vue.createVNode)(_Portal.default, {
  174. "getContainer": getContainer,
  175. "ref": componentRef,
  176. "didUpdate": props.didUpdate
  177. }, {
  178. default: () => {
  179. var _a;
  180. return (_a = slots.default) === null || _a === void 0 ? void 0 : _a.call(slots, childProps);
  181. }
  182. });
  183. }
  184. return portal;
  185. };
  186. }
  187. });