Skip to content

Commit 2b2d441

Browse files
committed
Allow a fallback mount option, to allow keeping a video/etc alive even when no OutputPortal is mounted
1 parent 8f70bf2 commit 2b2d441

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

src/index.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const ELEMENT_TYPE_SVG = 'svg';
77

88
type BaseOptions = {
99
attributes?: { [key: string]: string };
10+
fallbackMountNode?: React.MutableRefObject<Node>;
1011
};
1112

1213
type HtmlOptions = BaseOptions & {
@@ -144,6 +145,16 @@ const createPortalNode = <C extends Component<any>>(
144145
parent = undefined;
145146
lastPlaceholder = undefined;
146147
}
148+
149+
if (parent === undefined && options?.fallbackMountNode?.current) {
150+
const newParent = options?.fallbackMountNode.current;
151+
152+
newParent.appendChild(
153+
portalNode.element
154+
);
155+
156+
parent = newParent;
157+
}
147158
}
148159
} as AnyPortalNode<C>;
149160

@@ -153,6 +164,7 @@ const createPortalNode = <C extends Component<any>>(
153164
interface InPortalProps {
154165
node: AnyPortalNode;
155166
children: React.ReactNode;
167+
keepMounted: boolean;
156168
}
157169

158170
class InPortal extends React.PureComponent<InPortalProps, { nodeProps: {} }> {

stories/html.stories.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,4 +408,117 @@ storiesOf('Portals', module)
408408
}
409409

410410
return <MyComponent componentToShow='component-a' />
411+
})
412+
.add('default to fallback element when not in use', () => {
413+
const StoryComponent = () => {
414+
const fallbackRef = React.useRef(undefined);
415+
const portalNode = React.useMemo(() => createHtmlPortalNode({ fallbackMountNode: fallbackRef }), []);
416+
const [showPortal, setShowPortal] = React.useState(true);
417+
418+
return <div>
419+
<div>
420+
<InPortal node={portalNode} keepMounted>
421+
<video src="https://media.giphy.com/media/l0HlKghz8IvrQ8TYY/giphy.mp4" controls loop autoPlay />
422+
</InPortal>
423+
424+
<p>
425+
The video below should continue playing in the background toggled.
426+
</p>
427+
428+
<button onClick={() => setShowPortal(!showPortal)}>
429+
Click to toggle the OutPortal
430+
</button>
431+
432+
<hr/>
433+
434+
<h2>Regular OutPortal</h2>
435+
<Container>
436+
{ showPortal === true && <OutPortal node={portalNode} /> }
437+
</Container>
438+
439+
<h2>Fallback:</h2>
440+
<Container>
441+
<div ref={fallbackRef}></div>
442+
</Container>
443+
</div>
444+
</div>;
445+
};
446+
447+
return <StoryComponent />;
448+
})
449+
.add('persist playback whilst dispalyed in a hidden element', () => {
450+
const StoryComponent = () => {
451+
const fallbackRef = React.useRef(undefined);
452+
const portalNode = React.useMemo(() => createHtmlPortalNode({ fallbackMountNode: fallbackRef }), []);
453+
const [showPortal, setShowPortal] = React.useState(true);
454+
455+
return <div>
456+
<div ref={fallbackRef} style={{display: 'none'}}></div>
457+
<div>
458+
<InPortal node={portalNode} keepMounted>
459+
<video src="https://media.giphy.com/media/l0HlKghz8IvrQ8TYY/giphy.mp4" controls loop autoPlay />
460+
</InPortal>
461+
462+
<p>
463+
The video below should continue playing in the background toggled.
464+
</p>
465+
466+
<button onClick={() => setShowPortal(!showPortal)}>
467+
Click to toggle the OutPortal
468+
</button>
469+
470+
<hr/>
471+
472+
<div>
473+
{ showPortal === true && <OutPortal node={portalNode} /> }
474+
</div>
475+
</div>
476+
</div>;
477+
};
478+
479+
return <StoryComponent />;
480+
})
481+
.add('combine fallback container and switching between two places', () => {
482+
const StoryComponent = () => {
483+
const fallbackRef = React.useRef(undefined);
484+
const portalNode = React.useMemo(() => createHtmlPortalNode({ fallbackMountNode: fallbackRef }), []);
485+
const [showPortal, setShowPortal] = React.useState(true);
486+
const [secondPortalSelected, setSecondPortalSelected] = React.useState(true);
487+
488+
return <div>
489+
<div ref={fallbackRef} style={{display: 'none'}}></div>
490+
<div>
491+
<InPortal node={portalNode} keepMounted>
492+
<video src="https://media.giphy.com/media/l0HlKghz8IvrQ8TYY/giphy.mp4" controls loop autoPlay />
493+
</InPortal>
494+
495+
<p>
496+
The video below should continue playing in the background toggled.
497+
</p>
498+
499+
<button onClick={() => setSecondPortalSelected(!secondPortalSelected)}>
500+
Click to switch the OutPortal
501+
</button>
502+
503+
<button onClick={() => setShowPortal(!showPortal)}>
504+
Click to toggle the OutPortals
505+
</button>
506+
507+
<hr/>
508+
509+
{ showPortal === true && <div>
510+
<Container>
511+
<h2>Portal 1:</h2>
512+
{ !secondPortalSelected && <OutPortal node={portalNode} /> }
513+
</Container>
514+
<Container>
515+
<h2>Portal 2:</h2>
516+
{ secondPortalSelected && <OutPortal node={portalNode} /> }
517+
</Container>
518+
</div> }
519+
</div>
520+
</div>;
521+
};
522+
523+
return <StoryComponent />;
411524
});

0 commit comments

Comments
 (0)