rc-editor-core
Version:
editor-core ui component for react
515 lines (456 loc) • 21.2 kB
JavaScript
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
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; }
/* tslint:disable:member-ordering interface-name */
import React from 'react';
import PropTypes from 'prop-types';
import { Editor, EditorState, ContentState, CompositeDecorator, Modifier, getDefaultKeyBinding, KeyBindingUtil, DefaultDraftBlockRenderMap } from 'draft-js';
import { List, Map } from 'immutable';
import 'setimmediate';
import classnames from 'classnames';
import { createToolbar } from '../Toolbar';
import ConfigStore from './ConfigStore';
import GetHTML from './export/getHTML';
import exportText, { decodeContent } from './export/exportText';
import customHTML2Content from './customHTML2Content';
var hasCommandModifier = KeyBindingUtil.hasCommandModifier;
function noop() {}
;
var defaultPluginConfig = {};
var focusDummyStyle = {
width: 0,
opacity: 0,
border: 0,
position: 'absolute',
left: 0,
top: 0
};
var toolbar = createToolbar();
var configStore = new ConfigStore();
var EditorCore = function (_React$Component) {
_inherits(EditorCore, _React$Component);
function EditorCore(props) {
_classCallCheck(this, EditorCore);
var _this = _possibleConstructorReturn(this, _React$Component.call(this, props));
_this.cancelForceUpdateImmediate = function () {
clearImmediate(_this.forceUpdateImmediate);
_this.forceUpdateImmediate = null;
};
_this.handlePastedText = function (text, html) {
var editorState = _this.state.editorState;
if (html) {
var contentState = editorState.getCurrentContent();
var selection = editorState.getSelection();
var fragment = customHTML2Content(html, contentState);
var pastedContent = Modifier.replaceWithFragment(contentState, selection, fragment);
var newContent = pastedContent.merge({
selectionBefore: selection,
selectionAfter: pastedContent.getSelectionAfter().set('hasFocus', true)
});
_this.setEditorState(EditorState.push(editorState, newContent, 'insert-fragment'), true);
return 'handled';
}
return 'not-handled';
};
_this.plugins = List(List(props.plugins).flatten(true));
var editorState = void 0;
if (props.value !== undefined) {
if (props.value instanceof EditorState) {
editorState = props.value || EditorState.createEmpty();
} else {
editorState = EditorState.createEmpty();
}
} else {
editorState = EditorState.createEmpty();
}
editorState = _this.generatorDefaultValue(editorState);
_this.state = {
plugins: _this.reloadPlugins(),
editorState: editorState,
customStyleMap: {},
customBlockStyleMap: {},
compositeDecorator: null
};
if (props.value !== undefined) {
_this.controlledMode = true;
}
return _this;
}
EditorCore.ToEditorState = function ToEditorState(text) {
var createEmptyContentState = ContentState.createFromText(decodeContent(text) || '');
var editorState = EditorState.createWithContent(createEmptyContentState);
return EditorState.forceSelection(editorState, createEmptyContentState.getSelectionAfter());
};
EditorCore.prototype.getDefaultValue = function getDefaultValue() {
var _props = this.props,
defaultValue = _props.defaultValue,
value = _props.value;
return value || defaultValue;
};
EditorCore.prototype.Reset = function Reset() {
var defaultValue = this.getDefaultValue();
var contentState = defaultValue ? defaultValue.getCurrentContent() : ContentState.createFromText('');
var updatedEditorState = EditorState.push(this.state.editorState, contentState, 'remove-range');
this.setEditorState(EditorState.forceSelection(updatedEditorState, contentState.getSelectionBefore()));
};
EditorCore.prototype.SetText = function SetText(text) {
var createTextContentState = ContentState.createFromText(text || '');
var editorState = EditorState.push(this.state.editorState, createTextContentState, 'change-block-data');
this.setEditorState(EditorState.moveFocusToEnd(editorState), true);
};
EditorCore.prototype.getChildContext = function getChildContext() {
return {
getEditorState: this.getEditorState,
setEditorState: this.setEditorState
};
};
EditorCore.prototype.reloadPlugins = function reloadPlugins() {
var _this2 = this;
return this.plugins && this.plugins.size ? this.plugins.map(function (plugin) {
// 如果插件有 callbacks 方法,则认为插件已经加载。
if (plugin.callbacks) {
return plugin;
}
// 如果插件有 constructor 方法,则构造插件
if (plugin.hasOwnProperty('constructor')) {
var pluginConfig = _extends(_this2.props.pluginConfig, plugin.config || {}, defaultPluginConfig);
return plugin.constructor(pluginConfig);
}
// else 无效插件
console.warn('>> 插件: [', plugin.name, '] 无效。插件或许已经过期。');
return false;
}).filter(function (plugin) {
return plugin;
}).toArray() : [];
};
EditorCore.prototype.componentWillMount = function componentWillMount() {
var plugins = this.initPlugins().concat([toolbar]);
var customStyleMap = {};
var customBlockStyleMap = {};
var customBlockRenderMap = Map(DefaultDraftBlockRenderMap);
var toHTMLList = List([]);
// initialize compositeDecorator
var compositeDecorator = new CompositeDecorator(plugins.filter(function (plugin) {
return plugin.decorators !== undefined;
}).map(function (plugin) {
return plugin.decorators;
}).reduce(function (prev, curr) {
return prev.concat(curr);
}, []));
// initialize Toolbar
var toolbarPlugins = List(plugins.filter(function (plugin) {
return !!plugin.component && plugin.name !== 'toolbar';
}));
// load inline styles...
plugins.forEach(function (plugin) {
var styleMap = plugin.styleMap,
blockStyleMap = plugin.blockStyleMap,
blockRenderMap = plugin.blockRenderMap,
toHtml = plugin.toHtml;
if (styleMap) {
for (var key in styleMap) {
if (styleMap.hasOwnProperty(key)) {
customStyleMap[key] = styleMap[key];
}
}
}
if (blockStyleMap) {
for (var _key in blockStyleMap) {
if (blockStyleMap.hasOwnProperty(_key)) {
customBlockStyleMap[_key] = blockStyleMap[_key];
customBlockRenderMap = customBlockRenderMap.set(_key, {
element: null
});
}
}
}
if (toHtml) {
toHTMLList = toHTMLList.push(toHtml);
}
if (blockRenderMap) {
for (var _key2 in blockRenderMap) {
if (blockRenderMap.hasOwnProperty(_key2)) {
customBlockRenderMap = customBlockRenderMap.set(_key2, blockRenderMap[_key2]);
}
}
}
});
configStore.set('customStyleMap', customStyleMap);
configStore.set('customBlockStyleMap', customBlockStyleMap);
configStore.set('blockRenderMap', customBlockRenderMap);
configStore.set('customStyleFn', this.customStyleFn.bind(this));
configStore.set('toHTMLList', toHTMLList);
this.setState({
toolbarPlugins: toolbarPlugins,
compositeDecorator: compositeDecorator
});
this.setEditorState(EditorState.set(this.state.editorState, { decorator: compositeDecorator }), false, false);
};
EditorCore.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
if (this.forceUpdateImmediate) {
this.cancelForceUpdateImmediate();
}
if (this.controlledMode) {
var decorators = nextProps.value.getDecorator();
var editorState = decorators ? nextProps.value : EditorState.set(nextProps.value, { decorator: this.state.compositeDecorator });
this.setState({
editorState: editorState
});
}
};
EditorCore.prototype.componentWillUnmount = function componentWillUnmount() {
this.cancelForceUpdateImmediate();
};
// 处理 value
EditorCore.prototype.generatorDefaultValue = function generatorDefaultValue(editorState) {
var defaultValue = this.getDefaultValue();
if (defaultValue) {
return defaultValue;
}
return editorState;
};
EditorCore.prototype.getStyleMap = function getStyleMap() {
return configStore.get('customStyleMap');
};
EditorCore.prototype.setStyleMap = function setStyleMap(customStyleMap) {
configStore.set('customStyleMap', customStyleMap);
this.render();
};
EditorCore.prototype.initPlugins = function initPlugins() {
var _this3 = this;
var enableCallbacks = ['focus', 'getEditorState', 'setEditorState', 'getStyleMap', 'setStyleMap'];
return this.getPlugins().map(function (plugin) {
enableCallbacks.forEach(function (callbackName) {
if (plugin.callbacks.hasOwnProperty(callbackName)) {
plugin.callbacks[callbackName] = _this3[callbackName].bind(_this3);
}
});
return plugin;
});
};
EditorCore.prototype.focusEditor = function focusEditor(ev) {
this.refs.editor.focus(ev);
if (this.props.readOnly) {
this._focusDummy.focus();
}
if (this.props.onFocus) {
this.props.onFocus(ev);
}
};
EditorCore.prototype._focus = function _focus(ev) {
if (!ev || !ev.nativeEvent || !ev.nativeEvent.target) {
return;
}
if (document.activeElement && document.activeElement.getAttribute('contenteditable') === 'true') {
return;
}
return this.focus(ev);
};
EditorCore.prototype.focus = function focus(ev) {
var _this4 = this;
var event = ev && ev.nativeEvent;
if (event && event.target === this._editorWrapper) {
var editorState = this.state.editorState;
var selection = editorState.getSelection();
if (!selection.getHasFocus()) {
if (selection.isCollapsed()) {
return this.setState({
editorState: EditorState.moveSelectionToEnd(editorState)
}, function () {
_this4.focusEditor(ev);
});
}
}
}
this.focusEditor(ev);
};
EditorCore.prototype.getPlugins = function getPlugins() {
return this.state.plugins.slice();
};
EditorCore.prototype.getEventHandler = function getEventHandler() {
var _this5 = this;
var enabledEvents = ['onUpArrow', 'onDownArrow', 'handleReturn', 'onFocus', 'onBlur', 'onTab', 'handlePastedText'];
var eventHandler = {};
enabledEvents.forEach(function (event) {
eventHandler[event] = _this5.generatorEventHandler(event);
});
return eventHandler;
};
EditorCore.prototype.getEditorState = function getEditorState() {
var needFocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
if (needFocus) {
this.refs.editor.focus();
}
return this.state.editorState;
};
EditorCore.prototype.setEditorState = function setEditorState(editorState) {
var _this6 = this;
var focusEditor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var triggerChange = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var newEditorState = editorState;
this.getPlugins().forEach(function (plugin) {
if (plugin.onChange) {
var updatedEditorState = plugin.onChange(newEditorState);
if (updatedEditorState) {
newEditorState = updatedEditorState;
}
}
});
if (this.props.onChange && triggerChange) {
this.props.onChange(newEditorState);
// close this issue https://github.com/ant-design/ant-design/issues/5788
// when onChange not take any effect
// `<Editor />` won't rerender cause no props is changed.
// add an timeout here,
// if props.onChange not trigger componentWillReceiveProps,
// we will force render Editor with previous editorState,
if (this.controlledMode) {
this.forceUpdateImmediate = setImmediate(function () {
return _this6.setState({
editorState: new EditorState(_this6.state.editorState.getImmutable())
});
});
}
}
if (!this.controlledMode) {
this.setState({ editorState: newEditorState }, focusEditor ? function () {
return setImmediate(function () {
return _this6.refs.editor.focus();
});
} : noop);
}
};
EditorCore.prototype.handleKeyBinding = function handleKeyBinding(ev) {
if (this.props.onKeyDown) {
ev.ctrlKey = hasCommandModifier(ev);
var keyDownResult = this.props.onKeyDown(ev);
if (keyDownResult) {
return keyDownResult;
}
return getDefaultKeyBinding(ev);
}
return getDefaultKeyBinding(ev);
};
EditorCore.prototype.handleKeyCommand = function handleKeyCommand(command) {
if (this.props.multiLines) {
return this.eventHandle('handleKeyBinding', command);
}
return command === 'split-block' ? 'handled' : 'not-handled';
};
EditorCore.prototype.getBlockStyle = function getBlockStyle(contentBlock) {
var customBlockStyleMap = configStore.get('customBlockStyleMap');
var type = contentBlock.getType();
if (customBlockStyleMap.hasOwnProperty(type)) {
return customBlockStyleMap[type];
}
return '';
};
EditorCore.prototype.blockRendererFn = function blockRendererFn(contentBlock) {
var blockRenderResult = null;
this.getPlugins().forEach(function (plugin) {
if (plugin.blockRendererFn) {
var result = plugin.blockRendererFn(contentBlock);
if (result) {
blockRenderResult = result;
}
}
});
return blockRenderResult;
};
EditorCore.prototype.eventHandle = function eventHandle(eventName) {
var _props2;
var plugins = this.getPlugins();
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key3 = 1; _key3 < _len; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
for (var i = 0; i < plugins.length; i++) {
var plugin = plugins[i];
if (plugin.callbacks[eventName] && typeof plugin.callbacks[eventName] === 'function') {
var _plugin$callbacks;
var result = (_plugin$callbacks = plugin.callbacks)[eventName].apply(_plugin$callbacks, args);
if (result === true) {
return 'handled';
}
}
}
return this.props.hasOwnProperty(eventName) && (_props2 = this.props)[eventName].apply(_props2, args) === true ? 'handled' : 'not-handled';
};
EditorCore.prototype.generatorEventHandler = function generatorEventHandler(eventName) {
var _this7 = this;
return function () {
for (var _len2 = arguments.length, args = Array(_len2), _key4 = 0; _key4 < _len2; _key4++) {
args[_key4] = arguments[_key4];
}
return _this7.eventHandle.apply(_this7, [eventName].concat(args));
};
};
EditorCore.prototype.customStyleFn = function customStyleFn(styleSet) {
if (styleSet.size === 0) {
return {};
}
var plugins = this.getPlugins();
var resultStyle = {};
for (var i = 0; i < plugins.length; i++) {
if (plugins[i].customStyleFn) {
var styled = plugins[i].customStyleFn(styleSet);
if (styled) {
_extends(resultStyle, styled);
}
}
}
return resultStyle;
};
EditorCore.prototype.render = function render() {
var _classnames,
_this8 = this;
var _props3 = this.props,
prefixCls = _props3.prefixCls,
toolbars = _props3.toolbars,
style = _props3.style,
readOnly = _props3.readOnly,
multiLines = _props3.multiLines;
var _state = this.state,
editorState = _state.editorState,
toolbarPlugins = _state.toolbarPlugins;
var customStyleMap = configStore.get('customStyleMap');
var blockRenderMap = configStore.get('blockRenderMap');
var eventHandler = this.getEventHandler();
var Toolbar = toolbar.component;
var cls = classnames((_classnames = {}, _classnames[prefixCls + '-editor'] = true, _classnames.readonly = readOnly, _classnames.oneline = !multiLines, _classnames));
return React.createElement(
'div',
{ style: style, className: cls, onClick: this._focus.bind(this) },
React.createElement(Toolbar, { editorState: editorState, prefixCls: prefixCls, className: prefixCls + '-toolbar', plugins: toolbarPlugins, toolbars: toolbars }),
React.createElement(
'div',
{ className: prefixCls + '-editor-wrapper', ref: function ref(ele) {
return _this8._editorWrapper = ele;
}, style: style, onClick: function onClick(ev) {
return ev.preventDefault();
} },
React.createElement(Editor, _extends({}, this.props, eventHandler, { ref: 'editor', customStyleMap: customStyleMap, customStyleFn: this.customStyleFn.bind(this), editorState: editorState, handleKeyCommand: this.handleKeyCommand.bind(this), keyBindingFn: this.handleKeyBinding.bind(this), onChange: this.setEditorState.bind(this), blockStyleFn: this.getBlockStyle.bind(this), blockRenderMap: blockRenderMap, handlePastedText: this.handlePastedText, blockRendererFn: this.blockRendererFn.bind(this) })),
readOnly ? React.createElement('input', { style: focusDummyStyle, ref: function ref(ele) {
return _this8._focusDummy = ele;
}, onBlur: eventHandler.onBlur }) : null,
this.props.children
)
);
};
return EditorCore;
}(React.Component);
EditorCore.GetText = exportText;
EditorCore.GetHTML = GetHTML(configStore);
EditorCore.defaultProps = {
multiLines: true,
plugins: [],
prefixCls: 'rc-editor-core',
pluginConfig: {},
toolbars: [],
spilitLine: 'enter'
};
EditorCore.childContextTypes = {
getEditorState: PropTypes.func,
setEditorState: PropTypes.func
};
export default EditorCore;