Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface PortalNodeBase<C extends Component<any>> {
// If an expected placeholder is provided, only unmount if that's still that was the
// latest placeholder we replaced. This avoids some race conditions.
unmount(expectedPlaceholder?: Node): void;

}
export interface HtmlPortalNode<C extends Component<any> = Component<any>> extends PortalNodeBase<C> {
element: HTMLElement;
Expand Down Expand Up @@ -159,8 +160,25 @@ class InPortal extends React.PureComponent<InPortalProps, { nodeProps: {} }> {
this.addPropsChannel();
}

componentDidUpdate() {
componentDidUpdate(previousProps: InPortalProps) {
this.addPropsChannel();
if(previousProps.node.element !== this.props.node.element){
Object.keys(window).forEach(key => {
if (/^on/.test(key)) {
const eventType = key.slice(2);
this.props.node.element.addEventListener(eventType, this.onEventHandler);
if(previousProps.node.element){
previousProps.node.element.removeEventListener(eventType, this.onEventHandler);
}
}
});

}
}

onEventHandler(e:any){
e.stopPropagation();
this.props.node.element.dispatchEvent(e);
}

render() {
Expand Down
46 changes: 46 additions & 0 deletions stories/html.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,5 +349,51 @@ storiesOf('Portals', module)
</div>;
}

return <MyComponent componentToShow='component-a' />
}).add('Events bubbling from PortalOut', () => {
const MyExpensiveComponent = () => <div onMouseDown={() => console.log('expensive')}>expensive!</div>;

const MyComponent = () => {
const portalNode = React.useMemo(() => createHtmlPortalNode(), []);

return <div>
{/*
Create the content that you want to move around.
InPortals render as normal, but to detached DOM.
Until this is used MyExpensiveComponent will not
appear anywhere in the page.
*/}
<div onClick={() => alert('InPortal wrapper click event')}>
<InPortal node={portalNode}>
<MyExpensiveComponent
// Optionally provide props to use before this enters the DOM
myProp={"defaultValue"}
/>
</InPortal>
</div>

{/* ... The rest of your UI ... */}

{/* Pass the node to whoever might want to show it: */}
<ComponentA portalNode={portalNode} />
</div>;
}

const ComponentA = (props) => {
return <div
onClick={() => alert('OutPortal wrapper click event')}
onMouseDown={() => console.log('Mouse Down')}
onMouseEnter={() => console.log('Mouse enter')}
>
{/* ... Some more UI ... */}

A:

<OutPortal
node={props.portalNode} // Show the content from this node here
/>
</div>;
}

return <MyComponent componentToShow='component-a' />
});