Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/alex-cory/react-useportal
🌀 React hook for Portals
https://github.com/alex-cory/react-useportal
dialog dropdown javascript lightbox loading-bar modal notification portal react react-hooks react-portal tooltip
Last synced: 6 days ago
JSON representation
🌀 React hook for Portals
- Host: GitHub
- URL: https://github.com/alex-cory/react-useportal
- Owner: alex-cory
- License: mit
- Created: 2019-02-20T03:20:26.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2024-08-14T06:36:50.000Z (4 months ago)
- Last Synced: 2024-11-24T18:32:23.624Z (18 days ago)
- Topics: dialog, dropdown, javascript, lightbox, loading-bar, modal, notification, portal, react, react-hooks, react-portal, tooltip
- Language: TypeScript
- Homepage:
- Size: 5.21 MB
- Stars: 891
- Watchers: 7
- Forks: 34
- Open Issues: 44
-
Metadata Files:
- Readme: README.md
- License: license.md
Awesome Lists containing this project
- fucking-awesome-react-hooks - `react-useportal`
- awesome-react-hooks-cn - `react-useportal`
- awesome-react-hooks - `react-useportal`
- awesome-react-hooks - `react-useportal`
- awesome-react-hooks - react-useportal - 🌀 React hook for Portals (Packages)
README
usePortal
🌀 React hook for using Portals
Need to make dropdowns, lightboxes/modals/dialogs, global message notifications, or tooltips in React? React Portals provide a first-class way to render children into a DOM node that exists outside the DOM hierarchy of the parent component ([react docs](https://reactjs.org/docs/portals.html)).
This hook is also isomorphic, meaning it works with SSR (server side rendering).
Features
--------
- SSR (server side rendering) support
- TypeScript support
- 1 dependency ([use-ssr](https://github.com/alex-cory/use-ssr))
- Built in state### Examples
- [SSR Example - Next.js - codesandbox container](https://codesandbox.io/s/useportal-in-nextjs-codesandbox-container-9rm5o) (sometimes buggy, if so try [this example](https://codesandbox.io/s/useportal-in-nextjs-ux9nb))
- [Modal Example (useModal) - create-react-app](https://codesandbox.io/s/w6jp7z4pkk)
- [Dropdown Example (useDropdown) - Next.js](https://codesandbox.io/s/useportal-usedropdown-587fo)
- [Tooltip Example (useTooltip) - Next.js](https://codesandbox.io/s/useportal-usedropdown-dgesf)Installation
------------```shell
yarn add react-useportal or npm i -S react-useportal
```Usage
-----### Stateless
```jsx
import usePortal from 'react-useportal'const App = () => {
const { Portal } = usePortal()return (
This text is portaled at the end of document.body!
)
}const App = () => {
const { Portal } = usePortal({
bindTo: document && document.getElementById('san-francisco')
})return (
This text is portaled into San Francisco!
)
}
```### With State
```jsx
import usePortal from 'react-useportal'const App = () => {
var { openPortal, closePortal, isOpen, Portal } = usePortal()// want to use array destructuring? You can do that too
var [openPortal, closePortal, isOpen, Portal] = usePortal()return (
<>
Open Portal
{isOpen && (
This Portal handles its own state.{' '}
Close me!, hit ESC or
click outside of me.
)}
>
)
}
```### Need Animations?
```jsx
import usePortal from 'react-useportal'const App = () => {
const { openPortal, closePortal, isOpen, Portal } = usePortal()
return (
<>
Open Portal
This Portal handles its own state.{' '}
Close me!, hit ESC or
click outside of me.
>
)
}
```### Customizing the Portal directly
By using `onOpen`, `onClose` or any other event handler, you can modify the `Portal` and return it. See [useDropdown](https://codesandbox.io/s/useportal-usedropdown-587fo) for a working example. If opening the portal from a click event it's important that you pass the `event` object to `openPortal` and `togglePortal` otherwise you will need to attach a `ref` to the clicked element (if you want to be able to open the portal without passing an event you will need to set `programmaticallyOpen` to `true`).```jsx
const useModal = () => {
const { isOpen, openPortal, togglePortal, closePortal, Portal } = usePortal({
onOpen({ portal }) {
portal.current.style.cssText = `
/* add your css here for the Portal */
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
z-index: 1000;
`
}
})return {
Modal: Portal,
openModal: openPortal,
toggleModal: togglePortal,
closeModal: closePortal,
isOpen
}
}const App = () => {
const { openModal, closeModal, isOpen, Modal } = useModal()
return <>
openModal(e)}>Open Modal
{isOpen && (
This will dynamically center to the middle of the screen regardless of the size of what you put in here
)}
>
}
```**Make sure you are passing the html synthetic event to the `openPortal` and `togglePortal` . i.e. `onClick={e => openPortal(e)}`**
### Usage with a `ref`
If for some reason, you don't want to pass around the `event` to `openPortal` or `togglePortal` and you're not using `programmaticallyOpen`, you can use a `ref` like this.
```jsx
import usePortal from 'react-useportal'const App = () => {
var { ref, openPortal, closePortal, isOpen, Portal } = usePortal()return (
<>
{/* see below how I don't have to pass the event if I use the ref */}
openPortal()}>
Open Portal
{isOpen && (
This Portal handles its own state.{' '}
Close me!, hit ESC or
click outside of me.
)}
>
)
}
```Options
-----
| Option | Description |
| --------------------- | ---------------------------------------------------------------------------------------- |
| `closeOnOutsideClick` | This will close the portal when not clicking within the portal. Default is `true` |
| `closeOnEsc` | This will allow you to hit ESC and it will close the modal. Default is `true` |
| `bindTo` | This is the DOM node you want to attach the portal to. By default it attaches to `document.body` |
| `isOpen` | This will be the default for the portal. Default is `false` |
| `onOpen` | This is used to call something when the portal is opened and to modify the css of the portal directly |
| `onClose` | This is used to call something when the portal is closed and to modify the css of the portal directly |
| `onPortalClick` | This is fired whenever clicking on the `Portal` |
| html event handlers (i.e. `onClick`) | These can be used instead of `onOpen` to modify the css of the portal directly. [`onMouseEnter` and `onMouseLeave` example](https://codesandbox.io/s/useportal-usedropdown-dgesf) |
| `programmaticallyOpen` | This option allows you to open or toggle the portal without passing in an event. Default is `false` |### Option Usage
```js
const {
openPortal,
closePortal,
togglePortal,
isOpen,
Portal,
// if you don't pass an event to openPortal, closePortal, or togglePortal and you're not using programmaticallyOpen, you will need
// to put this on the element you want to interact with/click
ref,
// if for some reason you want to interact directly with the portal, you can with this ref
portalRef,
} = usePortal({
closeOnOutsideClick: true,
closeOnEsc: true,
bindTo, // attach the portal to this node in the DOM
isOpen: false,
// `event` has all the fields that a normal `event` would have such as `event.target.value`, etc.
// with the additional `portal` and `targetEl` added to it as seen in the examples below
onOpen: (event) => {
// can access: event.portal, event.targetEl, event.event, event.target, etc.
},
// `onClose` will not have an `event` unless you pass an `event` to `closePortal`
onClose({ portal, targetEl, event }) {},
// `targetEl` is the element that you either are attaching a `ref` to
// or that you are putting `openPortal` or `togglePortal` or `closePortal` on
onPortalClick({ portal, targetEl, event }) {},
// in addition, any event handler such as onClick, onMouseOver, etc will be handled the same
onClick({ portal, targetEl, event }) {}
})
```
Todos
------
- [ ] React Native support. [1](https://github.com/zenyr/react-native-portal) [2](https://github.com/cloudflare/react-gateway) [3](https://medium.com/@naorzruk/portals-in-react-native-22797ba8aa1b) [4](https://stackoverflow.com/questions/46505378/can-we-have-react-16-portal-functionality-react-native) [5](https://github.com/callstack/react-native-paper/blob/master/src/components/Portal/PortalManager.tsx) Probably going to have to add a `Provider`...
- [ ] add correct typescript return types
- [ ] add support for popup windows [resource 1](https://javascript.info/popup-windows) [resource 2](https://hackernoon.com/using-a-react-16-portal-to-do-something-cool-2a2d627b0202). Maybe something like
```jsx
const { openPortal, closePortal, isOpen, Portal } = usePortal({
popup: ['', '', 'width=600,height=400,left=200,top=200']
})
// window.open('', '', 'width=600,height=400,left=200,top=200')
```
- [ ] tests (priority)
- [ ] maybe have a `` then you can change the order of the array destructuring syntax
- [ ] fix code so maintainability is A
- [ ] set up code climate test coverage
- [ ] optimize badges [see awesome badge list](https://github.com/boennemann/badges)
- [ ] add code climate test coverage badge