Initial commit

This commit is contained in:
2025-03-07 19:22:02 +01:00
commit 4a98255d83
55743 changed files with 5280367 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
import * as React from 'react';
import { usePortal_unstable } from './usePortal';
import { renderPortal_unstable } from './renderPortal';
/**
* A portal provides a way to render children into a DOM node
* that exists outside the DOM hierarchy of the parent component.
*/ export const Portal = (props)=>{
const state = usePortal_unstable(props);
return renderPortal_unstable(state);
};
Portal.displayName = 'Portal';
@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Portal/Portal.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { usePortal_unstable } from './usePortal';\nimport { renderPortal_unstable } from './renderPortal';\nimport type { PortalProps } from './Portal.types';\n\n/**\n * A portal provides a way to render children into a DOM node\n * that exists outside the DOM hierarchy of the parent component.\n */\nexport const Portal: React.FC<PortalProps> = props => {\n const state = usePortal_unstable(props);\n\n return renderPortal_unstable(state);\n};\n\nPortal.displayName = 'Portal';\n"],"names":["React","usePortal_unstable","renderPortal_unstable","Portal","props","state","displayName"],"rangeMappings":";;;;;;;;;;","mappings":"AAAA,YAAYA,WAAW,QAAQ;AAE/B,SAASC,kBAAkB,QAAQ,cAAc;AACjD,SAASC,qBAAqB,QAAQ,iBAAiB;AAGvD;;;CAGC,GACD,OAAO,MAAMC,SAAgCC,CAAAA;IAC3C,MAAMC,QAAQJ,mBAAmBG;IAEjC,OAAOF,sBAAsBG;AAC/B,EAAE;AAEFF,OAAOG,WAAW,GAAG"}
@@ -0,0 +1 @@
import * as React from 'react';
@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Portal/Portal.types.ts"],"sourcesContent":["import * as React from 'react';\n\nexport type PortalProps = {\n /**\n * React children\n */\n children?: React.ReactNode;\n\n /**\n * Where the portal children are mounted on DOM\n *\n * @default a new element on document.body without any styling\n */\n mountNode?: HTMLElement | null | { element?: HTMLElement | null; className?: string };\n};\n\nexport type PortalState = Pick<PortalProps, 'children'> & {\n mountNode: HTMLElement | null | undefined;\n\n /**\n * Ref to the root span element as virtual parent\n */\n virtualParentRootRef: React.MutableRefObject<HTMLSpanElement | null>;\n};\n"],"names":["React"],"rangeMappings":"","mappings":"AAAA,YAAYA,WAAW,QAAQ"}
+3
View File
@@ -0,0 +1,3 @@
export { Portal } from './Portal';
export { renderPortal_unstable } from './renderPortal';
export { usePortal_unstable } from './usePortal';
@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Portal/index.ts"],"sourcesContent":["export { Portal } from './Portal';\nexport type { PortalProps, PortalState } from './Portal.types';\nexport { renderPortal_unstable } from './renderPortal';\nexport { usePortal_unstable } from './usePortal';\n"],"names":["Portal","renderPortal_unstable","usePortal_unstable"],"rangeMappings":";;","mappings":"AAAA,SAASA,MAAM,QAAQ,WAAW;AAElC,SAASC,qBAAqB,QAAQ,iBAAiB;AACvD,SAASC,kBAAkB,QAAQ,cAAc"}
@@ -0,0 +1,10 @@
import * as ReactDOM from 'react-dom';
import * as React from 'react';
/**
* Render the final JSX of Portal
*/ export const renderPortal_unstable = (state)=>{
return /*#__PURE__*/ React.createElement("span", {
hidden: true,
ref: state.virtualParentRootRef
}, state.mountNode && /*#__PURE__*/ ReactDOM.createPortal(state.children, state.mountNode));
};
@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Portal/renderPortal.tsx"],"sourcesContent":["import * as ReactDOM from 'react-dom';\nimport * as React from 'react';\nimport type { PortalState } from './Portal.types';\n\n/**\n * Render the final JSX of Portal\n */\nexport const renderPortal_unstable = (state: PortalState): React.ReactElement => {\n return (\n <span hidden ref={state.virtualParentRootRef}>\n {state.mountNode && ReactDOM.createPortal(state.children, state.mountNode)}\n </span>\n );\n};\n"],"names":["ReactDOM","React","renderPortal_unstable","state","span","hidden","ref","virtualParentRootRef","mountNode","createPortal","children"],"rangeMappings":";;;;;;;;;","mappings":"AAAA,YAAYA,cAAc,YAAY;AACtC,YAAYC,WAAW,QAAQ;AAG/B;;CAEC,GACD,OAAO,MAAMC,wBAAwB,CAACC;IACpC,qBACE,oBAACC;QAAKC,QAAAA;QAAOC,KAAKH,MAAMI,oBAAoB;OACzCJ,MAAMK,SAAS,kBAAIR,SAASS,YAAY,CAACN,MAAMO,QAAQ,EAAEP,MAAMK,SAAS;AAG/E,EAAE"}
+80
View File
@@ -0,0 +1,80 @@
import { setVirtualParent } from '@fluentui/react-utilities';
import * as React from 'react';
import { toMountNodeProps } from '../../utils/toMountNodeProps';
import { usePortalMountNode } from './usePortalMountNode';
/**
* Create the state required to render Portal.
*
* The returned state can be modified with hooks such as usePortalStyles, before being passed to renderPortal_unstable.
*
* @param props - props from this instance of Portal
*/ export const usePortal_unstable = (props)=>{
const { element, className } = toMountNodeProps(props.mountNode);
const virtualParentRootRef = React.useRef(null);
const fallbackElement = usePortalMountNode({
disabled: !!element,
className
});
const mountNode = element !== null && element !== void 0 ? element : fallbackElement;
const state = {
children: props.children,
mountNode,
virtualParentRootRef
};
React.useEffect(()=>{
if (!mountNode) {
return;
}
const virtualParent = virtualParentRootRef.current;
// By default, we create a mount node for portal on `document.body` (see usePortalMountNode()) and have following structure:
//
// <body>
// <!-- ⚛️ application root -->
// <div id="root">
// <!-- ⬇️ portal node rendered in a tree to anchor (virtual parent node) -->
// <span aria-hidden="true"></span>
// </div>
// <div id="portal-mount-node">
// <!-- 🧩portal content -->
// </div>
// </body>
//
// 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):
// virtual.contains(mountNode) === false
// (while we need ⬇️⬇️⬇️)
// elementsContains(virtualParent, mountNode) === true
// elementsContains(mountNode, virtualParent) === false
//
// For more details, check docs for virtual parent utils.
//
// However, if a user provides a custom mount node (via `props`) the structure could be different:
//
// <body>
// <!-- application root -->
// <div id="root">
// <div id="portal-mount-node">
// <!-- 🧩portal content -->
//
// <span aria-hidden="true"></span>
// </div>
// </div>
// </body>
//
// 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.
//
// Otherwise, there is a circular reference - both elements are parents of each other:
// elementsContains(mountNode, virtualParent) === true
// elementsContains(virtualParent, mountNode) === true
const isVirtualParentInsideChild = mountNode.contains(virtualParent);
if (virtualParent && !isVirtualParentInsideChild) {
setVirtualParent(mountNode, virtualParent);
return ()=>{
setVirtualParent(mountNode, undefined);
};
}
}, [
virtualParentRootRef,
mountNode
]);
return state;
};
@@ -0,0 +1 @@
{"version":3,"sources":["../src/components/Portal/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"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","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"}
@@ -0,0 +1,79 @@
import * as React from 'react';
import { useThemeClassName_unstable as useThemeClassName, useFluent_unstable as useFluent, usePortalMountNode as usePortalMountNodeContext } from '@fluentui/react-shared-contexts';
import { mergeClasses } from '@griffel/react';
import { useFocusVisible } from '@fluentui/react-tabster';
import { useDisposable } from 'use-disposable';
import { usePortalMountNodeStylesStyles } from './usePortalMountNodeStyles.styles';
const useInsertionEffect = React['useInsertion' + 'Effect'];
/**
* Creates a new element on a "document.body" to mount portals.
*/ export const usePortalMountNode = (options)=>{
'use no memo';
const { targetDocument, dir } = useFluent();
const mountNode = usePortalMountNodeContext();
const focusVisibleRef = useFocusVisible();
const classes = usePortalMountNodeStylesStyles();
const themeClassName = useThemeClassName();
const className = mergeClasses(themeClassName, classes.root, options.className);
const targetNode = mountNode !== null && mountNode !== void 0 ? mountNode : targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.body;
const element = useDisposable(()=>{
if (targetNode === undefined || options.disabled) {
return [
null,
()=>null
];
}
const newElement = targetNode.ownerDocument.createElement('div');
targetNode.appendChild(newElement);
return [
newElement,
()=>newElement.remove()
];
}, [
targetNode
]);
if (useInsertionEffect) {
// eslint-disable-next-line react-hooks/rules-of-hooks
useInsertionEffect(()=>{
if (!element) {
return;
}
const classesToApply = className.split(' ').filter(Boolean);
element.classList.add(...classesToApply);
element.setAttribute('dir', dir);
element.setAttribute('data-portal-node', 'true');
focusVisibleRef.current = element;
return ()=>{
element.classList.remove(...classesToApply);
element.removeAttribute('dir');
};
}, [
className,
dir,
element,
focusVisibleRef
]);
} else {
// This useMemo call is intentional for React 17
// We don't want to re-create the portal element when its attributes change.
// This also should not be done in an effect because, changing the value of css variables
// after initial mount can trigger interesting CSS side effects like transitions.
// eslint-disable-next-line react-hooks/rules-of-hooks
React.useMemo(()=>{
if (!element) {
return;
}
// Force replace all classes
element.className = className;
element.setAttribute('dir', dir);
element.setAttribute('data-portal-node', 'true');
focusVisibleRef.current = element;
}, [
className,
dir,
element,
focusVisibleRef
]);
}
return element;
};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,12 @@
import { __styles } from '@griffel/react';
export const usePortalMountNodeStylesStyles = /*#__PURE__*/__styles({
root: {
qhf8xq: "f1euv43f",
Bhzewxz: "f15twtuk",
oyh7mz: ["f1vgc2s3", "f1e31b4d"],
j35jbq: ["f1e31b4d", "f1vgc2s3"],
Bj3rh1h: "f494woh"
}
}, {
d: [".f1euv43f{position:absolute;}", ".f15twtuk{top:0;}", ".f1vgc2s3{left:0;}", ".f1e31b4d{right:0;}", ".f494woh{z-index:1000000;}"]
});
@@ -0,0 +1 @@
{"version":3,"names":["__styles","usePortalMountNodeStylesStyles","root","qhf8xq","Bhzewxz","oyh7mz","j35jbq","Bj3rh1h","d"],"sources":["usePortalMountNodeStyles.styles.js"],"sourcesContent":["import { makeStyles } from '@griffel/react';\nexport const usePortalMountNodeStylesStyles = makeStyles({\n root: {\n // Creates new stacking context to prevent z-index issues\n // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context\n //\n // Also keeps a portal on top of a page to prevent scrollbars from appearing\n position: 'absolute',\n top: 0,\n left: 0,\n right: 0,\n zIndex: 1000000\n }\n});\n"],"mappings":"AAAA,SAAAA,QAAA,QAA2B,gBAAgB;AAC3C,OAAO,MAAMC,8BAA8B,gBAAGD,QAAA;EAAAE,IAAA;IAAAC,MAAA;IAAAC,OAAA;IAAAC,MAAA;IAAAC,MAAA;IAAAC,OAAA;EAAA;AAAA;EAAAC,CAAA;AAAA,CAY7C,CAAC","ignoreList":[]}