0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-04 10:22:38 +00:00

use CodeMirror Documents

Instead of re-building the whole editor box with every tab switch, we just swap in and out CodeMirror "documents".

Maintains undo history, scroll position, highlight coloring, etc.
This commit is contained in:
Trevor Buckner
2021-10-19 23:49:11 -04:00
parent b5ad75bcf2
commit 63d659ff49
3 changed files with 65 additions and 24 deletions

View File

@@ -60,6 +60,10 @@ const Editor = createClass({
window.removeEventListener('resize', this.updateEditorSize); window.removeEventListener('resize', this.updateEditorSize);
}, },
componentDidUpdate : function() {
this.highlightCustomMarkdown();
},
updateEditorSize : function() { updateEditorSize : function() {
if(this.refs.codeEditor) { if(this.refs.codeEditor) {
let paneHeight = this.refs.main.parentNode.clientHeight; let paneHeight = this.refs.main.parentNode.clientHeight;
@@ -177,25 +181,44 @@ const Editor = createClass({
this.refs.codeEditor?.updateSize(); this.refs.codeEditor?.updateSize();
}, },
//Called by CodeEditor after document switch, so Snippetbar can refresh UndoHistory
rerenderParent : function (){
this.forceUpdate();
},
renderEditor : function(){ renderEditor : function(){
if(this.isText()){ if(this.isText()){
return <CodeEditor key='text' return <>
ref='codeEditor' <CodeEditor key='codeEditor'
language='gfm' ref='codeEditor'
value={this.props.brew.text} language='gfm'
onChange={this.props.onTextChange} />; view={this.state.view}
value={this.props.brew.text}
onChange={this.props.onTextChange}
rerenderParent={this.rerenderParent} />
</>;
} }
if(this.isStyle()){ if(this.isStyle()){
return <CodeEditor key='style' return <>
ref='codeEditor' <CodeEditor key='codeEditor'
language='css' ref='codeEditor'
value={this.props.brew.style ?? DEFAULT_STYLE_TEXT} language='css'
onChange={this.props.onStyleChange} />; view={this.state.view}
value={this.props.brew.style ?? DEFAULT_STYLE_TEXT}
onChange={this.props.onStyleChange}
rerenderParent={this.rerenderParent} />
</>;
} }
if(this.isMeta()){ if(this.isMeta()){
return <MetadataEditor return <>
metadata={this.props.brew} <CodeEditor key='codeEditor'
onChange={this.props.onMetaChange} />; view={this.state.view}
style={{ display: 'none', width: '600px' }}
rerenderParent={this.rerenderParent} />
<MetadataEditor
metadata={this.props.brew}
onChange={this.props.onMetaChange} />
</>;
} }
}, },
@@ -212,7 +235,6 @@ const Editor = createClass({
}, },
render : function(){ render : function(){
this.highlightCustomMarkdown();
return ( return (
<div className='editor' ref='main'> <div className='editor' ref='main'>
<SnippetBar <SnippetBar
@@ -224,7 +246,7 @@ const Editor = createClass({
renderer={this.props.renderer} renderer={this.props.renderer}
undo={this.undo} undo={this.undo}
redo={this.redo} redo={this.redo}
historySize={this.historySize} /> historySize={this.historySize()} />
{this.renderEditor()} {this.renderEditor()}
</div> </div>

View File

@@ -63,11 +63,11 @@ const Snippetbar = createClass({
if(!this.props.showEditButtons) return; if(!this.props.showEditButtons) return;
return <div className='editors'> return <div className='editors'>
<div className={`editorTool undo ${this.props.historySize()?.undo ? 'active' : ''}`} <div className={`editorTool undo ${this.props.historySize.undo ? 'active' : ''}`}
onClick={this.props.undo} > onClick={this.props.undo} >
<i className='fas fa-undo' /> <i className='fas fa-undo' />
</div> </div>
<div className={`editorTool redo ${this.props.historySize()?.redo ? 'active' : ''}`} <div className={`editorTool redo ${this.props.historySize.redo ? 'active' : ''}`}
onClick={this.props.redo} > onClick={this.props.redo} >
<i className='fas fa-redo' /> <i className='fas fa-redo' />
</div> </div>

View File

@@ -25,25 +25,44 @@ const CodeEditor = createClass({
}; };
}, },
getInitialState : function() {
return {
docs : {}
};
},
componentDidMount : function() { componentDidMount : function() {
this.buildEditor(); this.buildEditor();
this.codeMirror.setValue(this.props.value);
this.codeMirror.clearHistory();
}, },
componentDidUpdate : function(prevProps) { componentDidUpdate : function(prevProps) {
if(prevProps.language !== this.props.language){ //rebuild editor when switching tabs if(prevProps.view !== this.props.view){ //view changed; swap documents
this.buildEditor(); let newDoc;
}
if(this.codeMirror && this.codeMirror.getValue() != this.props.value) { //update editor contents if brew.text is changed from outside if(!this.state.docs[this.props.view]) {
newDoc = CodeMirror.Doc(this.props.value, this.props.language);
} else {
newDoc = this.state.docs[this.props.view];
}
const oldDoc = { [prevProps.view]: this.codeMirror.swapDoc(newDoc) };
this.setState((prevState)=>({
docs : _.merge({}, prevState.docs, oldDoc)
}));
this.props.rerenderParent();
} else if(this.codeMirror?.getValue() != this.props.value) { //update editor contents if brew.text is changed from outside
this.codeMirror.setValue(this.props.value); this.codeMirror.setValue(this.props.value);
} }
}, },
buildEditor : function() { buildEditor : function() {
this.codeMirror = CodeMirror(this.refs.editor, { this.codeMirror = CodeMirror(this.refs.editor, {
value : this.props.value,
lineNumbers : true, lineNumbers : true,
lineWrapping : this.props.wrap, lineWrapping : this.props.wrap,
mode : this.props.language, //TODO: CSS MODE DOESN'T SEEM TO LOAD PROPERLY
indentWithTabs : true, indentWithTabs : true,
tabSize : 2, tabSize : 2,
historyEventDelay : 250, historyEventDelay : 250,
@@ -125,7 +144,7 @@ const CodeEditor = createClass({
//----------------------// //----------------------//
render : function(){ render : function(){
return <div className='codeEditor' ref='editor' />; return <div className='codeEditor' ref='editor' style={this.props.style}/>;
} }
}); });