forked from Gitlink/forgeplus-react
136 lines
4.4 KiB
JavaScript
136 lines
4.4 KiB
JavaScript
'use strict';
|
|
|
|
var React = require('react');
|
|
var ReactDOM = require('react-dom');
|
|
var PropTypes = require('prop-types');
|
|
var className = require('classnames');
|
|
var debounce = require('lodash.debounce');
|
|
var isEqual = require('lodash.isequal');
|
|
var createReactClass = require('create-react-class');
|
|
|
|
function normalizeLineEndings(str) {
|
|
if (!str) return str;
|
|
return str.replace(/\r\n|\r/g, '\n');
|
|
}
|
|
|
|
var CodeMirror = createReactClass({
|
|
propTypes: {
|
|
autoFocus: PropTypes.bool,
|
|
className: PropTypes.any,
|
|
codeMirrorInstance: PropTypes.func,
|
|
defaultValue: PropTypes.string,
|
|
name: PropTypes.string,
|
|
onChange: PropTypes.func,
|
|
onCursorActivity: PropTypes.func,
|
|
onFocusChange: PropTypes.func,
|
|
onScroll: PropTypes.func,
|
|
options: PropTypes.object,
|
|
path: PropTypes.string,
|
|
value: PropTypes.string,
|
|
preserveScrollPosition: PropTypes.bool
|
|
},
|
|
getDefaultProps: function getDefaultProps() {
|
|
return {
|
|
preserveScrollPosition: false
|
|
};
|
|
},
|
|
getCodeMirrorInstance: function getCodeMirrorInstance() {
|
|
return this.props.codeMirrorInstance || require('codemirror');
|
|
},
|
|
getInitialState: function getInitialState() {
|
|
return {
|
|
isFocused: false
|
|
};
|
|
},
|
|
componentWillMount: function componentWillMount() {
|
|
this.componentWillReceiveProps = debounce(this.componentWillReceiveProps, 0);
|
|
if (this.props.path) {
|
|
console.error('Warning: react-codemirror: the `path` prop has been changed to `name`');
|
|
}
|
|
},
|
|
componentDidMount: function componentDidMount() {
|
|
var codeMirrorInstance = this.getCodeMirrorInstance();
|
|
this.codeMirror = codeMirrorInstance.fromTextArea(this.textareaNode, this.props.options);
|
|
this.codeMirror.on('change', this.codemirrorValueChanged);
|
|
this.codeMirror.on('cursorActivity', this.cursorActivity);
|
|
this.codeMirror.on('focus', this.focusChanged.bind(this, true));
|
|
this.codeMirror.on('blur', this.focusChanged.bind(this, false));
|
|
this.codeMirror.on('scroll', this.scrollChanged);
|
|
this.codeMirror.setValue(this.props.defaultValue || this.props.value || '');
|
|
},
|
|
componentWillUnmount: function componentWillUnmount() {
|
|
// is there a lighter-weight way to remove the cm instance?
|
|
if (this.codeMirror) {
|
|
this.codeMirror.toTextArea();
|
|
}
|
|
},
|
|
componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
|
|
if (this.codeMirror && nextProps.value !== undefined && nextProps.value !== this.props.value && normalizeLineEndings(this.codeMirror.getValue()) !== normalizeLineEndings(nextProps.value)) {
|
|
if (this.props.preserveScrollPosition) {
|
|
var prevScrollPosition = this.codeMirror.getScrollInfo();
|
|
this.codeMirror.setValue(nextProps.value);
|
|
this.codeMirror.scrollTo(prevScrollPosition.left, prevScrollPosition.top);
|
|
} else {
|
|
this.codeMirror.setValue(nextProps.value);
|
|
}
|
|
}
|
|
if (typeof nextProps.options === 'object') {
|
|
for (var optionName in nextProps.options) {
|
|
if (nextProps.options.hasOwnProperty(optionName)) {
|
|
this.setOptionIfChanged(optionName, nextProps.options[optionName]);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
setOptionIfChanged: function setOptionIfChanged(optionName, newValue) {
|
|
var oldValue = this.codeMirror.getOption(optionName);
|
|
if (!isEqual(oldValue, newValue)) {
|
|
this.codeMirror.setOption(optionName, newValue);
|
|
}
|
|
},
|
|
getCodeMirror: function getCodeMirror() {
|
|
return this.codeMirror;
|
|
},
|
|
focus: function focus() {
|
|
if (this.codeMirror) {
|
|
this.codeMirror.focus();
|
|
}
|
|
},
|
|
focusChanged: function focusChanged(focused) {
|
|
this.setState({
|
|
isFocused: focused
|
|
});
|
|
this.props.onFocusChange && this.props.onFocusChange(focused);
|
|
},
|
|
cursorActivity: function cursorActivity(cm) {
|
|
this.props.onCursorActivity && this.props.onCursorActivity(cm);
|
|
},
|
|
scrollChanged: function scrollChanged(cm) {
|
|
this.props.onScroll && this.props.onScroll(cm.getScrollInfo());
|
|
},
|
|
codemirrorValueChanged: function codemirrorValueChanged(doc, change) {
|
|
if (this.props.onChange && change.origin !== 'setValue') {
|
|
this.props.onChange(doc.getValue(), change);
|
|
}
|
|
},
|
|
render: function render() {
|
|
var _this = this;
|
|
|
|
var editorClassName = className('ReactCodeMirror', this.state.isFocused ? 'ReactCodeMirror--focused' : null, this.props.className);
|
|
return React.createElement(
|
|
'div',
|
|
{ className: editorClassName },
|
|
React.createElement('textarea', {
|
|
ref: function (ref) {
|
|
return _this.textareaNode = ref;
|
|
},
|
|
name: this.props.name || this.props.path,
|
|
defaultValue: this.props.value,
|
|
autoComplete: 'off',
|
|
autoFocus: this.props.autoFocus
|
|
})
|
|
);
|
|
}
|
|
});
|
|
|
|
module.exports = CodeMirror; |