import React, { Component } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import CodeMirror, { EditorChange } from 'codemirror'; import { Base16Theme } from 'base16'; import { defaultStyle, themedStyle } from './styles'; import { Theme } from '../themes/default'; import 'codemirror/mode/javascript/javascript'; import 'codemirror/addon/fold/foldgutter'; import 'codemirror/addon/fold/foldcode'; import 'codemirror/addon/fold/brace-fold'; import '../../fonts/index.css'; import 'codemirror/lib/codemirror.css'; import 'codemirror/addon/fold/foldgutter.css'; const EditorContainer = styled.div( '' as unknown as TemplateStringsArray, ({ theme }: { theme: Theme }) => theme.scheme === 'default' && theme.light ? defaultStyle : themedStyle(theme), ); export interface EditorProps { value: string; mode: string; lineNumbers: boolean; lineWrapping: boolean; readOnly: boolean; theme?: Base16Theme; foldGutter: boolean; autofocus: boolean; onChange?: (value: string, change: EditorChange) => void; } /** * Based on [CodeMirror](http://codemirror.net/). */ export default class Editor extends Component { cm?: CodeMirror.Editor | null; node?: HTMLDivElement | null; componentDidMount() { this.cm = CodeMirror(this.node!, { value: this.props.value, mode: this.props.mode, lineNumbers: this.props.lineNumbers, lineWrapping: this.props.lineWrapping, readOnly: this.props.readOnly, autofocus: this.props.autofocus, foldGutter: this.props.foldGutter, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], }); if (this.props.onChange) { this.cm.on('change', (doc, change) => { this.props.onChange!(doc.getValue(), change); }); } } UNSAFE_componentWillReceiveProps(nextProps: EditorProps) { if (nextProps.value !== this.cm!.getValue()) { this.cm!.setValue(nextProps.value); } if (nextProps.readOnly !== this.props.readOnly) { this.cm!.setOption('readOnly', nextProps.readOnly); } if (nextProps.lineNumbers !== this.props.lineNumbers) { this.cm!.setOption('lineNumbers', nextProps.lineNumbers); } if (nextProps.lineWrapping !== this.props.lineWrapping) { this.cm!.setOption('lineWrapping', nextProps.lineWrapping); } if (nextProps.foldGutter !== this.props.foldGutter) { this.cm!.setOption('foldGutter', nextProps.foldGutter); } } shouldComponentUpdate() { return false; } componentWillUnmount() { const node = this.node!; node.removeChild(node.children[0]); this.cm = null; } getRef: React.RefCallback = (node) => { this.node = node; }; render() { return ; } static propTypes = { value: PropTypes.string, mode: PropTypes.string, lineNumbers: PropTypes.bool, lineWrapping: PropTypes.bool, readOnly: PropTypes.bool, theme: PropTypes.object, foldGutter: PropTypes.bool, autofocus: PropTypes.bool, onChange: PropTypes.func, }; static defaultProps = { value: '', mode: 'javascript', lineNumbers: true, lineWrapping: false, readOnly: false, foldGutter: true, autofocus: false, }; }