@rc-component/portal 1.0.0-1 → 1.0.0-10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/es/Portal.d.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import * as React from 'react';
2
2
  export declare type ContainerType = Element | DocumentFragment;
3
- export declare type GetContainer = string | ContainerType | (() => ContainerType);
3
+ export declare type GetContainer = string | ContainerType | (() => ContainerType) | false;
4
4
  export interface PortalProps {
5
5
  /** Customize container element. Default will create a div in document.body when `open` */
6
6
  getContainer?: GetContainer;
7
7
  children?: React.ReactNode;
8
8
  /** Show the portal children */
9
9
  open?: boolean;
10
+ /** Remove `children` when `open` is `false`. Set `false` will not handle remove process */
11
+ autoDestroy?: boolean;
10
12
  /** Lock screen scroll when open */
11
13
  autoLock?: boolean;
14
+ /** @private debug name. Do not use in prod */
15
+ debug?: string;
12
16
  }
13
17
  export default function Portal(props: PortalProps): JSX.Element;
package/es/Portal.js CHANGED
@@ -1,4 +1,4 @@
1
- import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
1
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
2
2
  import * as React from 'react';
3
3
  import { createPortal } from 'react-dom';
4
4
  import canUseDom from "rc-util/es/Dom/canUseDom";
@@ -8,7 +8,11 @@ import useScrollLocker from "./useScrollLocker";
8
8
  import { inlineMock } from "./mock";
9
9
 
10
10
  var getPortalContainer = function getPortalContainer(getContainer) {
11
- if (!canUseDom()) {
11
+ if (getContainer === false) {
12
+ return false;
13
+ }
14
+
15
+ if (!canUseDom() || !getContainer) {
12
16
  return null;
13
17
  }
14
18
 
@@ -27,35 +31,54 @@ export default function Portal(props) {
27
31
  var open = props.open,
28
32
  autoLock = props.autoLock,
29
33
  getContainer = props.getContainer,
34
+ debug = props.debug,
35
+ _props$autoDestroy = props.autoDestroy,
36
+ autoDestroy = _props$autoDestroy === void 0 ? true : _props$autoDestroy,
30
37
  children = props.children;
31
38
 
32
39
  var _React$useState = React.useState(open),
33
40
  _React$useState2 = _slicedToArray(_React$useState, 2),
34
41
  mergedRender = _React$useState2[0],
35
- setMergedRender = _React$useState2[1];
42
+ setMergedRender = _React$useState2[1]; // ====================== Should Render ======================
43
+
44
+
45
+ React.useEffect(function () {
46
+ if (autoDestroy || open) {
47
+ setMergedRender(open);
48
+ }
49
+ }, [open, autoDestroy]); // ======================== Container ========================
36
50
 
37
- useScrollLocker(autoLock && open); // ====================== Should Render ======================
51
+ var _React$useState3 = React.useState(function () {
52
+ return getPortalContainer(getContainer);
53
+ }),
54
+ _React$useState4 = _slicedToArray(_React$useState3, 2),
55
+ innerContainer = _React$useState4[0],
56
+ setInnerContainer = _React$useState4[1];
38
57
 
39
58
  React.useEffect(function () {
40
- setMergedRender(open);
41
- }, [open]); // ======================== Container ========================
59
+ var customizeContainer = getPortalContainer(getContainer); // Tell component that we check this in effect which is safe to be `null`
42
60
 
43
- var customizeContainer = getPortalContainer(getContainer);
61
+ setInnerContainer(customizeContainer !== null && customizeContainer !== void 0 ? customizeContainer : null);
62
+ });
44
63
 
45
- var _useDom = useDom(mergedRender && !customizeContainer),
64
+ var _useDom = useDom(mergedRender && !innerContainer, debug),
46
65
  _useDom2 = _slicedToArray(_useDom, 2),
47
66
  defaultContainer = _useDom2[0],
48
67
  queueCreate = _useDom2[1];
49
68
 
50
- var mergedContainer = customizeContainer || defaultContainer; // ========================= Render ==========================
69
+ var mergedContainer = innerContainer !== null && innerContainer !== void 0 ? innerContainer : defaultContainer; // ========================= Locker ==========================
70
+
71
+ useScrollLocker(autoLock && open && canUseDom() && (mergedContainer === defaultContainer || mergedContainer === document.body)); // ========================= Render ==========================
51
72
  // Do not render when nothing need render
73
+ // When innerContainer is `undefined`, it may not ready since user use ref in the same render
52
74
 
53
- if (!mergedRender || !canUseDom()) {
75
+ if (!mergedRender || !canUseDom() || innerContainer === undefined) {
54
76
  return null;
55
- }
77
+ } // Render inline
78
+
56
79
 
57
- console.log(inlineMock());
80
+ var renderInline = mergedContainer === false || inlineMock();
58
81
  return /*#__PURE__*/React.createElement(OrderContext.Provider, {
59
82
  value: queueCreate
60
- }, inlineMock() ? children : /*#__PURE__*/createPortal(children, mergedContainer));
83
+ }, renderInline ? children : /*#__PURE__*/createPortal(children, mergedContainer));
61
84
  }
package/es/useDom.d.ts CHANGED
@@ -3,4 +3,4 @@ import type { QueueCreate } from './Context';
3
3
  * Will add `div` to document. Nest call will keep order
4
4
  * @param render Render DOM in document
5
5
  */
6
- export default function useDom(render: boolean): [HTMLDivElement, QueueCreate];
6
+ export default function useDom(render: boolean, debug?: string): [HTMLDivElement, QueueCreate];
package/es/useDom.js CHANGED
@@ -1,7 +1,8 @@
1
- import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
- import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
1
+ import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
2
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
3
3
  import * as React from 'react';
4
4
  import useLayoutEffect from "rc-util/es/hooks/useLayoutEffect";
5
+ import canUseDom from "rc-util/es/Dom/canUseDom";
5
6
  import OrderContext from "./Context";
6
7
  var EMPTY_LIST = [];
7
8
  /**
@@ -9,9 +10,18 @@ var EMPTY_LIST = [];
9
10
  * @param render Render DOM in document
10
11
  */
11
12
 
12
- export default function useDom(render) {
13
+ export default function useDom(render, debug) {
13
14
  var _React$useState = React.useState(function () {
15
+ if (!canUseDom()) {
16
+ return null;
17
+ }
18
+
14
19
  var defaultEle = document.createElement('div');
20
+
21
+ if (process.env.NODE_ENV !== 'production' && debug) {
22
+ defaultEle.setAttribute('data-debug', debug);
23
+ }
24
+
15
25
  return defaultEle;
16
26
  }),
17
27
  _React$useState2 = _slicedToArray(_React$useState, 1),
@@ -27,7 +37,8 @@ export default function useDom(render) {
27
37
 
28
38
  var mergedQueueCreate = queueCreate || function (appendFn) {
29
39
  setQueue(function (origin) {
30
- return [appendFn].concat(_toConsumableArray(origin));
40
+ var newQueue = [appendFn].concat(_toConsumableArray(origin));
41
+ return newQueue;
31
42
  });
32
43
  }; // =========================== DOM ===========================
33
44
 
@@ -1,43 +1,32 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
1
2
  import * as React from 'react';
2
3
  import { updateCSS, removeCSS } from "rc-util/es/Dom/dynamicCSS";
4
+ import useLayoutEffect from "rc-util/es/hooks/useLayoutEffect";
3
5
  import getScrollBarSize from "rc-util/es/getScrollBarSize";
4
- var lockCount = 0;
5
- var locked = false;
6
+ import { isBodyOverflowing } from "./util";
6
7
  var UNIQUE_ID = "rc-util-locker-".concat(Date.now());
8
+ var uuid = 0;
9
+ export default function useScrollLocker(lock) {
10
+ var mergedLock = !!lock;
7
11
 
8
- function syncLocker() {
9
- var nextLocked = lockCount > 0;
10
-
11
- if (locked !== nextLocked) {
12
- locked = nextLocked;
12
+ var _React$useState = React.useState(function () {
13
+ uuid += 1;
14
+ return "".concat(UNIQUE_ID, "_").concat(uuid);
15
+ }),
16
+ _React$useState2 = _slicedToArray(_React$useState, 1),
17
+ id = _React$useState2[0];
13
18
 
14
- if (locked) {
19
+ useLayoutEffect(function () {
20
+ if (mergedLock) {
15
21
  var scrollbarSize = getScrollBarSize();
16
- updateCSS("\nhtml body {\n overflow-y: hidden;\n width: calc(100% - ".concat(scrollbarSize, "px);\n}"), UNIQUE_ID);
22
+ var isOverflow = isBodyOverflowing();
23
+ updateCSS("\nhtml body {\n overflow-y: hidden;\n ".concat(isOverflow ? "width: calc(100% - ".concat(scrollbarSize, "px);") : '', "\n}"), id);
17
24
  } else {
18
- removeCSS(UNIQUE_ID);
25
+ removeCSS(id);
19
26
  }
20
- }
21
- }
22
27
 
23
- export default function useScrollLocker(lock) {
24
- React.useLayoutEffect(function () {
25
- if (lock) {
26
- lockCount += 1;
27
- syncLocker();
28
- } else {
29
- lockCount -= 1;
30
- syncLocker();
31
- }
32
- }, [lock]);
33
- var lockRef = React.useRef(lock);
34
- lockRef.current = lock;
35
- React.useLayoutEffect(function () {
36
28
  return function () {
37
- if (lockRef.current) {
38
- lockCount -= 1;
39
- syncLocker();
40
- }
29
+ removeCSS(id);
41
30
  };
42
- }, []);
31
+ }, [mergedLock, id]);
43
32
  }
package/es/util.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Test usage export. Do not use in your production
3
+ */
4
+ export declare function isBodyOverflowing(): boolean;
package/es/util.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Test usage export. Do not use in your production
3
+ */
4
+ export function isBodyOverflowing() {
5
+ return document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight) && window.innerWidth > document.body.offsetWidth;
6
+ }
package/lib/Portal.d.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  import * as React from 'react';
2
2
  export declare type ContainerType = Element | DocumentFragment;
3
- export declare type GetContainer = string | ContainerType | (() => ContainerType);
3
+ export declare type GetContainer = string | ContainerType | (() => ContainerType) | false;
4
4
  export interface PortalProps {
5
5
  /** Customize container element. Default will create a div in document.body when `open` */
6
6
  getContainer?: GetContainer;
7
7
  children?: React.ReactNode;
8
8
  /** Show the portal children */
9
9
  open?: boolean;
10
+ /** Remove `children` when `open` is `false`. Set `false` will not handle remove process */
11
+ autoDestroy?: boolean;
10
12
  /** Lock screen scroll when open */
11
13
  autoLock?: boolean;
14
+ /** @private debug name. Do not use in prod */
15
+ debug?: string;
12
16
  }
13
17
  export default function Portal(props: PortalProps): JSX.Element;
package/lib/Portal.js CHANGED
@@ -26,7 +26,11 @@ var _useScrollLocker = _interopRequireDefault(require("./useScrollLocker"));
26
26
  var _mock = require("./mock");
27
27
 
28
28
  var getPortalContainer = function getPortalContainer(getContainer) {
29
- if (!(0, _canUseDom.default)()) {
29
+ if (getContainer === false) {
30
+ return false;
31
+ }
32
+
33
+ if (!(0, _canUseDom.default)() || !getContainer) {
30
34
  return null;
31
35
  }
32
36
 
@@ -45,35 +49,54 @@ function Portal(props) {
45
49
  var open = props.open,
46
50
  autoLock = props.autoLock,
47
51
  getContainer = props.getContainer,
52
+ debug = props.debug,
53
+ _props$autoDestroy = props.autoDestroy,
54
+ autoDestroy = _props$autoDestroy === void 0 ? true : _props$autoDestroy,
48
55
  children = props.children;
49
56
 
50
57
  var _React$useState = React.useState(open),
51
58
  _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
52
59
  mergedRender = _React$useState2[0],
53
- setMergedRender = _React$useState2[1];
60
+ setMergedRender = _React$useState2[1]; // ====================== Should Render ======================
54
61
 
55
- (0, _useScrollLocker.default)(autoLock && open); // ====================== Should Render ======================
56
62
 
57
63
  React.useEffect(function () {
58
- setMergedRender(open);
59
- }, [open]); // ======================== Container ========================
64
+ if (autoDestroy || open) {
65
+ setMergedRender(open);
66
+ }
67
+ }, [open, autoDestroy]); // ======================== Container ========================
68
+
69
+ var _React$useState3 = React.useState(function () {
70
+ return getPortalContainer(getContainer);
71
+ }),
72
+ _React$useState4 = (0, _slicedToArray2.default)(_React$useState3, 2),
73
+ innerContainer = _React$useState4[0],
74
+ setInnerContainer = _React$useState4[1];
75
+
76
+ React.useEffect(function () {
77
+ var customizeContainer = getPortalContainer(getContainer); // Tell component that we check this in effect which is safe to be `null`
60
78
 
61
- var customizeContainer = getPortalContainer(getContainer);
79
+ setInnerContainer(customizeContainer !== null && customizeContainer !== void 0 ? customizeContainer : null);
80
+ });
62
81
 
63
- var _useDom = (0, _useDom3.default)(mergedRender && !customizeContainer),
82
+ var _useDom = (0, _useDom3.default)(mergedRender && !innerContainer, debug),
64
83
  _useDom2 = (0, _slicedToArray2.default)(_useDom, 2),
65
84
  defaultContainer = _useDom2[0],
66
85
  queueCreate = _useDom2[1];
67
86
 
68
- var mergedContainer = customizeContainer || defaultContainer; // ========================= Render ==========================
87
+ var mergedContainer = innerContainer !== null && innerContainer !== void 0 ? innerContainer : defaultContainer; // ========================= Locker ==========================
88
+
89
+ (0, _useScrollLocker.default)(autoLock && open && (0, _canUseDom.default)() && (mergedContainer === defaultContainer || mergedContainer === document.body)); // ========================= Render ==========================
69
90
  // Do not render when nothing need render
91
+ // When innerContainer is `undefined`, it may not ready since user use ref in the same render
70
92
 
71
- if (!mergedRender || !(0, _canUseDom.default)()) {
93
+ if (!mergedRender || !(0, _canUseDom.default)() || innerContainer === undefined) {
72
94
  return null;
73
- }
95
+ } // Render inline
96
+
74
97
 
75
- console.log((0, _mock.inlineMock)());
98
+ var renderInline = mergedContainer === false || (0, _mock.inlineMock)();
76
99
  return /*#__PURE__*/React.createElement(_Context.default.Provider, {
77
100
  value: queueCreate
78
- }, (0, _mock.inlineMock)() ? children : /*#__PURE__*/(0, _reactDom.createPortal)(children, mergedContainer));
101
+ }, renderInline ? children : /*#__PURE__*/(0, _reactDom.createPortal)(children, mergedContainer));
79
102
  }
package/lib/useDom.d.ts CHANGED
@@ -3,4 +3,4 @@ import type { QueueCreate } from './Context';
3
3
  * Will add `div` to document. Nest call will keep order
4
4
  * @param render Render DOM in document
5
5
  */
6
- export default function useDom(render: boolean): [HTMLDivElement, QueueCreate];
6
+ export default function useDom(render: boolean, debug?: string): [HTMLDivElement, QueueCreate];
package/lib/useDom.js CHANGED
@@ -17,6 +17,8 @@ var React = _interopRequireWildcard(require("react"));
17
17
 
18
18
  var _useLayoutEffect = _interopRequireDefault(require("rc-util/lib/hooks/useLayoutEffect"));
19
19
 
20
+ var _canUseDom = _interopRequireDefault(require("rc-util/lib/Dom/canUseDom"));
21
+
20
22
  var _Context = _interopRequireDefault(require("./Context"));
21
23
 
22
24
  var EMPTY_LIST = [];
@@ -25,9 +27,18 @@ var EMPTY_LIST = [];
25
27
  * @param render Render DOM in document
26
28
  */
27
29
 
28
- function useDom(render) {
30
+ function useDom(render, debug) {
29
31
  var _React$useState = React.useState(function () {
32
+ if (!(0, _canUseDom.default)()) {
33
+ return null;
34
+ }
35
+
30
36
  var defaultEle = document.createElement('div');
37
+
38
+ if (process.env.NODE_ENV !== 'production' && debug) {
39
+ defaultEle.setAttribute('data-debug', debug);
40
+ }
41
+
31
42
  return defaultEle;
32
43
  }),
33
44
  _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 1),
@@ -43,7 +54,8 @@ function useDom(render) {
43
54
 
44
55
  var mergedQueueCreate = queueCreate || function (appendFn) {
45
56
  setQueue(function (origin) {
46
- return [appendFn].concat((0, _toConsumableArray2.default)(origin));
57
+ var newQueue = [appendFn].concat((0, _toConsumableArray2.default)(origin));
58
+ return newQueue;
47
59
  });
48
60
  }; // =========================== DOM ===========================
49
61
 
@@ -1,57 +1,50 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
-
5
3
  var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
6
4
 
5
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
6
+
7
7
  Object.defineProperty(exports, "__esModule", {
8
8
  value: true
9
9
  });
10
10
  exports.default = useScrollLocker;
11
11
 
12
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
+
12
14
  var React = _interopRequireWildcard(require("react"));
13
15
 
14
16
  var _dynamicCSS = require("rc-util/lib/Dom/dynamicCSS");
15
17
 
18
+ var _useLayoutEffect = _interopRequireDefault(require("rc-util/lib/hooks/useLayoutEffect"));
19
+
16
20
  var _getScrollBarSize = _interopRequireDefault(require("rc-util/lib/getScrollBarSize"));
17
21
 
18
- var lockCount = 0;
19
- var locked = false;
22
+ var _util = require("./util");
23
+
20
24
  var UNIQUE_ID = "rc-util-locker-".concat(Date.now());
25
+ var uuid = 0;
21
26
 
22
- function syncLocker() {
23
- var nextLocked = lockCount > 0;
27
+ function useScrollLocker(lock) {
28
+ var mergedLock = !!lock;
24
29
 
25
- if (locked !== nextLocked) {
26
- locked = nextLocked;
30
+ var _React$useState = React.useState(function () {
31
+ uuid += 1;
32
+ return "".concat(UNIQUE_ID, "_").concat(uuid);
33
+ }),
34
+ _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 1),
35
+ id = _React$useState2[0];
27
36
 
28
- if (locked) {
37
+ (0, _useLayoutEffect.default)(function () {
38
+ if (mergedLock) {
29
39
  var scrollbarSize = (0, _getScrollBarSize.default)();
30
- (0, _dynamicCSS.updateCSS)("\nhtml body {\n overflow-y: hidden;\n width: calc(100% - ".concat(scrollbarSize, "px);\n}"), UNIQUE_ID);
40
+ var isOverflow = (0, _util.isBodyOverflowing)();
41
+ (0, _dynamicCSS.updateCSS)("\nhtml body {\n overflow-y: hidden;\n ".concat(isOverflow ? "width: calc(100% - ".concat(scrollbarSize, "px);") : '', "\n}"), id);
31
42
  } else {
32
- (0, _dynamicCSS.removeCSS)(UNIQUE_ID);
43
+ (0, _dynamicCSS.removeCSS)(id);
33
44
  }
34
- }
35
- }
36
45
 
37
- function useScrollLocker(lock) {
38
- React.useLayoutEffect(function () {
39
- if (lock) {
40
- lockCount += 1;
41
- syncLocker();
42
- } else {
43
- lockCount -= 1;
44
- syncLocker();
45
- }
46
- }, [lock]);
47
- var lockRef = React.useRef(lock);
48
- lockRef.current = lock;
49
- React.useLayoutEffect(function () {
50
46
  return function () {
51
- if (lockRef.current) {
52
- lockCount -= 1;
53
- syncLocker();
54
- }
47
+ (0, _dynamicCSS.removeCSS)(id);
55
48
  };
56
- }, []);
49
+ }, [mergedLock, id]);
57
50
  }
package/lib/util.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Test usage export. Do not use in your production
3
+ */
4
+ export declare function isBodyOverflowing(): boolean;
package/lib/util.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isBodyOverflowing = isBodyOverflowing;
7
+
8
+ /**
9
+ * Test usage export. Do not use in your production
10
+ */
11
+ function isBodyOverflowing() {
12
+ return document.body.scrollHeight > (window.innerHeight || document.documentElement.clientHeight) && window.innerWidth > document.body.offsetWidth;
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rc-component/portal",
3
- "version": "1.0.0-1",
3
+ "version": "1.0.0-10",
4
4
  "description": "React Portal Component",
5
5
  "keywords": [
6
6
  "react",
@@ -36,12 +36,15 @@
36
36
  "prepublishOnly": "npm run compile && np --no-cleanup --yolo --no-publish",
37
37
  "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"",
38
38
  "start": "dumi dev",
39
- "test": "umi-test",
40
- "test:coverage": "umi-test --coverage",
39
+ "test": "npm run test:client && npm run test:server",
40
+ "test:client": "umi-test --testPathIgnorePatterns=ssr.test.tsx --testPathIgnorePatterns=ssr.test.tsx",
41
+ "test:coverage": "npm run test:client --coverage",
42
+ "test:server": "umi-test --env=node tests/ssr.test.tsx",
41
43
  "watch": "father dev"
42
44
  },
43
45
  "dependencies": {
44
46
  "@babel/runtime": "^7.18.0",
47
+ "classnames": "^2.3.2",
45
48
  "rc-util": "^5.8.0"
46
49
  },
47
50
  "devDependencies": {