|
|
@ -14,10 +14,9 @@ var IS_MOBILE = ( |
|
|
|
); |
|
|
|
|
|
|
|
var CodeMirrorEditor = React.createClass({ |
|
|
|
componentDidMount: function(root) { |
|
|
|
if (IS_MOBILE) { |
|
|
|
return; |
|
|
|
} |
|
|
|
componentDidMount: function() { |
|
|
|
if (IS_MOBILE) return; |
|
|
|
|
|
|
|
this.editor = CodeMirror.fromTextArea(this.refs.editor.getDOMNode(), { |
|
|
|
mode: 'javascript', |
|
|
|
lineNumbers: false, |
|
|
@ -26,15 +25,21 @@ var CodeMirrorEditor = React.createClass({ |
|
|
|
theme: 'solarized-light', |
|
|
|
readOnly: this.props.readOnly |
|
|
|
}); |
|
|
|
this.editor.on('change', this.onChange); |
|
|
|
this.onChange(); |
|
|
|
this.editor.on('change', this.handleChange); |
|
|
|
}, |
|
|
|
|
|
|
|
componentDidUpdate: function() { |
|
|
|
if (this.props.readOnly) { |
|
|
|
this.editor.setValue(this.props.codeText); |
|
|
|
} |
|
|
|
}, |
|
|
|
onChange: function() { |
|
|
|
if (this.props.onChange) { |
|
|
|
var content = this.editor.getValue(); |
|
|
|
this.props.onChange(content); |
|
|
|
|
|
|
|
handleChange: function() { |
|
|
|
if (!this.props.readOnly) { |
|
|
|
this.props.onChange && this.props.onChange(this.editor.getValue()); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
render: function() { |
|
|
|
// wrap in a div to fully contain CodeMirror
|
|
|
|
var editor; |
|
|
@ -46,7 +51,7 @@ var CodeMirrorEditor = React.createClass({ |
|
|
|
} |
|
|
|
|
|
|
|
return ( |
|
|
|
<div className={this.props.className}> |
|
|
|
<div style={this.props.style} className={this.props.className}> |
|
|
|
{editor} |
|
|
|
</div> |
|
|
|
); |
|
|
@ -67,7 +72,7 @@ var selfCleaningTimeout = { |
|
|
|
var ReactPlayground = React.createClass({ |
|
|
|
mixins: [selfCleaningTimeout], |
|
|
|
|
|
|
|
MODES: {XJS: 'XJS', JS: 'JS'}, //keyMirror({XJS: true, JS: true}),
|
|
|
|
MODES: {JSX: 'JSX', JS: 'JS'}, //keyMirror({JSX: true, JS: true}),
|
|
|
|
|
|
|
|
propTypes: { |
|
|
|
codeText: React.PropTypes.string.isRequired, |
|
|
@ -84,15 +89,19 @@ var ReactPlayground = React.createClass({ |
|
|
|
}, |
|
|
|
|
|
|
|
getInitialState: function() { |
|
|
|
return {mode: this.MODES.XJS, code: this.props.codeText}; |
|
|
|
return { |
|
|
|
mode: this.MODES.JSX, |
|
|
|
code: this.props.codeText, |
|
|
|
}; |
|
|
|
}, |
|
|
|
|
|
|
|
bindState: function(name) { |
|
|
|
return function(value) { |
|
|
|
var newState = {}; |
|
|
|
newState[name] = value; |
|
|
|
this.setState(newState); |
|
|
|
}.bind(this); |
|
|
|
handleCodeChange: function(value) { |
|
|
|
this.setState({code: value}); |
|
|
|
this.executeCode(); |
|
|
|
}, |
|
|
|
|
|
|
|
handleCodeModeSwitch: function(mode) { |
|
|
|
this.setState({mode: mode}); |
|
|
|
}, |
|
|
|
|
|
|
|
compileCode: function() { |
|
|
@ -100,25 +109,53 @@ var ReactPlayground = React.createClass({ |
|
|
|
}, |
|
|
|
|
|
|
|
render: function() { |
|
|
|
var content; |
|
|
|
if (this.state.mode === this.MODES.XJS) { |
|
|
|
content = |
|
|
|
<CodeMirrorEditor |
|
|
|
onChange={this.bindState('code')} |
|
|
|
className="playgroundStage" |
|
|
|
codeText={this.state.code} |
|
|
|
/>; |
|
|
|
} else if (this.state.mode === this.MODES.JS) { |
|
|
|
content = |
|
|
|
<div className="playgroundJS playgroundStage"> |
|
|
|
{this.compileCode()} |
|
|
|
</div>; |
|
|
|
} |
|
|
|
var isJS = this.state.mode === this.MODES.JS; |
|
|
|
var compiledCode = ''; |
|
|
|
try { |
|
|
|
compiledCode = this.compileCode(); |
|
|
|
} catch (err) {} |
|
|
|
|
|
|
|
// we're creating both versions, to avoid the flicker when switching from
|
|
|
|
// one view to another when CodeMirror recompiles
|
|
|
|
var jsContent = |
|
|
|
<CodeMirrorEditor |
|
|
|
style={{display: isJS ? 'inherit' : 'none'}} |
|
|
|
className="playgroundStage CodeMirror-readonly" |
|
|
|
onChange={this.handleCodeChange} |
|
|
|
codeText={compiledCode} |
|
|
|
readOnly={true} |
|
|
|
/>; |
|
|
|
|
|
|
|
var jsxContent = |
|
|
|
<CodeMirrorEditor |
|
|
|
style={{display: isJS ? 'none' : 'inherit'}} |
|
|
|
onChange={this.handleCodeChange} |
|
|
|
className="playgroundStage" |
|
|
|
codeText={this.state.code} |
|
|
|
/>; |
|
|
|
|
|
|
|
var JSXTabClassName = |
|
|
|
'playground-tab' + (isJS ? '' : ' playground-tab-active'); |
|
|
|
var JSTabClassName = |
|
|
|
'playground-tab' + (isJS ? ' playground-tab-active' : ''); |
|
|
|
|
|
|
|
return ( |
|
|
|
<div className="playground"> |
|
|
|
<div> |
|
|
|
<div |
|
|
|
className={JSXTabClassName} |
|
|
|
onClick={this.handleCodeModeSwitch.bind(this, this.MODES.JSX)}> |
|
|
|
Live JSX Editor |
|
|
|
</div> |
|
|
|
<div |
|
|
|
className={JSTabClassName} |
|
|
|
onClick={this.handleCodeModeSwitch.bind(this, this.MODES.JS)}> |
|
|
|
Compiled JS |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div className="playgroundCode"> |
|
|
|
{content} |
|
|
|
{jsxContent} |
|
|
|
{jsContent} |
|
|
|
</div> |
|
|
|
<div className="playgroundPreview"> |
|
|
|
<div ref="mount" /> |
|
|
@ -126,12 +163,19 @@ var ReactPlayground = React.createClass({ |
|
|
|
</div> |
|
|
|
); |
|
|
|
}, |
|
|
|
|
|
|
|
componentDidMount: function() { |
|
|
|
this.executeCode(); |
|
|
|
}, |
|
|
|
componentDidUpdate: function() { |
|
|
|
this.executeCode(); |
|
|
|
|
|
|
|
componentWillUpdate: function(nextProps, nextState) { |
|
|
|
// 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.state.mode === nextState.mode) { |
|
|
|
this.executeCode(); |
|
|
|
}; |
|
|
|
}, |
|
|
|
|
|
|
|
executeCode: function() { |
|
|
|
var mountNode = this.refs.mount.getDOMNode(); |
|
|
|
|
|
|
@ -149,10 +193,10 @@ var ReactPlayground = React.createClass({ |
|
|
|
} else { |
|
|
|
eval(compiledCode); |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
} catch (err) { |
|
|
|
this.setTimeout(function() { |
|
|
|
React.renderComponent( |
|
|
|
<div className="playgroundError">{e.toString()}</div>, |
|
|
|
<div className="playgroundError">{err.toString()}</div>, |
|
|
|
mountNode |
|
|
|
); |
|
|
|
}, 500); |
|
|
|