PortalWrapper.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
  2. import _createClass from "@babel/runtime/helpers/esm/createClass";
  3. import _assertThisInitialized from "@babel/runtime/helpers/esm/assertThisInitialized";
  4. import _inherits from "@babel/runtime/helpers/esm/inherits";
  5. import _createSuper from "@babel/runtime/helpers/esm/createSuper";
  6. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  7. import _typeof from "@babel/runtime/helpers/esm/typeof";
  8. /* eslint-disable no-underscore-dangle,react/require-default-props */
  9. import * as React from 'react';
  10. import raf from "./raf";
  11. import Portal from "./Portal";
  12. import canUseDom from "./Dom/canUseDom";
  13. import switchScrollingEffect from "./switchScrollingEffect";
  14. import setStyle from "./setStyle";
  15. import ScrollLocker from "./Dom/scrollLocker";
  16. var openCount = 0;
  17. var supportDom = canUseDom();
  18. /** @private Test usage only */
  19. export function getOpenCount() {
  20. return process.env.NODE_ENV === 'test' ? openCount : 0;
  21. }
  22. // https://github.com/ant-design/ant-design/issues/19340
  23. // https://github.com/ant-design/ant-design/issues/19332
  24. var cacheOverflow = {};
  25. var getParent = function getParent(getContainer) {
  26. if (!supportDom) {
  27. return null;
  28. }
  29. if (getContainer) {
  30. if (typeof getContainer === 'string') {
  31. return document.querySelectorAll(getContainer)[0];
  32. }
  33. if (typeof getContainer === 'function') {
  34. return getContainer();
  35. }
  36. if (_typeof(getContainer) === 'object' && getContainer instanceof window.HTMLElement) {
  37. return getContainer;
  38. }
  39. }
  40. return document.body;
  41. };
  42. var PortalWrapper = /*#__PURE__*/function (_React$Component) {
  43. _inherits(PortalWrapper, _React$Component);
  44. var _super = _createSuper(PortalWrapper);
  45. function PortalWrapper(props) {
  46. var _this;
  47. _classCallCheck(this, PortalWrapper);
  48. _this = _super.call(this, props);
  49. _defineProperty(_assertThisInitialized(_this), "container", void 0);
  50. _defineProperty(_assertThisInitialized(_this), "componentRef", /*#__PURE__*/React.createRef());
  51. _defineProperty(_assertThisInitialized(_this), "rafId", void 0);
  52. _defineProperty(_assertThisInitialized(_this), "scrollLocker", void 0);
  53. _defineProperty(_assertThisInitialized(_this), "renderComponent", void 0);
  54. _defineProperty(_assertThisInitialized(_this), "updateScrollLocker", function (prevProps) {
  55. var _ref = prevProps || {},
  56. prevVisible = _ref.visible;
  57. var _this$props = _this.props,
  58. getContainer = _this$props.getContainer,
  59. visible = _this$props.visible;
  60. if (visible && visible !== prevVisible && supportDom && getParent(getContainer) !== _this.scrollLocker.getContainer()) {
  61. _this.scrollLocker.reLock({
  62. container: getParent(getContainer)
  63. });
  64. }
  65. });
  66. _defineProperty(_assertThisInitialized(_this), "updateOpenCount", function (prevProps) {
  67. var _ref2 = prevProps || {},
  68. prevVisible = _ref2.visible,
  69. prevGetContainer = _ref2.getContainer;
  70. var _this$props2 = _this.props,
  71. visible = _this$props2.visible,
  72. getContainer = _this$props2.getContainer;
  73. // Update count
  74. if (visible !== prevVisible && supportDom && getParent(getContainer) === document.body) {
  75. if (visible && !prevVisible) {
  76. openCount += 1;
  77. } else if (prevProps) {
  78. openCount -= 1;
  79. }
  80. }
  81. // Clean up container if needed
  82. var getContainerIsFunc = typeof getContainer === 'function' && typeof prevGetContainer === 'function';
  83. if (getContainerIsFunc ? getContainer.toString() !== prevGetContainer.toString() : getContainer !== prevGetContainer) {
  84. _this.removeCurrentContainer();
  85. }
  86. });
  87. _defineProperty(_assertThisInitialized(_this), "attachToParent", function () {
  88. var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  89. if (force || _this.container && !_this.container.parentNode) {
  90. var parent = getParent(_this.props.getContainer);
  91. if (parent) {
  92. parent.appendChild(_this.container);
  93. return true;
  94. }
  95. return false;
  96. }
  97. return true;
  98. });
  99. _defineProperty(_assertThisInitialized(_this), "getContainer", function () {
  100. if (!supportDom) {
  101. return null;
  102. }
  103. if (!_this.container) {
  104. _this.container = document.createElement('div');
  105. _this.attachToParent(true);
  106. }
  107. _this.setWrapperClassName();
  108. return _this.container;
  109. });
  110. _defineProperty(_assertThisInitialized(_this), "setWrapperClassName", function () {
  111. var wrapperClassName = _this.props.wrapperClassName;
  112. if (_this.container && wrapperClassName && wrapperClassName !== _this.container.className) {
  113. _this.container.className = wrapperClassName;
  114. }
  115. });
  116. _defineProperty(_assertThisInitialized(_this), "removeCurrentContainer", function () {
  117. var _this$container;
  118. // Portal will remove from `parentNode`.
  119. // Let's handle this again to avoid refactor issue.
  120. (_this$container = _this.container) === null || _this$container === void 0 || (_this$container = _this$container.parentNode) === null || _this$container === void 0 || _this$container.removeChild(_this.container);
  121. });
  122. /**
  123. * Enhance ./switchScrollingEffect
  124. * 1. Simulate document body scroll bar with
  125. * 2. Record body has overflow style and recover when all of PortalWrapper invisible
  126. * 3. Disable body scroll when PortalWrapper has open
  127. *
  128. * @memberof PortalWrapper
  129. */
  130. _defineProperty(_assertThisInitialized(_this), "switchScrollingEffect", function () {
  131. if (openCount === 1 && !Object.keys(cacheOverflow).length) {
  132. switchScrollingEffect();
  133. // Must be set after switchScrollingEffect
  134. cacheOverflow = setStyle({
  135. overflow: 'hidden',
  136. overflowX: 'hidden',
  137. overflowY: 'hidden'
  138. });
  139. } else if (!openCount) {
  140. setStyle(cacheOverflow);
  141. cacheOverflow = {};
  142. switchScrollingEffect(true);
  143. }
  144. });
  145. _this.scrollLocker = new ScrollLocker({
  146. container: getParent(props.getContainer)
  147. });
  148. return _this;
  149. }
  150. _createClass(PortalWrapper, [{
  151. key: "componentDidMount",
  152. value: function componentDidMount() {
  153. var _this2 = this;
  154. this.updateOpenCount();
  155. if (!this.attachToParent()) {
  156. this.rafId = raf(function () {
  157. _this2.forceUpdate();
  158. });
  159. }
  160. }
  161. }, {
  162. key: "componentDidUpdate",
  163. value: function componentDidUpdate(prevProps) {
  164. this.updateOpenCount(prevProps);
  165. this.updateScrollLocker(prevProps);
  166. this.setWrapperClassName();
  167. this.attachToParent();
  168. }
  169. }, {
  170. key: "componentWillUnmount",
  171. value: function componentWillUnmount() {
  172. var _this$props3 = this.props,
  173. visible = _this$props3.visible,
  174. getContainer = _this$props3.getContainer;
  175. if (supportDom && getParent(getContainer) === document.body) {
  176. // 离开时不会 render, 导到离开时数值不变,改用 func 。。
  177. openCount = visible && openCount ? openCount - 1 : openCount;
  178. }
  179. this.removeCurrentContainer();
  180. raf.cancel(this.rafId);
  181. }
  182. }, {
  183. key: "render",
  184. value: function render() {
  185. var _this$props4 = this.props,
  186. children = _this$props4.children,
  187. forceRender = _this$props4.forceRender,
  188. visible = _this$props4.visible;
  189. var portal = null;
  190. var childProps = {
  191. getOpenCount: function getOpenCount() {
  192. return openCount;
  193. },
  194. getContainer: this.getContainer,
  195. switchScrollingEffect: this.switchScrollingEffect,
  196. scrollLocker: this.scrollLocker
  197. };
  198. if (forceRender || visible || this.componentRef.current) {
  199. portal = /*#__PURE__*/React.createElement(Portal, {
  200. getContainer: this.getContainer,
  201. ref: this.componentRef
  202. }, children(childProps));
  203. }
  204. return portal;
  205. }
  206. }]);
  207. return PortalWrapper;
  208. }(React.Component);
  209. export default PortalWrapper;