11import  React  from  'react' ; 
22import  ReactDOM  from  'react-dom' ; 
3+ import  PropTypes  from  'prop-types' ; 
4+ import  classNames  from  'classnames/bind' ; 
5+ 6+ import  styles  from  './index.css' ; 
7+ const  cx  =  classNames . bind ( styles ) ; 
38
49class  IFramePlayground  extends  React . Component  { 
510 ref  =  React . createRef ( ) ;  // <iframe> ref 
611 state  =  { 
712 container : null ,  // container within which the user content should be rendered 
13+  width : 0 ,  // iframe width 
814 height : 0 ,  // iframe height 
15+  isResizing : false ,  // is the user currently resizing IFramePlayground? 
16+  direction : 'horizontal' ,  // resizing direction 
917 } ; 
1018
1119 /** 
@@ -29,13 +37,23 @@ class IFramePlayground extends React.Component {
2937 } ) ; 
3038 } ; 
3139
32-  updateHeight  =  iFrameNode  =>  { 
33-  const  children  =  Array . from ( iFrameNode . contentDocument . body . childNodes ) ; 
34-  const  height  =  children . reduce ( 
35-  ( prevVal ,  child )  =>  prevVal  +  child . offsetHeight , 
40+  /** Once <iframe> is fully loaded, we can then determine its size */ 
41+  setSize  =  iFrameNode  =>  { 
42+  const  {  enableResizing }  =  this . props ; 
43+ 44+  // Determine width 
45+  const  parentNode  =  iFrameNode . parentNode ; 
46+  const  width  =  enableResizing  ? parentNode . offsetWidth  : '100%' ; 
47+ 48+  // Determine height 
49+  const  childNodes  =  Array . from ( iFrameNode . contentDocument . body . childNodes ) ; 
50+  const  height  =  childNodes . reduce ( 
51+  ( prevVal ,  childNode )  =>  prevVal  +  childNode . offsetHeight , 
3652 0 , 
3753 ) ; 
54+ 3855 this . setState ( { 
56+  width, 
3957 height, 
4058 } ) ; 
4159 } ; 
@@ -51,31 +69,90 @@ class IFramePlayground extends React.Component {
5169 container : iFrameNode . contentDocument . body , 
5270 } ) ; 
5371 this . copyStyles ( iFrameNode ) ; 
54-  this . updateHeight ( iFrameNode ) ; 
72+  this . setSize ( iFrameNode ) ; 
73+  } 
74+  } ; 
75+ 76+  handleResizeStart  =  ( e ,  direction )  =>  { 
77+  e . preventDefault ( ) ; 
78+  window . addEventListener ( 'mousemove' ,  this . handleResize ) ; 
79+  window . addEventListener ( 'mouseup' ,  this . handleResizeStop ) ; 
80+  this . setState ( { 
81+  isResizing : true , 
82+  direction, 
83+  } ) ; 
84+  } ; 
85+ 86+  handleResize  =  e  =>  { 
87+  const  {  minWidth,  minHeight }  =  this . props ; 
88+  const  {  direction,  width,  height }  =  this . state ; 
89+  if  ( direction  ===  'vertical' )  { 
90+  const  newHeight  =  height  +  e . movementY ; 
91+  this . setState ( { 
92+  height : newHeight  <  minHeight  ? minHeight  : newHeight , 
93+  } ) ; 
94+  }  else  { 
95+  const  newWidth  =  width  +  e . movementX ; 
96+  this . setState ( { 
97+  width : newWidth  <  minWidth  ? minWidth  : newWidth , 
98+  } ) ; 
5599 } 
56100 } ; 
57101
102+  handleResizeStop  =  ( )  =>  { 
103+  this . removeEventListeners ( ) ; 
104+  this . setState ( { 
105+  isResizing : false , 
106+  } ) ; 
107+  } ; 
108+ 109+  removeEventListeners  =  ( )  =>  { 
110+  window . removeEventListener ( 'mousemove' ,  this . handleResize ) ; 
111+  window . removeEventListener ( 'mouseup' ,  this . handleResizeStop ) ; 
112+  } ; 
113+ 58114 render ( )  { 
59-  const  {  children,  style }  =  this . props ; 
60-  const  {  container,  height }  =  this . state ; 
115+  const  {  children,  style,  enableResizing  }  =  this . props ; 
116+  const  {  container,  width , height,  isResizing  }  =  this . state ; 
61117 const  iFrameNode  =  this . ref . current ; 
62118 return  ( 
63-  < iframe 
64-  sandbox = "allow-same-origin" 
65-  ref = { this . ref } 
66-  srcDoc = { '<!DOCTYPE html>' } 
67-  style = { { 
68-  height, 
69-  width : '100%' , 
70-  border : 'none' , 
71-  ...style , 
72-  } } 
73-  > 
74-  { container  && 
75-  iFrameNode  && 
76-  iFrameNode . contentDocument  && 
77-  ReactDOM . createPortal ( children ,  container ) } 
78-  </ iframe > 
119+  < div  className = { cx ( 'playground' ) } > 
120+  < iframe 
121+  sandbox = "allow-same-origin" 
122+  ref = { this . ref } 
123+  srcDoc = { '<!DOCTYPE html>' } 
124+  className = { cx ( 'iframe' ) } 
125+  style = { { 
126+  width, 
127+  height, 
128+  pointerEvents : isResizing  ? 'none'  : 'all' , 
129+  ...style , 
130+  } } 
131+  > 
132+  { container  && 
133+  iFrameNode  && 
134+  iFrameNode . contentDocument  && 
135+  ReactDOM . createPortal ( children ,  container ) } 
136+  </ iframe > 
137+  { enableResizing  &&  ( 
138+  < React . Fragment > 
139+  < div 
140+  className = { cx ( 'resize-controller' ,  'height-resize-controller' ) } 
141+  onMouseDown = { e  =>  this . handleResizeStart ( e ,  'vertical' ) } 
142+  style = { { 
143+  width, 
144+  } } 
145+  /> 
146+  < div 
147+  className = { cx ( 'resize-controller' ,  'width-resize-controller' ) } 
148+  onMouseDown = { e  =>  this . handleResizeStart ( e ,  'horizontal' ) } 
149+  style = { { 
150+  left : width , 
151+  } } 
152+  /> 
153+  </ React . Fragment > 
154+  ) } 
155+  </ div > 
79156 ) ; 
80157 } 
81158
@@ -85,6 +162,26 @@ class IFramePlayground extends React.Component {
85162 iFrameNode . addEventListener ( 'load' ,  this . handleLoad ) ; 
86163 } 
87164 } 
165+ 166+  componentWillMount ( )  { 
167+  this . removeEventListeners ( ) ; 
168+  } 
88169} 
89170
171+ IFramePlayground . propTypes  =  { 
172+  /** 
173+  * If enabled, resize controller will be available. 
174+  * You can use it to control `<frame>` viewport size instead of using Docz Playground resize controller 
175+  * */ 
176+  enableResizing : PropTypes . bool , 
177+  minWidth : PropTypes . number , 
178+  minHeight : PropTypes . number , 
179+ } ; 
180+ 181+ IFramePlayground . defaultProps  =  { 
182+  enableResizing : false , 
183+  minWidth : 200 , 
184+  minHeight : 200 , 
185+ } ; 
186+ 90187export  default  IFramePlayground ; 
0 commit comments