Created
October 12, 2017 17:24
-
-
Save bvaughn/c65d556693fed3dbc6ab2025efac2c2b to your computer and use it in GitHub Desktop.
CodeEditor with JSX toggle (example)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (c) 2013-present, Facebook, Inc. | |
* | |
* @emails react-core | |
*/ | |
'use strict'; | |
import React, {Component} from 'react'; | |
import ReactDOM from 'react-dom'; | |
import Remarkable from 'remarkable'; | |
// TODO: switch back to the upstream version of react-live | |
// once https://github.com/FormidableLabs/react-live/issues/37 is fixed. | |
import {LiveProvider, LiveEditor} from '@gaearon/react-live'; | |
import {colors, media} from 'theme'; | |
import MetaTitle from 'templates/components/MetaTitle'; | |
// eslint-disable-next-line no-undef | |
const compileES5 = code => | |
Babel.transform(code, {presets: ['es2015', 'react']}).code; | |
// eslint-disable-next-line no-undef | |
const compileES6 = code => Babel.transform(code, {presets: ['react']}).code; | |
class CodeEditor extends Component { | |
constructor(props, context) { | |
super(props, context); | |
this.state = { | |
showJSX: true, | |
...this._updateState(props.code), | |
}; | |
} | |
componentDidMount() { | |
// Initial render() will always be a no-op, | |
// Because the mountNode ref won't exist yet. | |
this._render(); | |
} | |
componentDidUpdate(prevProps, prevState) { | |
if (prevState.compiledES5 !== this.state.compiledES5) { | |
this._render(); | |
} | |
} | |
render() { | |
const {children, code} = this.props; | |
const {compiledES6, error, showJSX} = this.state; | |
return ( | |
<LiveProvider code={showJSX ? code : compiledES6} mountStylesheet={false}> | |
<div | |
css={{ | |
[media.greaterThan('xlarge')]: { | |
display: 'flex', | |
flexDirection: 'row', | |
}, | |
[media.lessThan('large')]: { | |
display: 'block', | |
}, | |
}}> | |
{children && ( | |
<div | |
css={{ | |
flex: '0 0 33%', | |
[media.lessThan('xlarge')]: { | |
marginBottom: 20, | |
}, | |
'& h3': { | |
color: colors.dark, | |
maxWidth: '11em', | |
paddingTop: 0, | |
}, | |
'& p': { | |
marginTop: 15, | |
marginRight: 40, | |
lineHeight: 1.7, | |
[media.greaterThan('xlarge')]: { | |
marginTop: 25, | |
}, | |
}, | |
}}> | |
{children} | |
</div> | |
)} | |
<div | |
css={{ | |
[media.greaterThan('medium')]: { | |
flex: '0 0 67%', | |
display: 'flex', | |
alignItems: 'stretch', | |
flexDirection: 'row', | |
}, | |
[media.lessThan('small')]: { | |
display: 'block', | |
}, | |
}}> | |
<div | |
css={{ | |
flex: '0 0 70%', | |
overflow: 'hidden', | |
borderRadius: '10px 0 0 10px', | |
[media.lessThan('medium')]: { | |
borderRadius: '10px 10px 0 0', | |
}, | |
}}> | |
<div | |
css={{ | |
display: 'flex', | |
alignItems: 'center', | |
justifyContent: 'space-between', | |
padding: '0px 10px', | |
background: colors.darker, | |
color: colors.white, | |
}}> | |
<MetaTitle onDark={true}>Live Editor</MetaTitle> | |
<label | |
css={{ | |
fontSize: 14, | |
}}> | |
<input | |
checked={this.state.showJSX} | |
onChange={event => | |
this.setState({showJSX: event.target.checked})} | |
type="checkbox" | |
/>{' '} | |
JSX? | |
</label> | |
</div> | |
<div | |
css={{ | |
height: '100%', | |
width: '100%', | |
borderRadius: '0', | |
maxHeight: '340px !important', | |
marginTop: '0 !important', | |
marginLeft: '0 !important', | |
paddingLeft: '0 !important', | |
marginRight: '0 !important', | |
paddingRight: '0 !important', | |
marginBottom: '0 !important', | |
paddingBottom: '20px !important', | |
[media.lessThan('medium')]: { | |
marginBottom: '0 !important', | |
}, | |
'& pre.prism-code[contenteditable]': { | |
outline: 0, | |
overflow: 'auto', | |
marginRight: '0 !important', | |
marginBottom: '0 !important', | |
}, | |
}} | |
className="gatsby-highlight"> | |
<LiveEditor onChange={this._onChange} /> | |
</div> | |
</div> | |
{error && ( | |
<div | |
css={{ | |
flex: '0 0 30%', | |
overflow: 'hidden', | |
border: `1px solid ${colors.error}`, | |
borderRadius: '0 10px 10px 0', | |
fontSize: 12, | |
lineHeight: 1.5, | |
[media.lessThan('medium')]: { | |
borderRadius: '0 0 10px 10px', | |
}, | |
}}> | |
<div | |
css={{ | |
padding: '0px 10px', | |
background: colors.error, | |
color: colors.white, | |
}}> | |
<MetaTitle | |
cssProps={{ | |
color: colors.white, | |
}}> | |
Error | |
</MetaTitle> | |
</div> | |
<pre | |
css={{ | |
whiteSpace: 'pre-wrap', | |
wordBreak: 'break-word', | |
color: colors.error, | |
padding: 10, | |
}}> | |
{error.message} | |
</pre> | |
</div> | |
)} | |
{!error && ( | |
<div | |
css={{ | |
flex: '0 0 30%', | |
overflow: 'hidden', | |
border: `1px solid ${colors.divider}`, | |
borderRadius: '0 10px 10px 0', | |
[media.lessThan('medium')]: { | |
borderRadius: '0 0 10px 10px', | |
}, | |
}}> | |
<div | |
css={{ | |
padding: '0 10px', | |
backgroundColor: colors.divider, | |
}}> | |
<MetaTitle>Result</MetaTitle> | |
</div> | |
<div | |
css={{ | |
padding: 10, | |
maxHeight: '340px !important', | |
overflow: 'auto', | |
'& input': { | |
width: '100%', | |
display: 'block', | |
border: '1px solid #ccc', // TODO | |
padding: 5, | |
}, | |
'& button': { | |
marginTop: 10, | |
padding: '5px 10px', | |
}, | |
'& textarea': { | |
width: '100%', | |
marginTop: 10, | |
height: 60, | |
padding: 5, | |
}, | |
}} | |
ref={this._setMountRef} | |
/> | |
</div> | |
)} | |
</div> | |
</div> | |
</LiveProvider> | |
); | |
} | |
_render() { | |
if (!this._mountNode) { | |
return; | |
} | |
const {compiledES5} = this.state; | |
try { | |
// Example code requires React, ReactDOM, and Remarkable to be within scope. | |
// It also requires a "mountNode" variable for ReactDOM.render() | |
// eslint-disable-next-line no-new-func | |
new Function('React', 'ReactDOM', 'Remarkable', 'mountNode', compiledES5)( | |
React, | |
ReactDOM, | |
Remarkable, | |
this._mountNode, | |
); | |
} catch (error) { | |
console.error(error); | |
this.setState({ | |
compiledES5: null, | |
error, | |
}); | |
} | |
} | |
_setMountRef = ref => { | |
this._mountNode = ref; | |
}; | |
_updateState(code) { | |
try { | |
return { | |
compiledES5: compileES5(code), | |
compiledES6: compileES6(code), | |
error: null, | |
}; | |
} catch (error) { | |
console.error(error); | |
return { | |
compiledES5: null, | |
compiledES6: null, | |
error, | |
}; | |
} | |
} | |
_onChange = code => { | |
this.setState(this._updateState(code)); | |
}; | |
} | |
export default CodeEditor; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment