/* @flow */ | |
import * as React from 'react'; | |
import { StyleSheet, css } from 'aphrodite'; | |
import Helmet from 'react-helmet'; | |
import debounce from 'lodash/debounce'; | |
import { renderOnClient } from '../common/ComponentUtils'; | |
import EditorSessionManager from '../utils/EditorSessionManager'; | |
import withThemeName from './theming/withThemeName'; | |
import colors from '../configs/colors'; | |
import type { Annotation } from '../utils/convertErrorToAnnotation'; | |
import type { TextFileEntry } from '../types'; | |
type Props = {| | |
entry: TextFileEntry, | |
annotations: Array<Annotation>, | |
onLoad?: Function, | |
onChangeCode: Function, | |
theme: string, | |
keybindings?: string, | |
|}; | |
type State = {| | |
height: string | number, | |
width: string | number, | |
|}; | |
@withThemeName | |
class BasicEditor extends React.PureComponent<Props, State> { | |
state = { | |
height: '100%', | |
width: '640px', | |
}; | |
componentDidMount() { | |
this._phantom.contentWindow.addEventListener('resize', this._handleResize); | |
this._handleResize(); | |
this._restoreSession(this.props.entry); | |
} | |
componentWillUpdate(nextProps: Props) { | |
if (this.props.entry.item.path !== nextProps.entry.item.path) { | |
this._saveSession(this.props.entry); | |
this._restoreSession(nextProps.entry); | |
} | |
} | |
componentWillUnmount() { | |
this._phantom.contentWindow.removeEventListener('resize', this._handleResize); | |
this._saveSession(this.props.entry); | |
} | |
_getMode = entry => (entry.item.path.endsWith('.json') ? 'json' : 'jsx'); | |
_saveSession = (entry: TextFileEntry) => { | |
EditorSessionManager.save(entry.item.path, this._editor.getSession()); | |
}; | |
_restoreSession = (entry: TextFileEntry) => { | |
const session = EditorSessionManager.create( | |
entry.item.path, | |
entry.item.content, | |
`ace/mode/${this._getMode(entry)}` | |
); | |
const editor = this._editor; | |
// Enable text wrapping | |
session.setUseWrapMode(true); | |
// Use 2 spaces for indentation | |
session.setOptions({ | |
tabSize: 2, | |
useSoftTabs: true, | |
}); | |
editor.setSession(session); | |
// Don't override Ctrl+L/Cmd+L | |
editor.commands.removeCommands(['gotoline']); | |
}; | |
_editor: any; | |
_phantom: any; | |
_adjustSoftWrap = (editor: any) => { | |
const characterWidth = editor.renderer.characterWidth; | |
const contentWidth = editor.renderer.scroller.clientWidth; | |
const session = editor.getSession(); | |
if (contentWidth > 0 && session.getUseWrapMode()) { | |
session.setWrapLimit(parseInt((contentWidth - 24) / characterWidth, 10)); | |
} | |
}; | |
_handleResize = debounce( | |
() => { | |
const size = this._phantom.getBoundingClientRect(); | |
this.setState( | |
{ | |
height: size.height, | |
width: size.width, | |
}, | |
() => { | |
if (this._editor) { | |
this._editor.resize(); | |
this._adjustSoftWrap(this._editor); | |
} | |
} | |
); | |
}, | |
100, | |
{ leading: true, trailing: true } | |
); | |
_handleLoad = (editor: any) => { | |
this._editor = editor; | |
if (this.props.onLoad) { | |
this.props.onLoad(editor); | |
} | |
}; | |
render() { | |
require('brace'); | |
const AceEditor = require('react-ace').default; | |
require('brace/mode/jsx'); | |
require('brace/mode/json'); | |
require('brace/ext/language_tools'); | |
require('brace/ext/searchbox'); | |
require('ayu-ace/light'); | |
require('ayu-ace/mirage'); | |
require('brace/keybinding/vim'); | |
const { onChangeCode, entry, annotations, theme } = this.props; | |
return ( | |
<div className={css(styles.container)}> | |
<iframe ref={c => (this._phantom = c)} type="text/html" className={css(styles.phantom)} /> | |
<AceEditor | |
enableBasicAutocompletion | |
enableLiveAutocompletion | |
focus | |
mode={this._getMode(entry)} | |
tabSize={2} | |
theme={theme === 'light' ? 'ayu-light' : 'ayu-mirage'} | |
onChange={onChangeCode} | |
name="code" | |
value={entry.item.content} | |
annotations={annotations} | |
editorProps={{ $blockScrolling: Infinity }} | |
showPrintMargin={false} | |
scrollMargin={[16, 0, 0, 0]} | |
style={{ | |
width: this.state.width, | |
height: this.state.height, | |
lineHeight: 1.5, | |
}} | |
className={css(styles.editor, styles.fill)} | |
onLoad={this._handleLoad} | |
keyboardHandler={this.props.editorMode === 'normal' ? null : this.props.editorMode} | |
/> | |
<Helmet | |
style={[ | |
{ | |
cssText: ` | |
/* Editor styles */ | |
.ace_gutter { | |
border-right: 1px solid ${colors.border}; !important; | |
background: none !important; | |
color: #999 !important; | |
} | |
.ace_gutter-cell.ace_error { | |
background-image: url(${require('../assets/cross-red.png')}) !important; | |
background-size: 16px !important; | |
} | |
.ace_search { | |
right: 0 !important; | |
bottom: 0 !important; | |
left: auto !important; | |
top: auto !important; | |
margin: 0 1em !important; | |
border-radius: 5px 5px 0 0 !important; | |
background-color: #fff !important; | |
border: 1px solid ${colors.border} !important; | |
border-bottom: 0 !important; | |
padding: 1em !important; | |
padding-bottom: 1em !important; | |
max-width: 339px !important; | |
} | |
.ace_search_form { | |
border-color: ${colors.border} !important; | |
} | |
.ace_search_field { | |
height: 28px !important; | |
width: 212px !important; | |
color: inherit; | |
} | |
.ace_searchbtn { | |
height: 28px !important; | |
width: 33px !important; | |
} | |
.ace_replacebtn { | |
height: 28px !important; | |
} | |
.ace_searchbtn_close { | |
display: none !important; | |
} | |
.ace_tooltip { | |
background-color: #fbfbfb !important; | |
background-image: none !important; | |
border-color: lightgray !important; | |
border-radius: 0 !important; | |
} | |
/* Theme specific */ | |
.ace_editor.ace_autocomplete.ace-ayu-mirage { | |
background-color: ${colors.ayu.mirage.background}; | |
border-color: ${colors.ayu.mirage.border}; | |
color: ${colors.ayu.mirage.text}; | |
} | |
.ace_editor.ace_autocomplete.ace-ayu-mirage .ace_completion-highlight { | |
color: #FFCC66; | |
} | |
.ace_editor.ace_autocomplete.ace-ayu-mirage .ace_marker-layer .ace_active-line, | |
.ace_editor.ace_autocomplete.ace-ayu-mirage .ace_marker-layer .ace_line-hover { | |
background-color: rgba(255, 255, 255, .1); | |
border-color: transparent; | |
} | |
.ace_editor.ace-ayu-mirage .ace_search { | |
background-color: ${colors.ayu.mirage.background} !important; | |
border-color: ${colors.ayu.mirage.border} !important; | |
} | |
.ace_editor.ace-ayu-mirage .ace_search_form, | |
.ace_editor.ace-ayu-mirage .ace_replace_form { | |
border-color: ${colors.ayu.mirage.border} !important; | |
} | |
.ace_editor.ace-ayu-mirage .ace_search_field, | |
.ace_editor.ace-ayu-mirage .ace_searchbtn, | |
.ace_editor.ace-ayu-mirage .ace_replacebtn, | |
.ace_editor.ace-ayu-mirage .ace_searchbtn_close { | |
background-color: ${colors.background.dark} !important; | |
border-color: ${colors.ayu.mirage.border} !important; | |
color: inherit; | |
} | |
.ace_editor.ace-ayu-mirage .ace_button { | |
background-color: ${colors.background.dark} !important; | |
color: inherit; | |
} | |
.ace_editor.ace-ayu-mirage .ace_button:not(.checked) { | |
border-color: ${colors.ayu.mirage.border} !important; | |
} | |
.ace_editor.ace-ayu-mirage .ace_tooltip { | |
background-color: ${colors.ayu.mirage.background} !important; | |
border-color: ${colors.ayu.mirage.border} !important; | |
color: ${colors.ayu.mirage.text}; | |
} | |
`, | |
}, | |
]} | |
/> | |
</div> | |
); | |
} | |
} | |
export default renderOnClient(BasicEditor); | |
const styles = StyleSheet.create({ | |
container: { | |
flex: 1, | |
display: 'flex', | |
position: 'relative', | |
overflow: 'hidden', | |
}, | |
fill: { | |
position: 'absolute', | |
left: 0, | |
right: 0, | |
top: 0, | |
bottom: 0, | |
}, | |
resizable: { | |
opacity: 0, | |
}, | |
editor: { | |
backgroundColor: 'transparent', | |
fontFamily: [ | |
'Fira Code', | |
'Fira Mono', | |
'Monaco', | |
'Menlo', | |
'Ubuntu Mono', | |
'Consolas', | |
'source-code-pro', | |
'monospace', | |
], | |
}, | |
phantom: { | |
display: 'block', | |
position: 'absolute', | |
left: 0, | |
top: 0, | |
height: '100%', | |
width: '100%', | |
pointerEvents: 'none', | |
opacity: 0, | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
very good work