armo-editor
Version:
React text editor component.
262 lines (213 loc) • 9.2 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _dec, _class, _class2, _temp; // Based on these two files:
// https://github.com/JedWatson/react-codemirror/blob/master/src/Codemirror.js
// https://github.com/FormidableLabs/component-playground/blob/master/src/components/editor.jsx
var _Editor = require('./Editor.less');
var _Editor2 = _interopRequireDefault(_Editor);
var _exenv = require('exenv');
var _exenv2 = _interopRequireDefault(_exenv);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);
var _classnames = require('classnames');
var _classnames2 = _interopRequireDefault(_classnames);
var _lodash = require('lodash.debounce');
var _lodash2 = _interopRequireDefault(_lodash);
var _createPrefixer = require('utils/createPrefixer');
var _createPrefixer2 = _interopRequireDefault(_createPrefixer);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var codeMirror = void 0;
if (_exenv2.default.canUseDOM) {
codeMirror = require('codemirror');
require("codemirror/mode/jsx/jsx");
require("codemirror/mode/css/css");
require("codemirror/mode/markdown/markdown");
}
var prefix = (0, _createPrefixer2.default)('controls', 'Editor');
var OPTION_PROPS = ['readOnly', 'lineNumbers', 'lineWrapping', 'mode', 'theme'];
function normalizeLineEndings(str) {
if (!str) return str;
return str.replace(/\r\n|\r/g, '\n');
}
var Editor = (_dec = prefix.withName('Editor'), _dec(_class = (_temp = _class2 = function (_Component) {
_inherits(Editor, _Component);
function Editor(props) {
_classCallCheck(this, Editor);
var _this = _possibleConstructorReturn(this, (Editor.__proto__ || Object.getPrototypeOf(Editor)).call(this, props));
_this.state = {
isFocused: false
};
_this.highlightSelectedLines = function () {
if (Array.isArray(_this.props.selectedLines)) {
_this.props.selectedLines.forEach(function (lineNumber) {
return _this.codeMirror.addLineClass(lineNumber, "wrap", "CodeMirror-activeline-background");
});
}
};
_this.receiveTextareaRef = function (ref) {
return _this.textareaNode = ref;
};
_this.handleChange = function (doc, change) {
if (!_this.props.readOnly && _this.props.onChange && change.origin !== 'setValue') {
_this.props.onChange(doc.getValue());
}
};
_this.handleScroll = function (codeMirror) {
_this.props.onScroll && _this.props.onScroll(codeMirror.getScrollInfo());
};
_this.handleScrollIntoView = function (codeMirror, e) {
e.preventDefault();
};
_this.componentWillReceiveProps = (0, _lodash2.default)(_this.componentWillReceiveProps, 0);
return _this;
}
_createClass(Editor, [{
key: 'componentDidMount',
value: function componentDidMount() {
var options = {
matchBrackets: true,
smartIndent: false,
tabSize: 2,
indentWithTabs: false,
extraKeys: {
Tab: function Tab(cm) {
var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
cm.replaceSelection(spaces);
}
}
};
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = OPTION_PROPS[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var key = _step.value;
options[key] = this.props[key];
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (this.props.fitToContent) {
options.viewportMargin = Infinity;
}
this.codeMirror = codeMirror.fromTextArea(this.textareaNode, options);
this.codeMirror.on('change', this.handleChange);
this.codeMirror.on('focus', this.handleFocus.bind(this, true));
this.codeMirror.on('blur', this.handleFocus.bind(this, false));
this.codeMirror.on('scroll', this.handleScroll);
this.codeMirror.on('scrollCursorIntoView', this.handleScrollIntoView);
this.codeMirror.setValue(this.props.defaultValue || this.props.value || '');
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
if (this.codeMirror && nextProps.value !== undefined && 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);
}
}
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = OPTION_PROPS[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var key = _step2.value;
var prop = nextProps[key];
if (prop !== this.props[key]) {
this.codeMirror.setOption(key, prop);
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
// is there a lighter-weight way to remove the cm instance?
if (this.codeMirror) {
this.codeMirror.toTextArea();
}
}
}, {
key: 'focus',
value: function focus() {
if (this.codeMirror) {
this.codeMirror.focus();
}
}
}, {
key: 'render',
value: function render() {
var focused = this.state.focused;
return _react2.default.createElement(
'div',
{ className: (0, _classnames2.default)({ focused: focused }) },
_react2.default.createElement('textarea', {
ref: this.receiveTextareaRef,
defaultValue: this.props.value,
autoComplete: 'off'
})
);
}
}, {
key: 'handleFocus',
value: function handleFocus(isFocused) {
this.setState({ isFocused: isFocused });
}
}]);
return Editor;
}(_react.Component), _class2.propTypes = {
theme: _propTypes2.default.string,
readOnly: _propTypes2.default.bool,
fitToContent: _propTypes2.default.bool,
value: _propTypes2.default.string,
selectedLines: _propTypes2.default.array,
onChange: _propTypes2.default.func,
mode: _propTypes2.default.oneOf(['jsx', 'css']),
lineNumbers: _propTypes2.default.bool,
lineWrapping: _propTypes2.default.bool,
style: _propTypes2.default.object,
className: _propTypes2.default.string
}, _class2.defaultProps = {
theme: "monokai",
fitToContent: false
}, _temp)) || _class);
exports.default = Editor;