@fluentui/react-portal 9.4.2 → 9.4.4

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/CHANGELOG.md CHANGED
@@ -1,12 +1,33 @@
1
1
  # Change Log - @fluentui/react-portal
2
2
 
3
- This log was last generated on Mon, 20 Nov 2023 09:51:22 GMT and should not be manually modified.
3
+ This log was last generated on Thu, 14 Dec 2023 09:51:35 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## [9.4.4](https://github.com/microsoft/fluentui/tree/@fluentui/react-portal_v9.4.4)
8
+
9
+ Thu, 14 Dec 2023 09:51:35 GMT
10
+ [Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-portal_v9.4.3..@fluentui/react-portal_v9.4.4)
11
+
12
+ ### Patches
13
+
14
+ - fix: Virtual parent cannot be the DOM child of mount node ([PR #29990](https://github.com/microsoft/fluentui/pull/29990) by lingfangao@hotmail.com)
15
+ - Bump @fluentui/react-shared-contexts to v9.13.1 ([commit](https://github.com/microsoft/fluentui/commit/80a1b02be2fbbdde916ac87fbf760e442a2295c4) by beachball)
16
+ - Bump @fluentui/react-tabster to v9.15.1 ([commit](https://github.com/microsoft/fluentui/commit/80a1b02be2fbbdde916ac87fbf760e442a2295c4) by beachball)
17
+ - Bump @fluentui/react-utilities to v9.15.3 ([commit](https://github.com/microsoft/fluentui/commit/80a1b02be2fbbdde916ac87fbf760e442a2295c4) by beachball)
18
+
19
+ ## [9.4.3](https://github.com/microsoft/fluentui/tree/@fluentui/react-portal_v9.4.3)
20
+
21
+ Thu, 30 Nov 2023 13:42:06 GMT
22
+ [Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-portal_v9.4.2..@fluentui/react-portal_v9.4.3)
23
+
24
+ ### Patches
25
+
26
+ - Bump @fluentui/react-tabster to v9.15.0 ([PR #29929](https://github.com/microsoft/fluentui/pull/29929) by beachball)
27
+
7
28
  ## [9.4.2](https://github.com/microsoft/fluentui/tree/@fluentui/react-portal_v9.4.2)
8
29
 
9
- Mon, 20 Nov 2023 09:51:22 GMT
30
+ Mon, 20 Nov 2023 09:55:10 GMT
10
31
  [Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-portal_v9.4.1..@fluentui/react-portal_v9.4.2)
11
32
 
12
33
  ### Patches
package/dist/index.d.ts CHANGED
@@ -41,6 +41,11 @@ export declare const renderPortal_unstable: (state: PortalState) => React_2.Reac
41
41
 
42
42
  export { setVirtualParent }
43
43
 
44
+ /**
45
+ * The function that normalizes the `mountNode` prop into an object with element and className props.
46
+ *
47
+ * @param mountNode - an HTML element or an object with props
48
+ */
44
49
  export declare function toMountNodeProps(mountNode: PortalProps['mountNode']): {
45
50
  element?: HTMLElement | null;
46
51
  className?: string;
@@ -1,22 +1,7 @@
1
- import { isHTMLElement, setVirtualParent } from '@fluentui/react-utilities';
1
+ import { setVirtualParent } from '@fluentui/react-utilities';
2
2
  import * as React from 'react';
3
+ import { toMountNodeProps } from '../../utils/toMountNodeProps';
3
4
  import { usePortalMountNode } from './usePortalMountNode';
4
- export function toMountNodeProps(mountNode) {
5
- if (isHTMLElement(mountNode)) {
6
- return {
7
- element: mountNode
8
- };
9
- }
10
- if (typeof mountNode === 'object') {
11
- if (mountNode === null) {
12
- return {
13
- element: null
14
- };
15
- }
16
- return mountNode;
17
- }
18
- return {};
19
- }
20
5
  /**
21
6
  * Create the state required to render Portal.
22
7
  *
@@ -30,23 +15,66 @@ export function toMountNodeProps(mountNode) {
30
15
  disabled: !!element,
31
16
  className
32
17
  });
18
+ const mountNode = element !== null && element !== void 0 ? element : fallbackElement;
33
19
  const state = {
34
20
  children: props.children,
35
- mountNode: element !== null && element !== void 0 ? element : fallbackElement,
21
+ mountNode,
36
22
  virtualParentRootRef
37
23
  };
38
24
  React.useEffect(()=>{
39
- if (state.virtualParentRootRef.current && state.mountNode) {
40
- setVirtualParent(state.mountNode, state.virtualParentRootRef.current);
25
+ if (!mountNode) {
26
+ return;
27
+ }
28
+ const virtualParent = virtualParentRootRef.current;
29
+ // By default, we create a mount node for portal on `document.body` (see usePortalMountNode()) and have following structure:
30
+ //
31
+ // <body>
32
+ // <!-- ⚛️ application root -->
33
+ // <div id="root">
34
+ // <!-- ⬇️ portal node rendered in a tree to anchor (virtual parent node) -->
35
+ // <span aria-hidden="true"></span>
36
+ // </div>
37
+ // <div id="portal-mount-node">
38
+ // <!-- 🧩portal content -->
39
+ // </div>
40
+ // </body>
41
+ //
42
+ // To make sure that `.elementContains()` works correctly, we link a virtual parent to a portal node (a virtual parent node becomes a parent of mount node):
43
+ // virtual.contains(mountNode) === false
44
+ // (while we need ⬇️⬇️⬇️)
45
+ // elementsContains(virtualParent, mountNode) === true
46
+ // elementsContains(mountNode, virtualParent) === false
47
+ //
48
+ // For more details, check docs for virtual parent utils.
49
+ //
50
+ // However, if a user provides a custom mount node (via `props`) the structure could be different:
51
+ //
52
+ // <body>
53
+ // <!-- application root -->
54
+ // <div id="root">
55
+ // <div id="portal-mount-node">
56
+ // <!-- 🧩portal content -->
57
+ //
58
+ // <span aria-hidden="true"></span>
59
+ // </div>
60
+ // </div>
61
+ // </body>
62
+ //
63
+ // A mount node in this case contains portal's content and a virtual parent node. In this case nodes linking is redundant and the check below avoids it.
64
+ //
65
+ // Otherwise, there is a circular reference - both elements are parents of each other:
66
+ // elementsContains(mountNode, virtualParent) === true
67
+ // elementsContains(virtualParent, mountNode) === true
68
+ const isVirtualParentInsideChild = mountNode.contains(virtualParent);
69
+ if (virtualParent && !isVirtualParentInsideChild) {
70
+ setVirtualParent(mountNode, virtualParent);
71
+ return ()=>{
72
+ setVirtualParent(mountNode, undefined);
73
+ };
41
74
  }
42
- return ()=>{
43
- if (state.mountNode) {
44
- setVirtualParent(state.mountNode, undefined);
45
- }
46
- };
47
75
  }, [
48
- state.virtualParentRootRef,
49
- state.mountNode
76
+ virtualParentRootRef,
77
+ mountNode
50
78
  ]);
51
79
  return state;
52
80
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["usePortal.ts"],"sourcesContent":["import { isHTMLElement, setVirtualParent } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { usePortalMountNode } from './usePortalMountNode';\nimport type { PortalProps, PortalState } from './Portal.types';\n\nexport function toMountNodeProps(mountNode: PortalProps['mountNode']): {\n element?: HTMLElement | null;\n className?: string;\n} {\n if (isHTMLElement(mountNode)) {\n return { element: mountNode };\n }\n\n if (typeof mountNode === 'object') {\n if (mountNode === null) {\n return { element: null };\n }\n\n return mountNode;\n }\n\n return {};\n}\n\n/**\n * Create the state required to render Portal.\n *\n * The returned state can be modified with hooks such as usePortalStyles, before being passed to renderPortal_unstable.\n *\n * @param props - props from this instance of Portal\n */\nexport const usePortal_unstable = (props: PortalProps): PortalState => {\n const { element, className } = toMountNodeProps(props.mountNode);\n\n const virtualParentRootRef = React.useRef<HTMLSpanElement>(null);\n const fallbackElement = usePortalMountNode({ disabled: !!element, className });\n\n const state: PortalState = {\n children: props.children,\n mountNode: element ?? fallbackElement,\n virtualParentRootRef,\n };\n\n React.useEffect(() => {\n if (state.virtualParentRootRef.current && state.mountNode) {\n setVirtualParent(state.mountNode, state.virtualParentRootRef.current);\n }\n return () => {\n if (state.mountNode) {\n setVirtualParent(state.mountNode, undefined);\n }\n };\n }, [state.virtualParentRootRef, state.mountNode]);\n\n return state;\n};\n"],"names":["isHTMLElement","setVirtualParent","React","usePortalMountNode","toMountNodeProps","mountNode","element","usePortal_unstable","props","className","virtualParentRootRef","useRef","fallbackElement","disabled","state","children","useEffect","current","undefined"],"mappings":"AAAA,SAASA,aAAa,EAAEC,gBAAgB,QAAQ,4BAA4B;AAC5E,YAAYC,WAAW,QAAQ;AAE/B,SAASC,kBAAkB,QAAQ,uBAAuB;AAG1D,OAAO,SAASC,iBAAiBC,SAAmC;IAIlE,IAAIL,cAAcK,YAAY;QAC5B,OAAO;YAAEC,SAASD;QAAU;IAC9B;IAEA,IAAI,OAAOA,cAAc,UAAU;QACjC,IAAIA,cAAc,MAAM;YACtB,OAAO;gBAAEC,SAAS;YAAK;QACzB;QAEA,OAAOD;IACT;IAEA,OAAO,CAAC;AACV;AAEA;;;;;;CAMC,GACD,OAAO,MAAME,qBAAqB,CAACC;IACjC,MAAM,EAAEF,OAAO,EAAEG,SAAS,EAAE,GAAGL,iBAAiBI,MAAMH,SAAS;IAE/D,MAAMK,uBAAuBR,MAAMS,MAAM,CAAkB;IAC3D,MAAMC,kBAAkBT,mBAAmB;QAAEU,UAAU,CAAC,CAACP;QAASG;IAAU;IAE5E,MAAMK,QAAqB;QACzBC,UAAUP,MAAMO,QAAQ;QACxBV,WAAWC,oBAAAA,qBAAAA,UAAWM;QACtBF;IACF;IAEAR,MAAMc,SAAS,CAAC;QACd,IAAIF,MAAMJ,oBAAoB,CAACO,OAAO,IAAIH,MAAMT,SAAS,EAAE;YACzDJ,iBAAiBa,MAAMT,SAAS,EAAES,MAAMJ,oBAAoB,CAACO,OAAO;QACtE;QACA,OAAO;YACL,IAAIH,MAAMT,SAAS,EAAE;gBACnBJ,iBAAiBa,MAAMT,SAAS,EAAEa;YACpC;QACF;IACF,GAAG;QAACJ,MAAMJ,oBAAoB;QAAEI,MAAMT,SAAS;KAAC;IAEhD,OAAOS;AACT,EAAE"}
1
+ {"version":3,"sources":["usePortal.ts"],"sourcesContent":["import { setVirtualParent } from '@fluentui/react-utilities';\nimport * as React from 'react';\n\nimport { toMountNodeProps } from '../../utils/toMountNodeProps';\nimport { usePortalMountNode } from './usePortalMountNode';\nimport type { PortalProps, PortalState } from './Portal.types';\n\n/**\n * Create the state required to render Portal.\n *\n * The returned state can be modified with hooks such as usePortalStyles, before being passed to renderPortal_unstable.\n *\n * @param props - props from this instance of Portal\n */\nexport const usePortal_unstable = (props: PortalProps): PortalState => {\n const { element, className } = toMountNodeProps(props.mountNode);\n\n const virtualParentRootRef = React.useRef<HTMLSpanElement>(null);\n const fallbackElement = usePortalMountNode({ disabled: !!element, className });\n\n const mountNode = element ?? fallbackElement;\n const state: PortalState = {\n children: props.children,\n mountNode,\n virtualParentRootRef,\n };\n\n React.useEffect(() => {\n if (!mountNode) {\n return;\n }\n\n const virtualParent = virtualParentRootRef.current;\n\n // By default, we create a mount node for portal on `document.body` (see usePortalMountNode()) and have following structure:\n //\n // <body>\n // <!-- ⚛️ application root -->\n // <div id=\"root\">\n // <!-- ⬇️ portal node rendered in a tree to anchor (virtual parent node) -->\n // <span aria-hidden=\"true\"></span>\n // </div>\n // <div id=\"portal-mount-node\">\n // <!-- 🧩portal content -->\n // </div>\n // </body>\n //\n // To make sure that `.elementContains()` works correctly, we link a virtual parent to a portal node (a virtual parent node becomes a parent of mount node):\n // virtual.contains(mountNode) === false\n // (while we need ⬇️⬇️⬇️)\n // elementsContains(virtualParent, mountNode) === true\n // elementsContains(mountNode, virtualParent) === false\n //\n // For more details, check docs for virtual parent utils.\n //\n // However, if a user provides a custom mount node (via `props`) the structure could be different:\n //\n // <body>\n // <!-- application root -->\n // <div id=\"root\">\n // <div id=\"portal-mount-node\">\n // <!-- 🧩portal content -->\n //\n // <span aria-hidden=\"true\"></span>\n // </div>\n // </div>\n // </body>\n //\n // A mount node in this case contains portal's content and a virtual parent node. In this case nodes linking is redundant and the check below avoids it.\n //\n // Otherwise, there is a circular reference - both elements are parents of each other:\n // elementsContains(mountNode, virtualParent) === true\n // elementsContains(virtualParent, mountNode) === true\n const isVirtualParentInsideChild = mountNode.contains(virtualParent);\n\n if (virtualParent && !isVirtualParentInsideChild) {\n setVirtualParent(mountNode, virtualParent);\n\n return () => {\n setVirtualParent(mountNode, undefined);\n };\n }\n }, [virtualParentRootRef, mountNode]);\n\n return state;\n};\n"],"names":["setVirtualParent","React","toMountNodeProps","usePortalMountNode","usePortal_unstable","props","element","className","mountNode","virtualParentRootRef","useRef","fallbackElement","disabled","state","children","useEffect","virtualParent","current","isVirtualParentInsideChild","contains","undefined"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,4BAA4B;AAC7D,YAAYC,WAAW,QAAQ;AAE/B,SAASC,gBAAgB,QAAQ,+BAA+B;AAChE,SAASC,kBAAkB,QAAQ,uBAAuB;AAG1D;;;;;;CAMC,GACD,OAAO,MAAMC,qBAAqB,CAACC;IACjC,MAAM,EAAEC,OAAO,EAAEC,SAAS,EAAE,GAAGL,iBAAiBG,MAAMG,SAAS;IAE/D,MAAMC,uBAAuBR,MAAMS,MAAM,CAAkB;IAC3D,MAAMC,kBAAkBR,mBAAmB;QAAES,UAAU,CAAC,CAACN;QAASC;IAAU;IAE5E,MAAMC,YAAYF,oBAAAA,qBAAAA,UAAWK;IAC7B,MAAME,QAAqB;QACzBC,UAAUT,MAAMS,QAAQ;QACxBN;QACAC;IACF;IAEAR,MAAMc,SAAS,CAAC;QACd,IAAI,CAACP,WAAW;YACd;QACF;QAEA,MAAMQ,gBAAgBP,qBAAqBQ,OAAO;QAElD,4HAA4H;QAC5H,EAAE;QACF,SAAS;QACT,iCAAiC;QACjC,oBAAoB;QACpB,iFAAiF;QACjF,uCAAuC;QACvC,WAAW;QACX,iCAAiC;QACjC,gCAAgC;QAChC,WAAW;QACX,UAAU;QACV,EAAE;QACF,4JAA4J;QAC5J,0CAA0C;QAC1C,2BAA2B;QAC3B,wDAAwD;QACxD,yDAAyD;QACzD,EAAE;QACF,yDAAyD;QACzD,EAAE;QACF,kGAAkG;QAClG,EAAE;QACF,SAAS;QACT,8BAA8B;QAC9B,oBAAoB;QACpB,mCAAmC;QACnC,kCAAkC;QAClC,EAAE;QACF,yCAAyC;QACzC,aAAa;QACb,WAAW;QACX,UAAU;QACV,EAAE;QACF,wJAAwJ;QACxJ,EAAE;QACF,sFAAsF;QACtF,wDAAwD;QACxD,wDAAwD;QACxD,MAAMC,6BAA6BV,UAAUW,QAAQ,CAACH;QAEtD,IAAIA,iBAAiB,CAACE,4BAA4B;YAChDlB,iBAAiBQ,WAAWQ;YAE5B,OAAO;gBACLhB,iBAAiBQ,WAAWY;YAC9B;QACF;IACF,GAAG;QAACX;QAAsBD;KAAU;IAEpC,OAAOK;AACT,EAAE"}
package/lib/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { elementContains, setVirtualParent } from '@fluentui/react-utilities';
2
- export { Portal, renderPortal_unstable, toMountNodeProps, usePortal_unstable } from './components/Portal/index';
2
+ export { Portal, renderPortal_unstable, usePortal_unstable } from './components/Portal/index';
3
+ export { toMountNodeProps } from './utils/toMountNodeProps';
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["index.ts"],"sourcesContent":["export { elementContains, setVirtualParent } from '@fluentui/react-utilities';\nexport { Portal, renderPortal_unstable, toMountNodeProps, usePortal_unstable } from './components/Portal/index';\nexport type { PortalProps, PortalState } from './components/Portal/index';\n"],"names":["elementContains","setVirtualParent","Portal","renderPortal_unstable","toMountNodeProps","usePortal_unstable"],"mappings":"AAAA,SAASA,eAAe,EAAEC,gBAAgB,QAAQ,4BAA4B;AAC9E,SAASC,MAAM,EAAEC,qBAAqB,EAAEC,gBAAgB,EAAEC,kBAAkB,QAAQ,4BAA4B"}
1
+ {"version":3,"sources":["index.ts"],"sourcesContent":["export { elementContains, setVirtualParent } from '@fluentui/react-utilities';\nexport { Portal, renderPortal_unstable, usePortal_unstable } from './components/Portal/index';\nexport type { PortalProps, PortalState } from './components/Portal/index';\n\nexport { toMountNodeProps } from './utils/toMountNodeProps';\n"],"names":["elementContains","setVirtualParent","Portal","renderPortal_unstable","usePortal_unstable","toMountNodeProps"],"mappings":"AAAA,SAASA,eAAe,EAAEC,gBAAgB,QAAQ,4BAA4B;AAC9E,SAASC,MAAM,EAAEC,qBAAqB,EAAEC,kBAAkB,QAAQ,4BAA4B;AAG9F,SAASC,gBAAgB,QAAQ,2BAA2B"}
@@ -0,0 +1,21 @@
1
+ import { isHTMLElement } from '@fluentui/react-utilities';
2
+ /**
3
+ * The function that normalizes the `mountNode` prop into an object with element and className props.
4
+ *
5
+ * @param mountNode - an HTML element or an object with props
6
+ */ export function toMountNodeProps(mountNode) {
7
+ if (isHTMLElement(mountNode)) {
8
+ return {
9
+ element: mountNode
10
+ };
11
+ }
12
+ if (typeof mountNode === 'object') {
13
+ if (mountNode === null) {
14
+ return {
15
+ element: null
16
+ };
17
+ }
18
+ return mountNode;
19
+ }
20
+ return {};
21
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["toMountNodeProps.ts"],"sourcesContent":["import { isHTMLElement } from '@fluentui/react-utilities';\nimport type { PortalProps } from '../Portal';\n\n/**\n * The function that normalizes the `mountNode` prop into an object with element and className props.\n *\n * @param mountNode - an HTML element or an object with props\n */\nexport function toMountNodeProps(mountNode: PortalProps['mountNode']): {\n element?: HTMLElement | null;\n className?: string;\n} {\n if (isHTMLElement(mountNode)) {\n return { element: mountNode };\n }\n\n if (typeof mountNode === 'object') {\n if (mountNode === null) {\n return { element: null };\n }\n\n return mountNode;\n }\n\n return {};\n}\n"],"names":["isHTMLElement","toMountNodeProps","mountNode","element"],"mappings":"AAAA,SAASA,aAAa,QAAQ,4BAA4B;AAG1D;;;;CAIC,GACD,OAAO,SAASC,iBAAiBC,SAAmC;IAIlE,IAAIF,cAAcE,YAAY;QAC5B,OAAO;YAAEC,SAASD;QAAU;IAC9B;IAEA,IAAI,OAAOA,cAAc,UAAU;QACjC,IAAIA,cAAc,MAAM;YACtB,OAAO;gBAAEC,SAAS;YAAK;QACzB;QAEA,OAAOD;IACT;IAEA,OAAO,CAAC;AACV"}
@@ -2,64 +2,84 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- function _export(target, all) {
6
- for(var name in all)Object.defineProperty(target, name, {
7
- enumerable: true,
8
- get: all[name]
9
- });
10
- }
11
- _export(exports, {
12
- toMountNodeProps: function() {
13
- return toMountNodeProps;
14
- },
15
- usePortal_unstable: function() {
5
+ Object.defineProperty(exports, "usePortal_unstable", {
6
+ enumerable: true,
7
+ get: function() {
16
8
  return usePortal_unstable;
17
9
  }
18
10
  });
19
11
  const _interop_require_wildcard = require("@swc/helpers/_/_interop_require_wildcard");
20
12
  const _reactutilities = require("@fluentui/react-utilities");
21
13
  const _react = /*#__PURE__*/ _interop_require_wildcard._(require("react"));
14
+ const _toMountNodeProps = require("../../utils/toMountNodeProps");
22
15
  const _usePortalMountNode = require("./usePortalMountNode");
23
- function toMountNodeProps(mountNode) {
24
- if ((0, _reactutilities.isHTMLElement)(mountNode)) {
25
- return {
26
- element: mountNode
27
- };
28
- }
29
- if (typeof mountNode === 'object') {
30
- if (mountNode === null) {
31
- return {
32
- element: null
33
- };
34
- }
35
- return mountNode;
36
- }
37
- return {};
38
- }
39
16
  const usePortal_unstable = (props)=>{
40
- const { element, className } = toMountNodeProps(props.mountNode);
17
+ const { element, className } = (0, _toMountNodeProps.toMountNodeProps)(props.mountNode);
41
18
  const virtualParentRootRef = _react.useRef(null);
42
19
  const fallbackElement = (0, _usePortalMountNode.usePortalMountNode)({
43
20
  disabled: !!element,
44
21
  className
45
22
  });
23
+ const mountNode = element !== null && element !== void 0 ? element : fallbackElement;
46
24
  const state = {
47
25
  children: props.children,
48
- mountNode: element !== null && element !== void 0 ? element : fallbackElement,
26
+ mountNode,
49
27
  virtualParentRootRef
50
28
  };
51
29
  _react.useEffect(()=>{
52
- if (state.virtualParentRootRef.current && state.mountNode) {
53
- (0, _reactutilities.setVirtualParent)(state.mountNode, state.virtualParentRootRef.current);
30
+ if (!mountNode) {
31
+ return;
32
+ }
33
+ const virtualParent = virtualParentRootRef.current;
34
+ // By default, we create a mount node for portal on `document.body` (see usePortalMountNode()) and have following structure:
35
+ //
36
+ // <body>
37
+ // <!-- ⚛️ application root -->
38
+ // <div id="root">
39
+ // <!-- ⬇️ portal node rendered in a tree to anchor (virtual parent node) -->
40
+ // <span aria-hidden="true"></span>
41
+ // </div>
42
+ // <div id="portal-mount-node">
43
+ // <!-- 🧩portal content -->
44
+ // </div>
45
+ // </body>
46
+ //
47
+ // To make sure that `.elementContains()` works correctly, we link a virtual parent to a portal node (a virtual parent node becomes a parent of mount node):
48
+ // virtual.contains(mountNode) === false
49
+ // (while we need ⬇️⬇️⬇️)
50
+ // elementsContains(virtualParent, mountNode) === true
51
+ // elementsContains(mountNode, virtualParent) === false
52
+ //
53
+ // For more details, check docs for virtual parent utils.
54
+ //
55
+ // However, if a user provides a custom mount node (via `props`) the structure could be different:
56
+ //
57
+ // <body>
58
+ // <!-- application root -->
59
+ // <div id="root">
60
+ // <div id="portal-mount-node">
61
+ // <!-- 🧩portal content -->
62
+ //
63
+ // <span aria-hidden="true"></span>
64
+ // </div>
65
+ // </div>
66
+ // </body>
67
+ //
68
+ // A mount node in this case contains portal's content and a virtual parent node. In this case nodes linking is redundant and the check below avoids it.
69
+ //
70
+ // Otherwise, there is a circular reference - both elements are parents of each other:
71
+ // elementsContains(mountNode, virtualParent) === true
72
+ // elementsContains(virtualParent, mountNode) === true
73
+ const isVirtualParentInsideChild = mountNode.contains(virtualParent);
74
+ if (virtualParent && !isVirtualParentInsideChild) {
75
+ (0, _reactutilities.setVirtualParent)(mountNode, virtualParent);
76
+ return ()=>{
77
+ (0, _reactutilities.setVirtualParent)(mountNode, undefined);
78
+ };
54
79
  }
55
- return ()=>{
56
- if (state.mountNode) {
57
- (0, _reactutilities.setVirtualParent)(state.mountNode, undefined);
58
- }
59
- };
60
80
  }, [
61
- state.virtualParentRootRef,
62
- state.mountNode
81
+ virtualParentRootRef,
82
+ mountNode
63
83
  ]);
64
84
  return state;
65
85
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["usePortal.js"],"sourcesContent":["import { isHTMLElement, setVirtualParent } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { usePortalMountNode } from './usePortalMountNode';\nexport function toMountNodeProps(mountNode) {\n if (isHTMLElement(mountNode)) {\n return {\n element: mountNode\n };\n }\n if (typeof mountNode === 'object') {\n if (mountNode === null) {\n return {\n element: null\n };\n }\n return mountNode;\n }\n return {};\n}\n/**\n * Create the state required to render Portal.\n *\n * The returned state can be modified with hooks such as usePortalStyles, before being passed to renderPortal_unstable.\n *\n * @param props - props from this instance of Portal\n */ export const usePortal_unstable = (props)=>{\n const { element, className } = toMountNodeProps(props.mountNode);\n const virtualParentRootRef = React.useRef(null);\n const fallbackElement = usePortalMountNode({\n disabled: !!element,\n className\n });\n const state = {\n children: props.children,\n mountNode: element !== null && element !== void 0 ? element : fallbackElement,\n virtualParentRootRef\n };\n React.useEffect(()=>{\n if (state.virtualParentRootRef.current && state.mountNode) {\n setVirtualParent(state.mountNode, state.virtualParentRootRef.current);\n }\n return ()=>{\n if (state.mountNode) {\n setVirtualParent(state.mountNode, undefined);\n }\n };\n }, [\n state.virtualParentRootRef,\n state.mountNode\n ]);\n return state;\n};\n"],"names":["toMountNodeProps","usePortal_unstable","mountNode","isHTMLElement","element","props","className","virtualParentRootRef","React","useRef","fallbackElement","usePortalMountNode","disabled","state","children","useEffect","current","setVirtualParent","undefined"],"mappings":";;;;;;;;;;;IAGgBA,gBAAgB;eAAhBA;;IAsBCC,kBAAkB;eAAlBA;;;;gCAzB+B;iEACzB;oCACY;AAC5B,SAASD,iBAAiBE,SAAS;IACtC,IAAIC,IAAAA,6BAAa,EAACD,YAAY;QAC1B,OAAO;YACHE,SAASF;QACb;IACJ;IACA,IAAI,OAAOA,cAAc,UAAU;QAC/B,IAAIA,cAAc,MAAM;YACpB,OAAO;gBACHE,SAAS;YACb;QACJ;QACA,OAAOF;IACX;IACA,OAAO,CAAC;AACZ;AAOW,MAAMD,qBAAqB,CAACI;IACnC,MAAM,EAAED,OAAO,EAAEE,SAAS,EAAE,GAAGN,iBAAiBK,MAAMH,SAAS;IAC/D,MAAMK,uBAAuBC,OAAMC,MAAM,CAAC;IAC1C,MAAMC,kBAAkBC,IAAAA,sCAAkB,EAAC;QACvCC,UAAU,CAAC,CAACR;QACZE;IACJ;IACA,MAAMO,QAAQ;QACVC,UAAUT,MAAMS,QAAQ;QACxBZ,WAAWE,YAAY,QAAQA,YAAY,KAAK,IAAIA,UAAUM;QAC9DH;IACJ;IACAC,OAAMO,SAAS,CAAC;QACZ,IAAIF,MAAMN,oBAAoB,CAACS,OAAO,IAAIH,MAAMX,SAAS,EAAE;YACvDe,IAAAA,gCAAgB,EAACJ,MAAMX,SAAS,EAAEW,MAAMN,oBAAoB,CAACS,OAAO;QACxE;QACA,OAAO;YACH,IAAIH,MAAMX,SAAS,EAAE;gBACjBe,IAAAA,gCAAgB,EAACJ,MAAMX,SAAS,EAAEgB;YACtC;QACJ;IACJ,GAAG;QACCL,MAAMN,oBAAoB;QAC1BM,MAAMX,SAAS;KAClB;IACD,OAAOW;AACX"}
1
+ {"version":3,"sources":["usePortal.js"],"sourcesContent":["import { setVirtualParent } from '@fluentui/react-utilities';\nimport * as React from 'react';\nimport { toMountNodeProps } from '../../utils/toMountNodeProps';\nimport { usePortalMountNode } from './usePortalMountNode';\n/**\n * Create the state required to render Portal.\n *\n * The returned state can be modified with hooks such as usePortalStyles, before being passed to renderPortal_unstable.\n *\n * @param props - props from this instance of Portal\n */ export const usePortal_unstable = (props)=>{\n const { element, className } = toMountNodeProps(props.mountNode);\n const virtualParentRootRef = React.useRef(null);\n const fallbackElement = usePortalMountNode({\n disabled: !!element,\n className\n });\n const mountNode = element !== null && element !== void 0 ? element : fallbackElement;\n const state = {\n children: props.children,\n mountNode,\n virtualParentRootRef\n };\n React.useEffect(()=>{\n if (!mountNode) {\n return;\n }\n const virtualParent = virtualParentRootRef.current;\n // By default, we create a mount node for portal on `document.body` (see usePortalMountNode()) and have following structure:\n //\n // <body>\n // <!-- ⚛️ application root -->\n // <div id=\"root\">\n // <!-- ⬇️ portal node rendered in a tree to anchor (virtual parent node) -->\n // <span aria-hidden=\"true\"></span>\n // </div>\n // <div id=\"portal-mount-node\">\n // <!-- 🧩portal content -->\n // </div>\n // </body>\n //\n // To make sure that `.elementContains()` works correctly, we link a virtual parent to a portal node (a virtual parent node becomes a parent of mount node):\n // virtual.contains(mountNode) === false\n // (while we need ⬇️⬇️⬇️)\n // elementsContains(virtualParent, mountNode) === true\n // elementsContains(mountNode, virtualParent) === false\n //\n // For more details, check docs for virtual parent utils.\n //\n // However, if a user provides a custom mount node (via `props`) the structure could be different:\n //\n // <body>\n // <!-- application root -->\n // <div id=\"root\">\n // <div id=\"portal-mount-node\">\n // <!-- 🧩portal content -->\n //\n // <span aria-hidden=\"true\"></span>\n // </div>\n // </div>\n // </body>\n //\n // A mount node in this case contains portal's content and a virtual parent node. In this case nodes linking is redundant and the check below avoids it.\n //\n // Otherwise, there is a circular reference - both elements are parents of each other:\n // elementsContains(mountNode, virtualParent) === true\n // elementsContains(virtualParent, mountNode) === true\n const isVirtualParentInsideChild = mountNode.contains(virtualParent);\n if (virtualParent && !isVirtualParentInsideChild) {\n setVirtualParent(mountNode, virtualParent);\n return ()=>{\n setVirtualParent(mountNode, undefined);\n };\n }\n }, [\n virtualParentRootRef,\n mountNode\n ]);\n return state;\n};\n"],"names":["usePortal_unstable","props","element","className","toMountNodeProps","mountNode","virtualParentRootRef","React","useRef","fallbackElement","usePortalMountNode","disabled","state","children","useEffect","virtualParent","current","isVirtualParentInsideChild","contains","setVirtualParent","undefined"],"mappings":";;;;+BAUiBA;;;eAAAA;;;;gCAVgB;iEACV;kCACU;oCACE;AAOxB,MAAMA,qBAAqB,CAACC;IACnC,MAAM,EAAEC,OAAO,EAAEC,SAAS,EAAE,GAAGC,IAAAA,kCAAgB,EAACH,MAAMI,SAAS;IAC/D,MAAMC,uBAAuBC,OAAMC,MAAM,CAAC;IAC1C,MAAMC,kBAAkBC,IAAAA,sCAAkB,EAAC;QACvCC,UAAU,CAAC,CAACT;QACZC;IACJ;IACA,MAAME,YAAYH,YAAY,QAAQA,YAAY,KAAK,IAAIA,UAAUO;IACrE,MAAMG,QAAQ;QACVC,UAAUZ,MAAMY,QAAQ;QACxBR;QACAC;IACJ;IACAC,OAAMO,SAAS,CAAC;QACZ,IAAI,CAACT,WAAW;YACZ;QACJ;QACA,MAAMU,gBAAgBT,qBAAqBU,OAAO;QAClD,4HAA4H;QAC5H,EAAE;QACF,SAAS;QACT,iCAAiC;QACjC,oBAAoB;QACpB,iFAAiF;QACjF,uCAAuC;QACvC,WAAW;QACX,iCAAiC;QACjC,gCAAgC;QAChC,WAAW;QACX,UAAU;QACV,EAAE;QACF,4JAA4J;QAC5J,0CAA0C;QAC1C,2BAA2B;QAC3B,wDAAwD;QACxD,yDAAyD;QACzD,EAAE;QACF,yDAAyD;QACzD,EAAE;QACF,kGAAkG;QAClG,EAAE;QACF,SAAS;QACT,8BAA8B;QAC9B,oBAAoB;QACpB,mCAAmC;QACnC,kCAAkC;QAClC,EAAE;QACF,yCAAyC;QACzC,aAAa;QACb,WAAW;QACX,UAAU;QACV,EAAE;QACF,wJAAwJ;QACxJ,EAAE;QACF,sFAAsF;QACtF,wDAAwD;QACxD,wDAAwD;QACxD,MAAMC,6BAA6BZ,UAAUa,QAAQ,CAACH;QACtD,IAAIA,iBAAiB,CAACE,4BAA4B;YAC9CE,IAAAA,gCAAgB,EAACd,WAAWU;YAC5B,OAAO;gBACHI,IAAAA,gCAAgB,EAACd,WAAWe;YAChC;QACJ;IACJ,GAAG;QACCd;QACAD;KACH;IACD,OAAOO;AACX"}
@@ -21,12 +21,13 @@ _export(exports, {
21
21
  renderPortal_unstable: function() {
22
22
  return _index.renderPortal_unstable;
23
23
  },
24
- toMountNodeProps: function() {
25
- return _index.toMountNodeProps;
26
- },
27
24
  usePortal_unstable: function() {
28
25
  return _index.usePortal_unstable;
26
+ },
27
+ toMountNodeProps: function() {
28
+ return _toMountNodeProps.toMountNodeProps;
29
29
  }
30
30
  });
31
31
  const _reactutilities = require("@fluentui/react-utilities");
32
32
  const _index = require("./components/Portal/index");
33
+ const _toMountNodeProps = require("./utils/toMountNodeProps");
@@ -1 +1 @@
1
- {"version":3,"sources":["index.js"],"sourcesContent":["export { elementContains, setVirtualParent } from '@fluentui/react-utilities';\nexport { Portal, renderPortal_unstable, toMountNodeProps, usePortal_unstable } from './components/Portal/index';\n"],"names":["elementContains","setVirtualParent","Portal","renderPortal_unstable","toMountNodeProps","usePortal_unstable"],"mappings":";;;;;;;;;;;IAASA,eAAe;eAAfA,+BAAe;;IAAEC,gBAAgB;eAAhBA,gCAAgB;;IACjCC,MAAM;eAANA,aAAM;;IAAEC,qBAAqB;eAArBA,4BAAqB;;IAAEC,gBAAgB;eAAhBA,uBAAgB;;IAAEC,kBAAkB;eAAlBA,yBAAkB;;;gCAD1B;uBACkC"}
1
+ {"version":3,"sources":["index.js"],"sourcesContent":["export { elementContains, setVirtualParent } from '@fluentui/react-utilities';\nexport { Portal, renderPortal_unstable, usePortal_unstable } from './components/Portal/index';\nexport { toMountNodeProps } from './utils/toMountNodeProps';\n"],"names":["elementContains","setVirtualParent","Portal","renderPortal_unstable","usePortal_unstable","toMountNodeProps"],"mappings":";;;;;;;;;;;IAASA,eAAe;eAAfA,+BAAe;;IAAEC,gBAAgB;eAAhBA,gCAAgB;;IACjCC,MAAM;eAANA,aAAM;;IAAEC,qBAAqB;eAArBA,4BAAqB;;IAAEC,kBAAkB;eAAlBA,yBAAkB;;IACjDC,gBAAgB;eAAhBA,kCAAgB;;;gCAFyB;uBACgB;kCACjC"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "toMountNodeProps", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return toMountNodeProps;
9
+ }
10
+ });
11
+ const _reactutilities = require("@fluentui/react-utilities");
12
+ function toMountNodeProps(mountNode) {
13
+ if ((0, _reactutilities.isHTMLElement)(mountNode)) {
14
+ return {
15
+ element: mountNode
16
+ };
17
+ }
18
+ if (typeof mountNode === 'object') {
19
+ if (mountNode === null) {
20
+ return {
21
+ element: null
22
+ };
23
+ }
24
+ return mountNode;
25
+ }
26
+ return {};
27
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["toMountNodeProps.js"],"sourcesContent":["import { isHTMLElement } from '@fluentui/react-utilities';\n/**\n * The function that normalizes the `mountNode` prop into an object with element and className props.\n *\n * @param mountNode - an HTML element or an object with props\n */ export function toMountNodeProps(mountNode) {\n if (isHTMLElement(mountNode)) {\n return {\n element: mountNode\n };\n }\n if (typeof mountNode === 'object') {\n if (mountNode === null) {\n return {\n element: null\n };\n }\n return mountNode;\n }\n return {};\n}\n"],"names":["toMountNodeProps","mountNode","isHTMLElement","element"],"mappings":";;;;+BAKoBA;;;eAAAA;;;gCALU;AAKnB,SAASA,iBAAiBC,SAAS;IAC1C,IAAIC,IAAAA,6BAAa,EAACD,YAAY;QAC1B,OAAO;YACHE,SAASF;QACb;IACJ;IACA,IAAI,OAAOA,cAAc,UAAU;QAC/B,IAAIA,cAAc,MAAM;YACpB,OAAO;gBACHE,SAAS;YACb;QACJ;QACA,OAAOF;IACX;IACA,OAAO,CAAC;AACZ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluentui/react-portal",
3
- "version": "9.4.2",
3
+ "version": "9.4.4",
4
4
  "description": "A utility component that creates portals compatible with Fluent UI",
5
5
  "main": "lib-commonjs/index.js",
6
6
  "module": "lib/index.js",
@@ -32,9 +32,9 @@
32
32
  "@fluentui/scripts-tasks": "*"
33
33
  },
34
34
  "dependencies": {
35
- "@fluentui/react-shared-contexts": "^9.13.0",
36
- "@fluentui/react-tabster": "^9.14.6",
37
- "@fluentui/react-utilities": "^9.15.2",
35
+ "@fluentui/react-shared-contexts": "^9.13.1",
36
+ "@fluentui/react-tabster": "^9.15.1",
37
+ "@fluentui/react-utilities": "^9.15.3",
38
38
  "@griffel/react": "^1.5.14",
39
39
  "@swc/helpers": "^0.5.1",
40
40
  "use-disposable": "^1.0.1"