UNPKG

3.94 kBJSXView Raw
1import React from 'react'
2
3import brace from 'brace'
4import AceEditor from 'react-ace'
5import Frame, { FrameContextConsumer } from 'react-frame-component'
6
7import 'brace/mode/jsx'
8import 'brace/theme/monokai'
9import ComponentRenderer from './component-renderer'
10
11window.component = null
12
13class Wrapper extends React.Component {
14 constructor(props) {
15 super(props)
16 window.component = window.component || {}
17 this.iframeRef= React.createRef()
18 this.handleChange = this.handleChange.bind(this)
19 this.toggleEditor = this.toggleEditor.bind(this)
20 let { example } = props
21 example = example || 'return (<div>Example</div>)'
22 this.state = {
23 example,
24 height: 200,
25 showEditor: false,
26 }
27 this.executeScript(example)
28 }
29
30 executeScript(source) {
31 const { uniqId } = this.props
32 const script = document.createElement('script')
33 const self = this
34 script.onload = script.onerror = function() {
35 this.remove()
36 self.setState(state =>({
37 ...state,
38 component: window.component[uniqId] || '',
39 }))
40 }
41 const wrapper = `window.component['${uniqId}'] = (() => {
42 ${Object.keys(reactComponents).map(k => `const ${k} = reactComponents['${k}'];`).join('\n')}
43 try {
44 ${source}
45 } catch (error) {
46 console.log(error)
47 }
48 })()`
49 try {
50 const src = Babel.transform(wrapper, { presets: ['react', 'es2015'] }).code
51 script.src = 'data:text/plain;base64,' + btoa(src)
52 } catch (error) {
53 console.log(error)
54 }
55
56 document.body.appendChild(script)
57 }
58
59 handleChange(code) {
60 this.executeScript(code)
61 this.setState(state => ({
62 ...state,
63 example: code,
64 }))
65 }
66
67 computeHeight() {
68 const { height } = this.state
69 const padding = 5 // buffer for any unstyled margins
70 if (
71 this.iframeRef.current
72 && this.iframeRef.current.node.contentDocument
73 && this.iframeRef.current.node.contentDocument.body.offsetHeight !== 0
74 && this.iframeRef.current.node.contentDocument.body.offsetHeight !== (height - padding)
75 ) {
76 this.setState({
77 height: this.iframeRef.current.node.contentDocument.body.offsetHeight + padding,
78 })
79 }
80 }
81
82 componentDidUpdate() {
83 this.computeHeight()
84 }
85
86 componentDidMount() {
87 this.heightInterval = setInterval(() => {
88 this.computeHeight()
89 }, 1000)
90 }
91
92 componentWillUnmount() {
93 clearInterval(this.heightInterval)
94 }
95
96 toggleEditor(event) {
97 event.preventDefault()
98 this.setState(state => ({
99 ...state,
100 showEditor: !state.showEditor,
101 }))
102 }
103
104 render () {
105 const { component, height, showEditor } = this.state
106 return (
107 <div>
108 <Frame
109 className="component-wrapper"
110 ref={this.iframeRef}
111 style={{width: '100%', height }}
112 onLoad={this.computeHeight()}
113 >
114 <link type="text/css" rel="stylesheet" href="./build/entry.css" />
115 <FrameContextConsumer>
116 {
117 frameContext => (
118 <ComponentRenderer frameContext={frameContext}>
119 {component}
120 </ComponentRenderer>
121 )
122 }
123 </FrameContextConsumer>
124 </Frame>
125 <div className="bd__button">
126 <a href="#" onClick={this.toggleEditor}>Modify Example Code</a>
127 </div>
128 {showEditor ? (
129 <div className="field">
130 <AceEditor
131 style={{width: '100%', height: '200px', marginBottom: '20px'}}
132 value={this.state.example}
133 mode="jsx"
134 theme="monokai"
135 onChange={(code) => this.handleChange(code)}
136 name="editor-div"
137 editorProps={{ $useSoftTabs: true }}
138 />
139 </div>
140 ) : ''}
141 </div>
142 )
143 }
144}
145
146export default (props) => {
147 return (
148 <Wrapper {...props} />
149 )
150}
\No newline at end of file