1 | import React from 'react'
|
2 |
|
3 | import brace from 'brace'
|
4 | import AceEditor from 'react-ace'
|
5 | import Frame, { FrameContextConsumer } from 'react-frame-component'
|
6 |
|
7 | import 'brace/mode/jsx'
|
8 | import 'brace/theme/monokai'
|
9 | import ComponentRenderer from './component-renderer'
|
10 |
|
11 | window.component = null
|
12 |
|
13 | class 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
|
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 |
|
146 | export default (props) => {
|
147 | return (
|
148 | <Wrapper {...props} />
|
149 | )
|
150 | } |
\ | No newline at end of file |