react-useportal
Advanced tools
Comparing version 1.0.3 to 1.0.4
@@ -1,2 +0,2 @@ | ||
import { DOMAttributes, EventHandler, SyntheticEvent, MutableRefObject } from 'react'; | ||
import { DOMAttributes, SyntheticEvent, MutableRefObject } from 'react'; | ||
declare type HTMLElRef = MutableRefObject<HTMLElement>; | ||
@@ -9,4 +9,4 @@ declare type CustomEvent = { | ||
declare type CustomEventHandler = (customEvent: CustomEvent) => void | HTMLElRef; | ||
declare type EventHandlers = { | ||
[K in keyof DOMAttributes<K>]?: EventHandler<SyntheticEvent<any, Event>>; | ||
declare type CustomEventHandlers = { | ||
[K in keyof DOMAttributes<K>]?: CustomEventHandler; | ||
}; | ||
@@ -18,8 +18,7 @@ declare type UsePortalOptions = { | ||
isOpen?: boolean; | ||
stateful?: boolean; | ||
onOpen?: CustomEventHandler; | ||
onClose?: CustomEventHandler; | ||
} & EventHandlers; | ||
} & CustomEventHandlers; | ||
export default function usePortal({ closeOnOutsideClick, closeOnEsc, bindTo, // attach the portal to this node in the DOM | ||
isOpen: defaultIsOpen, onOpen, onClose, ...eventHandlers }?: UsePortalOptions): any; | ||
export {}; |
@@ -45,3 +45,5 @@ "use strict"; | ||
const handleEvent = react_1.useCallback((func, event) => { | ||
if (event && event.currentTarget) | ||
if (!func || isServer) | ||
return; | ||
if (event && event.currentTarget && event.currentTarget !== document) | ||
targetEl.current = event.currentTarget; | ||
@@ -51,2 +53,9 @@ // i.e. onClick, etc. inside usePortal({ onClick({ portal, targetEl }) {} }) | ||
}, [portal, targetEl]); | ||
// this should handle all eventHandlers like onClick, onMouseOver, etc. passed into the config | ||
const customEventHandlers = Object | ||
.entries(eventHandlers) | ||
.reduce((acc, [handlerName, eventHandler]) => { | ||
acc[handlerName] = (event) => handleEvent(eventHandler, event); | ||
return acc; | ||
}, {}); | ||
const openPortal = react_1.useCallback((event) => { | ||
@@ -96,2 +105,4 @@ if (isServer) | ||
}, [isServer, closePortal, closeOnOutsideClick, portal]); | ||
// used to remove the event listeners on unmount | ||
const eventListeners = react_1.useRef({}); | ||
react_1.useEffect(() => { | ||
@@ -102,9 +113,30 @@ if (isServer) | ||
return; | ||
// TODO: eventually will need to figure out a better solution for this. | ||
// Surely we can find a way to map onScroll/onWheel -> scroll/wheel better, | ||
// but for all other event handlers. For now this works. | ||
const eventHandlerMap = { | ||
onScroll: 'scroll', | ||
onWheel: 'wheel', | ||
}; | ||
const node = portal.current; | ||
elToMountTo.appendChild(portal.current); | ||
document && document.addEventListener('keydown', handleKeydown); | ||
document && document.addEventListener('mousedown', handleOutsideMouseClick); | ||
// handles all special case handlers. Currently only onScroll and onWheel | ||
Object.entries(eventHandlerMap).forEach(([handlerName /* onScroll */, eventListenerName /* scroll */]) => { | ||
if (!eventHandlers[handlerName]) | ||
return; | ||
eventListeners.current[handlerName] = (e) => handleEvent(eventHandlers[handlerName], e); | ||
document.addEventListener(eventListenerName, eventListeners.current[handlerName]); | ||
}); | ||
document.addEventListener('keydown', handleKeydown); | ||
document.addEventListener('mousedown', handleOutsideMouseClick); | ||
return () => { | ||
document && document.removeEventListener('keydown', handleKeydown); | ||
document && document.removeEventListener('mousedown', handleOutsideMouseClick); | ||
// handles all special case handlers. Currently only onScroll and onWheel | ||
Object.entries(eventHandlerMap).forEach(([handlerName, eventListenerName]) => { | ||
if (!eventHandlers[handlerName]) | ||
return; | ||
document.removeEventListener(eventListenerName, eventListeners.current[handlerName]); | ||
delete eventListeners.current[handlerName]; | ||
}); | ||
document.removeEventListener('keydown', handleKeydown); | ||
document.removeEventListener('mousedown', handleOutsideMouseClick); | ||
elToMountTo.removeChild(node); | ||
@@ -118,9 +150,2 @@ }; | ||
}, [portal]); | ||
// this should handle all eventHandlers like onClick, onMouseOver, etc. passed into the config | ||
const customEventHandlers = Object | ||
.entries(eventHandlers) | ||
.reduce((acc, [handlerName, eventHandler]) => { | ||
acc[handlerName] = (event) => handleEvent(eventHandler, event); | ||
return acc; | ||
}, {}); | ||
return Object.assign([openPortal, closePortal, open.current, Portal, togglePortal], Object.assign(Object.assign({ isOpen: open.current, openPortal, ref: targetEl, closePortal, | ||
@@ -127,0 +152,0 @@ togglePortal, |
{ | ||
"name": "react-useportal", | ||
"version": "1.0.3", | ||
"version": "1.0.4", | ||
"homepage": "https://codesandbox.io/s/w6jp7z4pkk", | ||
@@ -38,2 +38,3 @@ "main": "dist/usePortal.js", | ||
"devDependencies": { | ||
"@testing-library/react-hooks": "^2.0.1", | ||
"@types/jest": "^24.0.18", | ||
@@ -53,3 +54,2 @@ "@types/react": "^16.9.2", | ||
"react-dom": "^16.8.6", | ||
"react-hooks-testing-library": "^0.6.0", | ||
"react-test-renderer": "^16.8.6", | ||
@@ -56,0 +56,0 @@ "react-testing-library": "^8.0.0", |
@@ -55,3 +55,3 @@ <p style="text-align: center;" align="center"> | ||
### Examples | ||
- [Modal 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)) | ||
- [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) | ||
@@ -152,3 +152,3 @@ - [Dropdown Example (useDropdown) - Next.js](https://codesandbox.io/s/useportal-usedropdown-587fo) | ||
### 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. It's important that you pass the `event` object to `openPortal` otherwise you will need to attach a ref to the clicked element. | ||
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. 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. | ||
@@ -191,4 +191,32 @@ ```jsx | ||
**Make sure you are passing the html synthetic event to the `openPortal`. i.e. `onClick={e => openPortal(e)}`** | ||
**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`, 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 */} | ||
<button ref={ref} onClick={() => openPortal()}> | ||
Open Portal | ||
</button> | ||
{isOpen && ( | ||
<Portal> | ||
<p> | ||
This Portal handles its own state.{' '} | ||
<button onClick={closePortal}>Close me!</button>, hit ESC or | ||
click outside of me. | ||
</p> | ||
</Portal> | ||
)} | ||
</> | ||
) | ||
} | ||
``` | ||
Options | ||
@@ -228,2 +256,3 @@ ----- | ||
------ | ||
- [ ] add correct 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 | ||
@@ -238,5 +267,2 @@ ```jsx | ||
- [ ] maybe have a `<Provider order={['Portal', 'openPortal']} />` then you can change the order of the array destructuring syntax | ||
- [ ] make work without requiring the html synthetic event | ||
- [ ] add example for tooltip (like [this one](https://codepen.io/davidgilbertson/pen/ooXVyw)) | ||
- [ ] add as many examples as possible 😊 | ||
- [ ] fix code so maintainability is A | ||
@@ -246,5 +272,2 @@ - [ ] set up code climate test coverage | ||
- [ ] add code climate test coverage badge | ||
- [X] document when you are required to have synthetic event | ||
- [X] make isomorphic | ||
- [X] continuous integration | ||
- [X] greenkeeper | ||
- [ ] add example to docs with using a `ref` instead of the `event` in `openPortal`, etc. |
Sorry, the diff of this file is not supported yet
27348
172
268