react/docs/_js/live_editor.js

223 lines
5.5 KiB
JavaScript
Raw Normal View History

2013-05-30 04:24:51 +08:00
var IS_MOBILE = (
navigator.userAgent.match(/Android/i)
|| navigator.userAgent.match(/webOS/i)
|| navigator.userAgent.match(/iPhone/i)
|| navigator.userAgent.match(/iPad/i)
|| navigator.userAgent.match(/iPod/i)
|| navigator.userAgent.match(/BlackBerry/i)
|| navigator.userAgent.match(/Windows Phone/i)
);
2013-05-30 03:46:11 +08:00
var CodeMirrorEditor = React.createClass({
propTypes: {
lineNumbers: React.PropTypes.bool,
onChange: React.PropTypes.func,
},
getDefaultProps: function() {
return {
lineNumbers: false,
};
},
2014-01-17 17:53:26 +08:00
componentDidMount: function() {
if (IS_MOBILE) return;
this.editor = CodeMirror.fromTextArea(React.findDOMNode(this.refs.editor), {
2013-05-30 03:46:11 +08:00
mode: 'javascript',
lineNumbers: this.props.lineNumbers,
lineWrapping: true,
smartIndent: false, // javascript mode does bad things with jsx indents
2013-05-30 03:46:11 +08:00
matchBrackets: true,
theme: 'solarized-light',
readOnly: this.props.readOnly,
2013-05-30 03:46:11 +08:00
});
2014-01-17 17:53:26 +08:00
this.editor.on('change', this.handleChange);
},
componentDidUpdate: function() {
if (this.props.readOnly) {
this.editor.setValue(this.props.codeText);
}
2013-05-30 03:46:11 +08:00
},
2014-01-17 17:53:26 +08:00
handleChange: function() {
if (!this.props.readOnly) {
this.props.onChange && this.props.onChange(this.editor.getValue());
2013-05-30 03:46:11 +08:00
}
},
2014-01-17 17:53:26 +08:00
2013-05-30 03:46:11 +08:00
render: function() {
// wrap in a div to fully contain CodeMirror
2013-05-30 04:24:51 +08:00
var editor;
if (IS_MOBILE) {
editor = <pre style={{overflow: 'scroll'}}>{this.props.codeText}</pre>;
} else {
editor = <textarea ref="editor" defaultValue={this.props.codeText} />;
2013-05-30 04:24:51 +08:00
}
2013-05-30 03:46:11 +08:00
return (
2014-01-17 17:53:26 +08:00
<div style={this.props.style} className={this.props.className}>
2013-05-30 04:24:51 +08:00
{editor}
2013-05-30 03:46:11 +08:00
</div>
);
},
2013-05-30 03:46:11 +08:00
});
var selfCleaningTimeout = {
componentDidUpdate: function() {
clearTimeout(this.timeoutID);
},
setTimeout: function() {
clearTimeout(this.timeoutID);
this.timeoutID = setTimeout.apply(null, arguments);
},
};
2013-05-30 03:46:11 +08:00
var ReactPlayground = React.createClass({
mixins: [selfCleaningTimeout],
2014-01-17 17:53:26 +08:00
MODES: {JSX: 'JSX', JS: 'JS'}, //keyMirror({JSX: true, JS: true}),
2013-05-30 03:46:11 +08:00
propTypes: {
codeText: React.PropTypes.string.isRequired,
transformer: React.PropTypes.func,
renderCode: React.PropTypes.bool,
showCompiledJSTab: React.PropTypes.bool,
showLineNumbers: React.PropTypes.bool,
editorTabTitle: React.PropTypes.string,
},
getDefaultProps: function() {
return {
transformer: function(code) {
return JSXTransformer.transform(code).code;
},
editorTabTitle: 'Live JSX Editor',
showCompiledJSTab: true,
showLineNumbers: false,
};
},
2013-05-30 03:46:11 +08:00
getInitialState: function() {
2014-01-17 17:53:26 +08:00
return {
mode: this.MODES.JSX,
code: this.props.codeText,
};
2013-05-30 03:46:11 +08:00
},
2014-01-17 17:53:26 +08:00
handleCodeChange: function(value) {
this.setState({code: value});
this.executeCode();
},
handleCodeModeSwitch: function(mode) {
this.setState({mode: mode});
2013-05-30 03:46:11 +08:00
},
compileCode: function() {
return this.props.transformer(this.state.code);
2013-05-30 03:46:11 +08:00
},
render: function() {
2014-01-17 17:53:26 +08:00
var isJS = this.state.mode === this.MODES.JS;
var compiledCode = '';
try {
compiledCode = this.compileCode();
} catch (err) {}
var JSContent =
2014-01-17 17:53:26 +08:00
<CodeMirrorEditor
key="js"
2014-01-17 17:53:26 +08:00
className="playgroundStage CodeMirror-readonly"
onChange={this.handleCodeChange}
codeText={compiledCode}
readOnly={true}
lineNumbers={this.props.showLineNumbers}
2014-01-17 17:53:26 +08:00
/>;
var JSXContent =
2014-01-17 17:53:26 +08:00
<CodeMirrorEditor
key="jsx"
2014-01-17 17:53:26 +08:00
onChange={this.handleCodeChange}
className="playgroundStage"
codeText={this.state.code}
lineNumbers={this.props.showLineNumbers}
2014-01-17 17:53:26 +08:00
/>;
var JSXTabClassName =
'playground-tab' + (isJS ? '' : ' playground-tab-active');
var JSTabClassName =
'playground-tab' + (isJS ? ' playground-tab-active' : '');
2013-05-30 03:46:11 +08:00
var JSTab =
<div
className={JSTabClassName}
onClick={this.handleCodeModeSwitch.bind(this, this.MODES.JS)}>
Compiled JS
</div>;
var JSXTab =
<div
className={JSXTabClassName}
onClick={this.handleCodeModeSwitch.bind(this, this.MODES.JSX)}>
{this.props.editorTabTitle}
</div>
2013-05-30 03:46:11 +08:00
return (
<div className="playground">
2014-01-17 17:53:26 +08:00
<div>
{JSXTab}
{this.props.showCompiledJSTab && JSTab}
2014-01-17 17:53:26 +08:00
</div>
<div className="playgroundCode">
{isJS ? JSContent : JSXContent}
2013-05-30 03:46:11 +08:00
</div>
<div className="playgroundPreview">
2013-05-30 03:46:11 +08:00
<div ref="mount" />
</div>
</div>
);
},
2014-01-17 17:53:26 +08:00
2013-05-30 03:46:11 +08:00
componentDidMount: function() {
this.executeCode();
},
2014-01-17 17:53:26 +08:00
componentDidUpdate: function(prevProps, prevState) {
2014-01-17 17:53:26 +08:00
// execute code only when the state's not being updated by switching tab
// this avoids re-displaying the error, which comes after a certain delay
if (this.props.transformer !== prevProps.transformer ||
this.state.code !== prevState.code) {
2014-01-17 17:53:26 +08:00
this.executeCode();
2014-01-18 09:42:53 +08:00
}
2013-05-30 03:46:11 +08:00
},
2014-01-17 17:53:26 +08:00
2013-05-30 03:46:11 +08:00
executeCode: function() {
var mountNode = React.findDOMNode(this.refs.mount);
2013-05-30 03:46:11 +08:00
try {
React.unmountComponentAtNode(mountNode);
2013-05-30 03:46:11 +08:00
} catch (e) { }
try {
var compiledCode = this.compileCode();
2013-05-31 00:13:27 +08:00
if (this.props.renderCode) {
React.render(
<CodeMirrorEditor codeText={compiledCode} readOnly={true} />,
2013-05-31 00:13:27 +08:00
mountNode
);
} else {
eval(compiledCode);
2013-05-31 00:13:27 +08:00
}
2014-01-17 17:53:26 +08:00
} catch (err) {
this.setTimeout(function() {
React.render(
2014-01-17 17:53:26 +08:00
<div className="playgroundError">{err.toString()}</div>,
mountNode
);
}, 500);
2013-05-30 03:46:11 +08:00
}
},
2013-05-30 03:46:11 +08:00
});