|
| 1 | +## Overview |
| 2 | + |
| 3 | +Portals provide a quick and easy way to render elements at any given point in the DOM hierarchy. This can be useful when positioning tooltips, modals, nav menus, or other elements that need to be positioned higher in the DOM but controlled or triggered from a deeply nested component. |
| 4 | + |
| 5 | +<div id="rc-portal-ex-overview"><div> |
| 6 | +### Portal |
| 7 | + |
| 8 | +```jsx |
| 9 | +const { useState } = require('react'); |
| 10 | +import Portal from '../portal'; |
| 11 | +import Button from '../button'; |
| 12 | +import Content from '../content'; |
| 13 | +import Heading from '../heading'; |
| 14 | +import Link from '../link'; |
| 15 | +import Text from '../text'; |
| 16 | + |
| 17 | +const [portalLocation, setPortalLocation] = useState(); |
| 18 | +const [portalActive, setPortalActive] = useState(false); |
| 19 | +const renderIn = target => { |
| 20 | + setPortalLocation(target); |
| 21 | + setPortalActive(!!target); |
| 22 | +}; |
| 23 | + |
| 24 | +const location = { |
| 25 | + 'ex-sibling': 'the sibling div', |
| 26 | + 'ex-overview': 'the Overview section', |
| 27 | + 'ex-parent': 'the parent', |
| 28 | +}; |
| 29 | +<> |
| 30 | + <div id="rc-portal-ex-parent" style={{ color: 'blue' }}> |
| 31 | + <div |
| 32 | + id="rc-portal-ex-sibling" |
| 33 | + style={{ |
| 34 | + color: 'MintCream', |
| 35 | + padding: '8px', |
| 36 | + backgroundColor: 'LightSlateGrey', |
| 37 | + marginBottom: '8px', |
| 38 | + }} |
| 39 | + > |
| 40 | + Sibling Element |
| 41 | + </div> |
| 42 | + <div style={{ color: 'red' }}> |
| 43 | + <Portal target={portalLocation} active={portalActive}> |
| 44 | + <h3> |
| 45 | + {portalLocation |
| 46 | + ? `I'm rendering in ${location[portalLocation]}!` |
| 47 | + : `I'm not rendering in a portal `} |
| 48 | + </h3> |
| 49 | + </Portal> |
| 50 | + </div> |
| 51 | + </div> |
| 52 | + <Button onClick={() => renderIn('ex-sibling')}>Render in sibling</Button> |
| 53 | + <Button onClick={() => renderIn('ex-overview')}>Render in Overview</Button> |
| 54 | + <Button onClick={() => renderIn('ex-parent')}>Render in parent</Button> |
| 55 | + <Button onClick={() => renderIn()}>Deactivate portal</Button> |
| 56 | +</>; |
| 57 | +``` |
| 58 | + |
| 59 | +## Variations |
| 60 | + |
| 61 | +By default, if the target id was not found within the DOM, a div will be created and appended to the root node of the application. The target id, style, and className are then applied to the newly created div. If the target div already exists, the portal's children are appended to it. |
| 62 | + |
| 63 | +```jsx |
| 64 | +const { useState } = require('react'); |
| 65 | +import Button from '../button'; |
| 66 | +import Portal from '../portal'; |
| 67 | + |
| 68 | +const [showMenu, setShowMenu] = useState(false); |
| 69 | +const [showMore, setShowMore] = useState(false); |
| 70 | + |
| 71 | +const menuStyle = { |
| 72 | + backgroundColor: 'lightSlateGrey', |
| 73 | + borderRadius: '4px', |
| 74 | + color: 'mintCream', |
| 75 | + height: 'fit-content', |
| 76 | + width: '90%', |
| 77 | + position: 'absolute', |
| 78 | + top: '25px', |
| 79 | + left: '5%', |
| 80 | + zIndex: '100', |
| 81 | + textAlign: 'center', |
| 82 | +}; |
| 83 | +<> |
| 84 | + <Button onClick={() => setShowMenu(!showMenu)}> |
| 85 | + {showMenu ? 'Close' : 'Render in'} menu |
| 86 | + </Button> |
| 87 | + <Portal active={showMenu} style={menuStyle} className="test"> |
| 88 | + <h3>I'm some menu content</h3> |
| 89 | + </Portal> |
| 90 | +
|
| 91 | + <Button onClick={() => setShowMore(!showMore)}> |
| 92 | + {showMenu && showMore ? 'Hide' : 'Show'} more content |
| 93 | + </Button> |
| 94 | + <Portal>{showMenu && showMore && <h3>I'm more content</h3>}</Portal> |
| 95 | +</>; |
| 96 | +``` |
| 97 | + |
| 98 | +## Related |
| 99 | + |
| 100 | +- [TooltipHoverArea](#/React%20Components/TooltipHoverArea) |
0 commit comments