@chakra-ui/portal
Advanced tools
Comparing version 1.0.6 to 1.1.0
# Change Log | ||
## 1.1.0 | ||
### Minor Changes | ||
- [`e41e6b81b`](https://github.com/chakra-ui/chakra-ui/commit/e41e6b81bf6943fef9b34e5ddd31ee57b416a426) | ||
[#3210](https://github.com/chakra-ui/chakra-ui/pull/3210) Thanks | ||
[@segunadebayo](https://github.com/segunadebayo)! - - Add support for changing | ||
the container that portal is appended to. You can now pass `containerRef` to | ||
portal. | ||
- Update portal `README.md` and tests. | ||
- Add support for `appendToParentPortal={false}` to opt out of nested portals. | ||
- Fix issue with portal `zIndex` container where it renders elements outside | ||
of view. | ||
- Renamed `getContainer` prop to `containerRef` to make it possible to pass | ||
the `ref` directly. This affects the `Modal` component primarily | ||
```jsx live=false | ||
// Before | ||
<Portal getContainer={() => ref.current}>{/** Content */}</Portal> | ||
// After | ||
<Portal containerRef={ref}>{/** Content */}</Portal> | ||
``` | ||
### Patch Changes | ||
- Updated dependencies | ||
[[`b572bceed`](https://github.com/chakra-ui/chakra-ui/commit/b572bceedd9fb0c41c65118f0d9ba672791932ca)]: | ||
- @chakra-ui/hooks@1.1.3 | ||
## 1.0.6 | ||
@@ -4,0 +35,0 @@ |
@@ -20,2 +20,6 @@ "use strict"; | ||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } | ||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } | ||
var _createContext = (0, _utils.createContext)({ | ||
@@ -28,52 +32,43 @@ strict: false, | ||
var PORTAL_CLASSNAME = "chakra-portal"; | ||
var PORTAL_SELECTOR = ".chakra-portal"; | ||
var Container = function Container(props) { | ||
var children = props.children, | ||
zIndex = props.zIndex; | ||
return /*#__PURE__*/React.createElement("div", { | ||
className: "chakra-portal-zIndex", | ||
style: { | ||
position: "relative", | ||
zIndex: zIndex | ||
position: "absolute", | ||
zIndex: props.zIndex, | ||
top: 0, | ||
left: 0, | ||
right: 0 // NB: Don't add `bottom: 0`, it makes the entire app unusable | ||
// @see https://github.com/chakra-ui/chakra-ui/issues/3201 | ||
} | ||
}, children); | ||
}, props.children); | ||
}; | ||
/** | ||
* Portal | ||
* | ||
* Declarative component used to render children into a DOM node | ||
* that exists outside the DOM hierarchy of the parent component. | ||
* | ||
* @see Docs https://chakra-ui.com/docs/overlay/portal | ||
* Portal that uses `document.body` as container | ||
*/ | ||
function Portal(props) { | ||
var DefaultPortal = function DefaultPortal(props) { | ||
var appendToParentPortal = props.appendToParentPortal, | ||
children = props.children; | ||
var tempNode = React.useRef(null); | ||
var portal = React.useRef(null); | ||
var forceUpdate = (0, _hooks.useForceUpdate)(); | ||
var getContainer = (0, _hooks.useCallbackRef)(props.getContainer); | ||
var onMount = (0, _hooks.useCallbackRef)(props.onMount); | ||
var onUnmount = (0, _hooks.useCallbackRef)(props.onUnmount); | ||
var parentPortal = usePortalContext(); | ||
var manager = (0, _portalManager.usePortalManager)(); | ||
(0, _hooks.useSafeLayoutEffect)(function () { | ||
var _ref, _getContainer; | ||
if (!tempNode.current) return; | ||
var doc = tempNode.current.ownerDocument; | ||
var host = (_ref = (_getContainer = getContainer()) != null ? _getContainer : parentPortal) != null ? _ref : doc.body; | ||
/** | ||
* host may be `null` when a hot-loader | ||
* replaces components on the page | ||
*/ | ||
var host = appendToParentPortal ? parentPortal != null ? parentPortal : doc.body : doc.body; | ||
if (!host) return; | ||
portal.current = doc.createElement("div"); | ||
portal.current.className = Portal.className; | ||
portal.current.className = PORTAL_CLASSNAME; | ||
host.appendChild(portal.current); | ||
forceUpdate(); | ||
onMount(); | ||
var portalNode = portal.current; | ||
return function () { | ||
onUnmount(); | ||
if (host.contains(portalNode)) { | ||
@@ -84,14 +79,71 @@ host.removeChild(portalNode); | ||
}, []); | ||
var childrenToRender = manager != null && manager.zIndex ? /*#__PURE__*/React.createElement(Container, { | ||
zIndex: manager.zIndex | ||
}, props.children) : props.children; | ||
var _children = manager != null && manager.zIndex ? /*#__PURE__*/React.createElement(Container, { | ||
zIndex: manager == null ? void 0 : manager.zIndex | ||
}, children) : children; | ||
return portal.current ? /*#__PURE__*/(0, _reactDom.createPortal)( /*#__PURE__*/React.createElement(PortalContextProvider, { | ||
value: portal.current | ||
}, childrenToRender), portal.current) : /*#__PURE__*/React.createElement("span", { | ||
}, _children), portal.current) : /*#__PURE__*/React.createElement("span", { | ||
ref: tempNode | ||
}); | ||
}; | ||
/** | ||
* Portal that uses a custom container | ||
*/ | ||
var ContainerPortal = function ContainerPortal(props) { | ||
var children = props.children, | ||
containerRef = props.containerRef, | ||
appendToParentPortal = props.appendToParentPortal; | ||
var containerEl = containerRef.current; | ||
var host = containerEl != null ? containerEl : _utils.isBrowser ? document.body : undefined; | ||
var portal = React.useMemo(function () { | ||
var node = containerEl == null ? void 0 : containerEl.ownerDocument.createElement("div"); | ||
if (node) node.className = PORTAL_CLASSNAME; | ||
return node; | ||
}, [containerEl]); | ||
var forceUpdate = (0, _hooks.useForceUpdate)(); | ||
(0, _hooks.useSafeLayoutEffect)(function () { | ||
forceUpdate(); | ||
}, []); | ||
(0, _hooks.useSafeLayoutEffect)(function () { | ||
if (!portal || !host) return; | ||
host.appendChild(portal); | ||
return function () { | ||
host.removeChild(portal); | ||
}; | ||
}, [portal, host]); | ||
if (host && portal) { | ||
return /*#__PURE__*/(0, _reactDom.createPortal)( /*#__PURE__*/React.createElement(PortalContextProvider, { | ||
value: appendToParentPortal ? portal : null | ||
}, children), portal); | ||
} | ||
return null; | ||
}; | ||
/** | ||
* Portal | ||
* | ||
* Declarative component used to render children into a DOM node | ||
* that exists outside the DOM hierarchy of the parent component. | ||
* | ||
* @see Docs https://chakra-ui.com/docs/components/portal | ||
*/ | ||
function Portal(props) { | ||
var containerRef = props.containerRef, | ||
rest = _objectWithoutPropertiesLoose(props, ["containerRef"]); | ||
return containerRef ? /*#__PURE__*/React.createElement(ContainerPortal, _extends({ | ||
containerRef: containerRef | ||
}, rest)) : /*#__PURE__*/React.createElement(DefaultPortal, rest); | ||
} | ||
Portal.className = "chakra-portal"; | ||
Portal.selector = "." + Portal.className; | ||
Portal.defaultProps = { | ||
appendToParentPortal: true | ||
}; | ||
Portal.className = PORTAL_CLASSNAME; | ||
Portal.selector = PORTAL_SELECTOR; | ||
@@ -98,0 +150,0 @@ if (_utils.__DEV__) { |
@@ -1,3 +0,7 @@ | ||
import { useCallbackRef, useForceUpdate, useSafeLayoutEffect } from "@chakra-ui/hooks"; | ||
import { createContext, __DEV__ } from "@chakra-ui/utils"; | ||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } | ||
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } | ||
import { useForceUpdate, useSafeLayoutEffect } from "@chakra-ui/hooks"; | ||
import { createContext, isBrowser, __DEV__ } from "@chakra-ui/utils"; | ||
import * as React from "react"; | ||
@@ -10,55 +14,43 @@ import { createPortal } from "react-dom"; | ||
}); | ||
var PORTAL_CLASSNAME = "chakra-portal"; | ||
var PORTAL_SELECTOR = ".chakra-portal"; | ||
var Container = props => { | ||
var { | ||
children, | ||
zIndex | ||
} = props; | ||
return /*#__PURE__*/React.createElement("div", { | ||
className: "chakra-portal-zIndex", | ||
style: { | ||
position: "relative", | ||
zIndex | ||
} | ||
}, children); | ||
}; | ||
var Container = props => /*#__PURE__*/React.createElement("div", { | ||
className: "chakra-portal-zIndex", | ||
style: { | ||
position: "absolute", | ||
zIndex: props.zIndex, | ||
top: 0, | ||
left: 0, | ||
right: 0 // NB: Don't add `bottom: 0`, it makes the entire app unusable | ||
// @see https://github.com/chakra-ui/chakra-ui/issues/3201 | ||
} | ||
}, props.children); | ||
/** | ||
* Portal | ||
* | ||
* Declarative component used to render children into a DOM node | ||
* that exists outside the DOM hierarchy of the parent component. | ||
* | ||
* @see Docs https://chakra-ui.com/docs/overlay/portal | ||
* Portal that uses `document.body` as container | ||
*/ | ||
export function Portal(props) { | ||
var DefaultPortal = props => { | ||
var { | ||
appendToParentPortal, | ||
children | ||
} = props; | ||
var tempNode = React.useRef(null); | ||
var portal = React.useRef(null); | ||
var forceUpdate = useForceUpdate(); | ||
var getContainer = useCallbackRef(props.getContainer); | ||
var onMount = useCallbackRef(props.onMount); | ||
var onUnmount = useCallbackRef(props.onUnmount); | ||
var parentPortal = usePortalContext(); | ||
var manager = usePortalManager(); | ||
useSafeLayoutEffect(() => { | ||
var _ref, _getContainer; | ||
if (!tempNode.current) return; | ||
var doc = tempNode.current.ownerDocument; | ||
var host = (_ref = (_getContainer = getContainer()) != null ? _getContainer : parentPortal) != null ? _ref : doc.body; | ||
/** | ||
* host may be `null` when a hot-loader | ||
* replaces components on the page | ||
*/ | ||
var host = appendToParentPortal ? parentPortal != null ? parentPortal : doc.body : doc.body; | ||
if (!host) return; | ||
portal.current = doc.createElement("div"); | ||
portal.current.className = Portal.className; | ||
portal.current.className = PORTAL_CLASSNAME; | ||
host.appendChild(portal.current); | ||
forceUpdate(); | ||
onMount(); | ||
var portalNode = portal.current; | ||
return () => { | ||
onUnmount(); | ||
if (host.contains(portalNode)) { | ||
@@ -69,13 +61,74 @@ host.removeChild(portalNode); | ||
}, []); | ||
var childrenToRender = manager != null && manager.zIndex ? /*#__PURE__*/React.createElement(Container, { | ||
zIndex: manager.zIndex | ||
}, props.children) : props.children; | ||
var _children = manager != null && manager.zIndex ? /*#__PURE__*/React.createElement(Container, { | ||
zIndex: manager == null ? void 0 : manager.zIndex | ||
}, children) : children; | ||
return portal.current ? /*#__PURE__*/createPortal( /*#__PURE__*/React.createElement(PortalContextProvider, { | ||
value: portal.current | ||
}, childrenToRender), portal.current) : /*#__PURE__*/React.createElement("span", { | ||
}, _children), portal.current) : /*#__PURE__*/React.createElement("span", { | ||
ref: tempNode | ||
}); | ||
}; | ||
/** | ||
* Portal that uses a custom container | ||
*/ | ||
var ContainerPortal = props => { | ||
var { | ||
children, | ||
containerRef, | ||
appendToParentPortal | ||
} = props; | ||
var containerEl = containerRef.current; | ||
var host = containerEl != null ? containerEl : isBrowser ? document.body : undefined; | ||
var portal = React.useMemo(() => { | ||
var node = containerEl == null ? void 0 : containerEl.ownerDocument.createElement("div"); | ||
if (node) node.className = PORTAL_CLASSNAME; | ||
return node; | ||
}, [containerEl]); | ||
var forceUpdate = useForceUpdate(); | ||
useSafeLayoutEffect(() => { | ||
forceUpdate(); | ||
}, []); | ||
useSafeLayoutEffect(() => { | ||
if (!portal || !host) return; | ||
host.appendChild(portal); | ||
return () => { | ||
host.removeChild(portal); | ||
}; | ||
}, [portal, host]); | ||
if (host && portal) { | ||
return /*#__PURE__*/createPortal( /*#__PURE__*/React.createElement(PortalContextProvider, { | ||
value: appendToParentPortal ? portal : null | ||
}, children), portal); | ||
} | ||
return null; | ||
}; | ||
/** | ||
* Portal | ||
* | ||
* Declarative component used to render children into a DOM node | ||
* that exists outside the DOM hierarchy of the parent component. | ||
* | ||
* @see Docs https://chakra-ui.com/docs/components/portal | ||
*/ | ||
export function Portal(props) { | ||
var { | ||
containerRef | ||
} = props, | ||
rest = _objectWithoutPropertiesLoose(props, ["containerRef"]); | ||
return containerRef ? /*#__PURE__*/React.createElement(ContainerPortal, _extends({ | ||
containerRef: containerRef | ||
}, rest)) : /*#__PURE__*/React.createElement(DefaultPortal, rest); | ||
} | ||
Portal.className = "chakra-portal"; | ||
Portal.selector = "." + Portal.className; | ||
Portal.defaultProps = { | ||
appendToParentPortal: true | ||
}; | ||
Portal.className = PORTAL_CLASSNAME; | ||
Portal.selector = PORTAL_SELECTOR; | ||
@@ -82,0 +135,0 @@ if (__DEV__) { |
import * as React from "react"; | ||
export interface PortalProps { | ||
/** | ||
* Function called when the portal mounts | ||
* The `ref` to the component where the portal will be attached to. | ||
*/ | ||
onMount?(): void; | ||
containerRef?: React.RefObject<HTMLElement | null>; | ||
/** | ||
* Function called when the portal unmounts | ||
* The content or node you'll like to portal | ||
*/ | ||
onUnmount?(): void; | ||
children: React.ReactNode; | ||
/** | ||
* Function that will be called to get the parent element | ||
* that the portal will be attached to. | ||
* If `true`, the portal will check if it is within a parent portal | ||
* and append itself to the parent's portal node. | ||
* This provides nesting for portals. | ||
* | ||
* If `false`, the portal will always append to `document.body` | ||
* regardless of nesting. It is used to opt out of portal nesting. | ||
*/ | ||
getContainer?: () => HTMLElement | null; | ||
/** | ||
* The content or node you'll like to portal | ||
*/ | ||
children: React.ReactNode; | ||
appendToParentPortal?: boolean; | ||
} | ||
@@ -27,6 +27,9 @@ /** | ||
* | ||
* @see Docs https://chakra-ui.com/docs/overlay/portal | ||
* @see Docs https://chakra-ui.com/docs/components/portal | ||
*/ | ||
export declare function Portal(props: PortalProps): JSX.Element; | ||
export declare namespace Portal { | ||
var defaultProps: { | ||
appendToParentPortal: boolean; | ||
}; | ||
var className: string; | ||
@@ -33,0 +36,0 @@ var selector: string; |
{ | ||
"name": "@chakra-ui/portal", | ||
"version": "1.0.6", | ||
"version": "1.1.0", | ||
"description": "React component used to render children outside the DOM hierarchy of the parent component", | ||
@@ -55,3 +55,3 @@ "keywords": [ | ||
"dependencies": { | ||
"@chakra-ui/hooks": "1.1.2", | ||
"@chakra-ui/hooks": "1.1.3", | ||
"@chakra-ui/utils": "1.1.0" | ||
@@ -58,0 +58,0 @@ }, |
# @chakra-ui/portal | ||
A wrapper for rendering components in React Portals, with support for nested | ||
portals and stacking. No need to use `z-index` at all with this portal, that's | ||
right! | ||
portals and stacking. | ||
@@ -41,4 +40,2 @@ ## Installation | ||
> It'll only render into `document.body` if you don't include `PortalManager`. | ||
```jsx | ||
@@ -65,12 +62,12 @@ <div> | ||
You can also portal elements into a custom containers. Simply pass a `container` | ||
prop that points to the `node` of that element. | ||
You can also portal elements into a custom containers. Simply pass a | ||
`containerRef` prop that points to the `node` of that element. | ||
```jsx | ||
<> | ||
<div data-testid="container" ref={ref} /> | ||
<Portal container={() => ref.current}> | ||
<h1 data-testid="heading">Hello world</h1> | ||
<div ref={ref} /> | ||
<Portal containerRef={ref}> | ||
<h1>Hello world</h1> | ||
</Portal> | ||
</> | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
48580
374
72
+ Added@chakra-ui/hooks@1.1.3(transitive)
- Removed@chakra-ui/hooks@1.1.2(transitive)
Updated@chakra-ui/hooks@1.1.3