-
Notifications
You must be signed in to change notification settings - Fork 443
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9f639b5
commit e8ae80e
Showing
8 changed files
with
234 additions
and
99 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
export const options = { | ||
value: '', | ||
mode: 'jsx', | ||
theme: 'react', | ||
keyMap: 'sublime', | ||
indentUnit: 2, | ||
lineNumbers: true, | ||
dragDrop: false, | ||
showCursorWhenSelecting: true, | ||
autoCloseBrackets: true, | ||
matchTags: { | ||
bothTags: true, | ||
}, | ||
extraKeys: { | ||
'Tab': 'indentMore', | ||
'Cmd-/': (cm) => { | ||
cm.listSelections().forEach(() => { | ||
cm.toggleComment({ lineComment: '//' }) | ||
}) | ||
}, | ||
}, | ||
} | ||
|
||
export const requireAddons = () => { | ||
require('codemirror/mode/jsx/jsx') | ||
require('codemirror/keymap/sublime') | ||
require('codemirror/addon/fold/xml-fold') // required for matchtags | ||
require('codemirror/addon/edit/matchtags') | ||
require('codemirror/addon/edit/closebrackets') | ||
require('codemirror/addon/comment/comment') | ||
require('codemirror/addon/selection/active-line') | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React, { Component } from 'react' | ||
import PropTypes from 'prop-types' | ||
import 'codemirror/lib/codemirror.css' | ||
import { options, requireAddons } from './codemirrorConfig' | ||
import './style.scss' | ||
|
||
export default class Editor extends Component { | ||
componentDidMount() { | ||
const { onChange, readOnly, value } = this.props | ||
|
||
requireAddons() | ||
const Codemirror = require('codemirror') | ||
this.cm = Codemirror(this.editor, Object.assign(options, { readOnly })) | ||
this.cm.on('changes', cm => { | ||
onChange && onChange(cm.getValue()) | ||
}) | ||
this.cm.setValue(value) | ||
} | ||
|
||
render() { | ||
return <div className="editor" ref={ref => (this.editor = ref)} /> | ||
} | ||
} | ||
|
||
Editor.propTypes = { | ||
onChange: PropTypes.func, | ||
value: PropTypes.string, | ||
readOnly: PropTypes.bool | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
.cm-s-react { | ||
font-family: 'source-code-pro', Menlo, 'Courier New', Consolas, monospace; | ||
font-size: 13px; | ||
line-height: 20px; | ||
color: #484848; | ||
} | ||
|
||
.cm-s-react .CodeMirror-linenumber { | ||
color: #D8D8D8; | ||
padding: 0 3px 0 3px; | ||
font-size: 10px; | ||
line-height: 22px; | ||
} | ||
|
||
.cm-s-react .CodeMirror-gutters { | ||
background: white; | ||
border-left: 4px solid rgba(238,238,238,1); | ||
border-right: 0px; | ||
} | ||
|
||
.cm-s-react span.cm-keyword { color: #1990B8; } | ||
.cm-s-react span.cm-atom { color: #C92C2C; } | ||
.cm-s-react span.cm-number { color: #C92C2C; } | ||
.cm-s-react span.cm-variable { color: black; } | ||
.cm-s-react span.cm-variable-2 { color: #0000C0; } | ||
.cm-s-react span.cm-variable-3 { color: #0000C0; } | ||
.cm-s-react span.cm-property { color: black; } | ||
.cm-s-react span.cm-operator { color: black; } | ||
.cm-s-react span.cm-comment { color: #7D8B99; } | ||
.cm-s-react span.cm-string { color: #2F9C0A; } | ||
.cm-s-react span.cm-string-2 { color: #2F9C0A; } | ||
.cm-s-react span.cm-link { color: #C92C2C; } | ||
|
||
.cm-s-react .CodeMirror-activeline-background { background: #e8f2ff; } | ||
.cm-s-react .CodeMirror-matchingtag { background: transparent; } | ||
.cm-s-react .cm-tag.CodeMirror-matchingtag:not(.cm-bracket) { text-decoration: underline; } | ||
|
||
@keyframes cm-line-warning { | ||
0% { background-color: white; } | ||
66% { background-color: white; } | ||
100% { background-color: #FFDADA; } | ||
} | ||
|
||
.cm-s-react .cm-line-error { | ||
background-color: #FFDADA; | ||
animation: cm-line-warning 0.5s; | ||
} | ||
|
||
/* read-only styles */ | ||
|
||
.read-only .CodeMirror, .read-only .CodeMirror-gutters { | ||
background: rgb(238,238,238); | ||
} | ||
|
||
.read-only .CodeMirror-linenumber { | ||
color: #BCBCBC; | ||
} | ||
|
||
.CodeMirror { | ||
height: auto; | ||
} | ||
.CodeMirror-gutters { | ||
z-index: 0; | ||
} | ||
.CodeMirror, .CodeMirror-gutters { | ||
background-color: #f9fafc !important; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,108 +1,127 @@ | ||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import PropTypes from 'prop-types'; | ||
import marked from 'marked'; | ||
import { transform } from 'babel-standalone'; | ||
import React from 'react' | ||
import ReactDOM from 'react-dom' | ||
import PropTypes from 'prop-types' | ||
import marked from 'marked' | ||
import { transform } from 'babel-standalone' | ||
import Editor from '../editor' | ||
|
||
export default class Canvas extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
super(props) | ||
|
||
this.playerId = `player-${parseInt(Math.random() * 1e9)}` | ||
this.document = this.props.children.match(/([^]*)\n?(```[^]+```)/) | ||
this.description = marked(this.document[1]) | ||
this.source = this.document[2].match(/```(.*)\n([^]+)```/) | ||
|
||
this.state = { | ||
showBlock: false | ||
}; | ||
} | ||
} | ||
|
||
componentDidMount() { | ||
this.renderSource(); | ||
} | ||
|
||
componentDidUpdate() { | ||
this.renderSource(); | ||
this.renderSource(this.source[2]) | ||
} | ||
|
||
getHeight() { | ||
return Math.max(this.refs.highlight.offsetHeight, this.refs.description && this.refs.description.offsetHeight || 0); | ||
get height() { | ||
return Math.max( | ||
this.refs.editor.offsetHeight, | ||
(this.refs.description && this.refs.description.offsetHeight) || 0 | ||
) | ||
} | ||
|
||
blockControl() { | ||
this.setState({ | ||
showBlock: !this.state.showBlock | ||
}); | ||
}) | ||
} | ||
|
||
renderSource() { | ||
if (this.shouldUpdate) { | ||
const div = this.refs.source; | ||
renderSource(value) { | ||
import('../../src') | ||
.then(Element => { | ||
const args = ['context', 'React', 'ReactDOM'] | ||
const argv = [this, React, ReactDOM] | ||
|
||
if (div instanceof HTMLElement) { | ||
require(['../../src'], Element => { | ||
const args = ['context', 'React'], argv = [this, React]; | ||
for (const key in Element) { | ||
args.push(key) | ||
argv.push(Element[key]) | ||
} | ||
|
||
for (const key in Element) { | ||
args.push(key); | ||
argv.push(Element[key]); | ||
return { | ||
args, | ||
argv | ||
} | ||
}) | ||
.then(({ args, argv }) => { | ||
const code = transform( | ||
` | ||
class Demo extends React.Component { | ||
${value} | ||
} | ||
ReactDOM.render(<Demo {...context.props} />, document.getElementById('${this.playerId}')) | ||
`, | ||
{ | ||
presets: ['es2015', 'react'] | ||
} | ||
).code | ||
|
||
args.push(this.component); | ||
args.push(code) | ||
|
||
ReactDOM.unmountComponentAtNode(div); | ||
ReactDOM.render(new Function(...args).apply(null, argv), div); | ||
}); | ||
} | ||
} | ||
new Function(...args).apply(null, argv) | ||
|
||
delete this.shouldUpdate; | ||
this.source[2] = value | ||
}) | ||
.catch(e => console.log(e)) | ||
} | ||
|
||
render() { | ||
const document = this.props.children.match(/([^]*)\n?(```[^]+```)/); | ||
const source = document[2].match(/```(.*)\n([^]+)```/); | ||
const description = marked(document[1]); | ||
const highlight = marked(document[2]); | ||
const component = transform(` | ||
class Demo extends React.Component { | ||
${source[2]} | ||
} | ||
__rtn = (function() { | ||
return <Demo {...context.props} /> | ||
})(); | ||
`, { | ||
presets: ['es2015', 'react'] | ||
}).code.replace('__rtn = ', 'return '); | ||
|
||
this.shouldUpdate = component != this.component || this.component === undefined; | ||
this.component = component; | ||
|
||
return ( | ||
<div className={`demo-block demo-box demo-${this.props.name}`}> | ||
<div className="source" ref="source"></div> | ||
<div className="meta" style={{ | ||
height: this.state.showBlock ? this.getHeight() : 0 | ||
}}> | ||
{description && <div ref="description" className="description" dangerouslySetInnerHTML={{ __html: description }}></div>} | ||
<div ref="highlight" className="highlight" dangerouslySetInnerHTML={{ __html: highlight }}></div> | ||
<div className="source" id={this.playerId} /> | ||
<div | ||
className="meta" | ||
style={{ | ||
height: this.state.showBlock ? this.height : 0 | ||
}} | ||
> | ||
{this.description && | ||
<div | ||
ref="description" | ||
className="description" | ||
dangerouslySetInnerHTML={{ __html: this.description }} | ||
/>} | ||
<div ref="editor"> | ||
<Editor | ||
value={this.source[2]} | ||
onChange={code => this.renderSource(code)} | ||
/> | ||
</div> | ||
|
||
</div> | ||
{ | ||
this.state.showBlock ? | ||
<div className="demo-block-control" onClick={this.blockControl.bind(this)}> | ||
<i className="el-icon-caret-top"></i><span>{this.props.locale.hide}</span> | ||
</div> | ||
: | ||
<div className="demo-block-control" onClick={this.blockControl.bind(this)}> | ||
<i className="el-icon-caret-bottom"></i><span>{this.props.locale.show}</span> | ||
{this.state.showBlock | ||
? <div | ||
className="demo-block-control" | ||
onClick={this.blockControl.bind(this)} | ||
> | ||
<i className="el-icon-caret-top" /> | ||
<span>{this.props.locale.hide}</span> | ||
</div> | ||
} | ||
: <div | ||
className="demo-block-control" | ||
onClick={this.blockControl.bind(this)} | ||
> | ||
<i className="el-icon-caret-bottom" /> | ||
<span>{this.props.locale.show}</span> | ||
</div>} | ||
</div> | ||
) | ||
} | ||
} | ||
|
||
Canvas.propTypes = { | ||
locale: PropTypes.object | ||
}; | ||
} | ||
|
||
Canvas.defaultProps = { | ||
locale: {} | ||
}; | ||
} |
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
Oops, something went wrong.