1- import * as lz from " lz-string" ;
1+ import * as lz from ' lz-string' ;
22import useDocusaurusContext from '@docusaurus/useDocusaurusContext' ;
3+ import { useEffect , useRef , useState } from 'react' ;
34
4- type Props = {
5+ type Props = {
56 autoplay ?: boolean ;
67 code : string ;
78 title ?: string ;
8- }
9+ mount ?: 'immediately' | 'visible' ;
10+ } ;
911
10- const style = {
12+ const rootStyle = {
1113 width : '100%' ,
1214 maxWidth : '640px' ,
1315 borderRadius : '12px' ,
@@ -17,24 +19,68 @@ const style = {
1719 display : 'block' ,
1820 height : '900px' ,
1921 border : 0 ,
20- overflow : 'hidden' ,
21- }
22+ overflow : 'hidden'
23+ } ;
24+
25+ const iframeStyle = {
26+ width : '100%' ,
27+ display : 'block' ,
28+ height : '100%' ,
29+ border : 0 ,
30+ overflow : 'hidden'
31+ } ;
32+
33+ export default ( props : Props ) => {
34+ const { autoplay = true , code, title, mount = 'immediately' } = props ;
35+
36+ const [ isReady , setIsReady ] = useState ( mount === 'immediately' ) ;
37+ const ref = useRef < HTMLDivElement > ( null ) ;
2238
23- export default ( { autoplay = true , code, title } : Props ) => {
2439 const params = new URLSearchParams ( {
2540 embed : 'true' ,
2641 code : lz . compressToEncodedURIComponent ( code )
2742 } ) ;
2843
2944 const { siteConfig } = useDocusaurusContext ( ) ;
3045 const { customFields } = siteConfig ;
31- const { playgroundUrl } = customFields ;
32-
46+ const { playgroundUrl } = customFields ;
47+
3348 if ( autoplay ) {
3449 params . set ( 'autoplay' , 'true' ) ;
3550 }
36-
51+
3752 const src = `${ playgroundUrl } /?${ params . toString ( ) } ` ;
3853
39- return < iframe src = { src } style = { style } title = { title } />
40- }
54+ useEffect ( ( ) => {
55+ if ( mount !== 'visible' ) {
56+ return ;
57+ }
58+
59+ const updateEntry = ( entries : IntersectionObserverEntry [ ] ) : void => {
60+ // This is a one-way gate:
61+ // Exiting the viewpoint will _not_ unmount the Playground
62+ if ( ! entries . every ( ( entry ) => entry . isIntersecting ) ) {
63+ return ;
64+ }
65+
66+ setIsReady ( true ) ;
67+ } ;
68+
69+ const node = ref ?. current ;
70+ const hasIOSupport = ! ! window . IntersectionObserver ;
71+
72+ if ( ! node || ! hasIOSupport ) return ;
73+
74+ // When 25% of the Playground is visible it is considered and mounted.
75+ const observer = new IntersectionObserver ( updateEntry , { threshold : 0.25 } ) ;
76+ observer . observe ( node ) ;
77+
78+ return ( ) => observer . disconnect ( ) ;
79+ } , [ mount ] ) ;
80+
81+ return (
82+ < div ref = { ref } style = { rootStyle } >
83+ { isReady ? < iframe src = { src } style = { iframeStyle } title = { title } /> : null }
84+ </ div >
85+ ) ;
86+ } ;
0 commit comments