98 lines
2.1 KiB
JavaScript
98 lines
2.1 KiB
JavaScript
/* eslint-disable no-eval*/
|
|
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import ReactDOM from 'react-dom';
|
|
import { transform } from 'babel-standalone';
|
|
import * as ReactToolbox from 'react-toolbox';
|
|
import style from './style.css';
|
|
|
|
const ERROR_TIMEOUT = 500;
|
|
|
|
const Preview = React.createClass({
|
|
propTypes: {
|
|
className: PropTypes.string,
|
|
code: PropTypes.string.isRequired,
|
|
scope: PropTypes.object
|
|
},
|
|
|
|
getDefaultProps () {
|
|
return {
|
|
className: '',
|
|
scope: { React, ...ReactToolbox }
|
|
};
|
|
},
|
|
|
|
getInitialState () {
|
|
return {
|
|
error: null
|
|
};
|
|
},
|
|
|
|
componentDidMount () {
|
|
this.executeCode();
|
|
},
|
|
|
|
componentDidUpdate (prevProps) {
|
|
clearTimeout(this.timeoutID);
|
|
if (this.props.code !== prevProps.code) {
|
|
this.executeCode();
|
|
}
|
|
},
|
|
|
|
setTimeout () {
|
|
clearTimeout(this.timeoutID);
|
|
this.timeoutID = setTimeout(...arguments);
|
|
},
|
|
|
|
compileCode () {
|
|
const code = `
|
|
(function (${Object.keys(this.props.scope).join(', ')}, mountNode) {
|
|
${this.props.code}
|
|
});`;
|
|
|
|
return transform(code, {
|
|
presets: ['es2015', 'stage-0', 'react']
|
|
}).code;
|
|
},
|
|
|
|
buildScope (mountNode) {
|
|
return Object.keys(this.props.scope).map(key => this.props.scope[key]).concat(mountNode);
|
|
},
|
|
|
|
executeCode () {
|
|
const mountNode = this.refs.mount;
|
|
const scope = this.buildScope(mountNode);
|
|
|
|
try {
|
|
ReactDOM.unmountComponentAtNode(mountNode);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
|
|
try {
|
|
ReactDOM.render(eval(this.compileCode())(...scope), mountNode);
|
|
if (this.state.error) {
|
|
this.setState({ error: null });
|
|
}
|
|
} catch (err) {
|
|
this.setTimeout(() => {
|
|
this.setState({ error: err.toString() });
|
|
}, ERROR_TIMEOUT);
|
|
}
|
|
},
|
|
|
|
render () {
|
|
let className = style.preview;
|
|
if (this.props.className) className += ` ${this.props.className}`;
|
|
|
|
return (
|
|
<div className={className}>
|
|
{this.state.error !== null ? <span className={style.error}>{this.state.error}</span> : null}
|
|
<div ref="mount" className={style.content} />
|
|
</div>
|
|
);
|
|
}
|
|
});
|
|
|
|
export default Preview;
|