@kreativsoftware/react-lz-editor
Version:
An open source react editor based on draft-Js and ant design, good support HTML, markdown and Draft Raw format.
887 lines (823 loc) • 33.2 kB
JSX
/**
* Created by lizhen on 4/26/2016.
*/
// import 'antd/dist/antd.css';
import React from 'react';
import PropTypes from 'prop-types';
import {
AtomicBlockUtils, Editor, Entity, convertToRaw, convertFromRaw,
EditorState, RichUtils, Modifier, CompositeDecorator, ContentState, getDefaultKeyBinding, KeyBindingUtil
} from 'draft-js';
import { Modal, Input, message } from 'antd';
import isEmpty from 'lodash/isEmpty';
import trim from 'lodash/trim';
import { stateToHTML, stateFromHTML, stateToMD, stateFromMD } from './utils';
import { PRO_COMMON } from '../global/supports/publicDatas';
import { lang } from '../global/i18n';
import LinkDecorator from './decorators/LinkDecorator';
import ImageDecorator from './decorators/ImageDecorator';
import VideoDecorator from './decorators/VideoDecorator';
import AudioDecorator from './decorators/AudioDecorator';
import ImgStyleControls from './toolBar/mediaImageUploader';
import VideoStyleControls from './toolBar/medioVideoUploader';
import AudioStyleControls from './toolBar/medioAudioUploader';
import ColorControls from './toolBar/colorControls';
import AutoSaveControls from './toolBar/autoSaveList';
import BlockStyleControls from './toolBar/blockStyleControls';
import AlignmentControls from './toolBar/alignmentControls';
import InlineStyleControls from './toolBar/inlineStyleControls';
import PasteNoStyleControls from './toolBar/pasteNoStyleControls';
import { AddUrl, CloseUrl } from './toolBar/urlControls';
import { OpenFull, SourceEditor } from './toolBar/cookieControls';
import RemoveStyleControls from './toolBar/removeStyleControls';
import UndoRedo from './toolBar/undoredoControls';
import { colorStyleMap } from './utils/colorConfig';
import ExtendedRichUtils from './utils/ExtendedRichUtils';
import './components.css';
import '../global/supports/resources/system.css';
const decorator = new CompositeDecorator([
LinkDecorator,
ImageDecorator,
VideoDecorator,
AudioDecorator
]);
class EditorConcist extends React.Component {
constructor(props) {
super(props);
this.state = {
openFullTest: '',
showSourceEditor: '',
showURLInput: false,
urlValue: '',
hasPasted: false,
autoSaveFun: null,
visible: false,
showMarkdownSource: false,
tempSouceContent: '',
language: 'en',
editorState: (() => {
let originalString = this.props.importContent;
originalString = !originalString
? ' '
: originalString;
if (!originalString) { // Error, do not user 'createEmpty' for the time been.
// this.state.alwaysEnterEmpty = true;
return EditorState.createEmpty(decorator);
}
const ConvertFormatProps = this.props.convertFormat;
let contentState;
if (ConvertFormatProps === 'html') {
contentState = stateFromHTML(originalString);
} else if (ConvertFormatProps === 'markdown') {
// console.log("markdown originalString",originalString)
contentState = stateFromMD(originalString);
} else if (ConvertFormatProps === 'raw') {
originalString = originalString.replace(/\s/g, '') ? originalString : '{}';
const rawContent = JSON.parse(originalString);
if (isEmpty(rawContent)) {
return EditorState.createWithContent('', decorator);
}
contentState = convertFromRaw(rawContent);
}
return EditorState.createWithContent(contentState, decorator);
})()
};
this.onChange = (editorState) => {
this.setState({ editorState });
const that = this;
if (that.timer) {
clearTimeout(that.timer);
}
that.timer = setTimeout(() => {
// convert state to object.
const rawContentState = that.state.editorState.getCurrentContent();
// const rawContent = convertToRaw(rawContentState);
// console.log('rawContentState', rawContentState);
let content;
const ConvertFormatProps = that.props.convertFormat;
if (ConvertFormatProps === 'html') {
content = stateToHTML(rawContentState);
} else if (ConvertFormatProps === 'markdown') {
content = stateToMD(rawContentState);
} else if (ConvertFormatProps === 'raw') {
const rawContent = convertToRaw(rawContentState);
content = JSON.stringify(rawContent);
}
that.props.cbReceiver(content);
// NOTE: When you set 'active' as 'true' in the editor, DO NOT use 'this.forceUpdate();', or you could not have able to select the text in editor probably.
// 富文本编辑器在设置active是true时,不可使用forceUpdate,否则会造成无法选中文本的问题!
}, 300);
};
this.handleKeyCommand = command => this._handleKeyCommand(command);
this.toggleBlockType = type => this._toggleBlockType(type);
this.toggleAlignment = type => this._toggleAlignment(type);
this.toggleInlineStyle = style => this._toggleInlineStyle(style);
this.customKeyBinding = this._customKeyBinding.bind(this);
this.handlePastedText = this._handlePastedText.bind(this);
/* VIDEO/AUDIO/IMAGE */
this.logState = () => {
const content = this.state.editorState.getCurrentContent();
// console.log(convertToRaw(content));
};
this.addMedia = this._addMedia.bind(this);
this.addAudio = this._addAudio.bind(this);
this.addImage = this._addImage.bind(this);
this.addVideo = this._addVideo.bind(this);
this.undoRedo = this._undoRedo.bind(this);
this.removeStyle = this._removeStyle.bind(this);
this.pasteNoStyle = this._pasteNoStyle.bind(this);
this.choiceAutoSave = this._choiceAutoSave.bind(this);
this.toggleColor = toggledColor => this._toggleColor(toggledColor);
this.promptForLink = this._promptForLink.bind(this);
this.onURLChange = e => this.setState({ urlValue: e.target.value });
this.confirmLink = this._confirmLink.bind(this);
this.onLinkInputKeyDown = this._onLinkInputKeyDown.bind(this);
this.removeLink = this._removeLink.bind(this);
this.openFull = this._openFull.bind(this);
this.toggleSource = this._toggleSource.bind(this);
this.handleOk = this.handleOk.bind(this);
this.handleCancel = this.handleCancel.bind(this);
this.solidHtml = this._solidHtml.bind(this);
this.changeMrakdownContent = this._changeMrakdownContent.bind(this);
}
get localLang() {
const lang = navigator.language || navigator.browserLanguage;
return { fullLang: lang, simpLang: lang.split('-')[0] };
}
componentDidMount() {
let currLang = this.localLang,
language;
if (lang[this.props.lang]) {
language = this.props.lang;
} else if (lang[currLang.fullLang]) {
language = currLang.fullLang;
} else if (lang[currLang.simpLang]) {
language = currLang.simpLang;
}
language = language || 'en';
this.setState({
language,
openFullTest: lang[language].fullScreen,
showSourceEditor: lang[language].sourceCode
});
const content = (this.props.importContent);
// const decorator = new CompositeDecorator([
// LinkDecorator,
// ImageDecorator
// ]);
const contentState = stateFromHTML(content);
// console.log("componentDidMount content",content);
// console.log("componentDidMount contentState",JSON.stringify(contentState));
// let values = EditorState.createWithContent(contentState, decorator);
// this.state.editorState = values;
this.state.autoSaveFun = setInterval(() => {
// Automaticly save text to draft-box every minute.
// 每分钟自动保存草稿一次
this.handleKeyCommand('editor-save');
}, 60000);
}
// This hook function will be called while you edit text in editor.
// 此钩子用作编辑时候的回调
componentWillReceiveProps(newProps) {
if (!newProps.active) {
return false;
}
if (newProps.importContent == this.props.importContent) {
return false;
}
const ConvertFormatProps = this.props.convertFormat;
let newContent = '';
// console.log("ConvertFormatProps",ConvertFormatProps)
if (ConvertFormatProps === 'html') {
newContent = newProps.importContent.replace(/[\s\xA0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]\>/g, '>');
if (newContent == 'undefined' || !newContent) {
newContent = '<p> </p>';
}
} else if (ConvertFormatProps === 'markdown') {
newContent = newProps.importContent || '';
this.state.tempSouceContent = newContent;
} else if (ConvertFormatProps === 'raw') {
newContent = newProps.importContent || '{}';
}
/* const decorator = new CompositeDecorator([
LinkDecorator,
ImageDecorator,
VideoDecorator,
AudioDecorator
]); */
// console.log("newContent",newContent)
let contentState;
if (ConvertFormatProps === 'html') {
contentState = stateFromHTML(newContent);
} else if (ConvertFormatProps === 'markdown') {
contentState = stateFromMD(newContent);
} else if (ConvertFormatProps === 'raw') {
const rawContent = JSON.parse(newContent);
contentState = convertFromRaw(rawContent);
}
// console.log("contentState",contentState)
// console.log("componentWillReceiveProps newContent",newContent);
// console.log("componentWillReceiveProps contentState",JSON.stringify(contentState));
const values = EditorState.createWithContent(contentState, decorator);
this.state.editorState = values;
}
componentWillUnmount() {
// console.log("componentWillUnmount! this.state.autoSaveFun",this.state.autoSaveFun);
clearInterval(this.state.autoSaveFun);
}
handleOk() {
this.setState({ visible: false });
}
handleCancel(e) {
this.setState({ visible: false });
}
_promptForLink(e) {
e.preventDefault();
const { editorState } = this.state;
const selection = editorState.getSelection();
if (!selection.isCollapsed()) {
const that = this;
this.setState({
showURLInput: true,
urlValue: '',
visible: true
}, () => {
});
} else {
message.error(lang[this.state.language].selectedText, 5);
}
}
_confirmLink(e) {
// console.log("_confirmLink urlValue", urlValue)
const { editorState, urlValue } = this.state;
const entityKey = Entity.create('LINK', 'MUTABLE', { url: urlValue });
this.onChange(RichUtils.toggleLink(editorState, editorState.getSelection(), entityKey));
this.setState({
showURLInput: false,
urlValue: ''
}, () => {
setTimeout(() => {
}, 0);
});
}
_onLinkInputKeyDown(e) {
if (e.which === 13) {
this._confirmLink(e);
return false;
}
}
_removeLink(e) {
e.preventDefault();
const { editorState } = this.state;
const selection = editorState.getSelection();
if (!selection.isCollapsed()) {
this.onChange(RichUtils.toggleLink(editorState, selection, null));
} else {
message.error(lang[this.state.language].selectedLink, 5);
}
}
_openFull(e) {
e.preventDefault();
const ele = document.querySelector('.RichEditor-root');
// let affix=document.querySelector("#text-editor-affix"),affixToolBar=document.querySelector("#text-editor-affix>div");
if (ele.classList.contains('openFullAll')) {
ele.className = ele.className.replace('openFullAll', '');
// affix.style="";
// affixToolBar.className="";
// affixToolBar.style=""
this.setState({
openFullTest: lang[this.state.language].fullScreen
});
} else {
ele.className += ' openFullAll';
setTimeout(() => {
// affix.style="width: "+affix.offsetWidth+"px; height: 0; margin-bottom: 70px;";
// affixToolBar.className="ant-affix";
// affixToolBar.style="position: fixed; top: 0; left: 0; width: "+affix.offsetWidth+"px;margin: 0 15px 15px;"
}, 500);
this.setState({
openFullTest: lang[this.state.language].quitFullScreen
});
}
}
_toggleSource(e) {
e.preventDefault();
const ele = document.querySelector('.RichEditor-root');
if (ele.classList.contains('showSource')) {
ele.className = ele.className.replace('showSource', '');
this.setState({
showSourceEditor: lang[this.state.language].sourceCode,
showMarkdownSource: false
});
} else {
ele.className += ' showSource';
this.setState({
showSourceEditor: lang[this.state.language].preview,
showMarkdownSource: true
});
}
}
_changeMrakdownContent(e) {
const markdownContent = e.target.value;
// console.log("markdownContent",markdownContent);
const contentState = stateFromMD(markdownContent);
const values = EditorState.createWithContent(contentState, decorator);
this.state.tempSouceContent = markdownContent;
this.state.editorState = values;
this.forceUpdate();
}
_handleKeyCommand(command) {
// console.log("command",command);
const { editorState } = this.state;
const newState = RichUtils.handleKeyCommand(editorState, command);
if (command === 'editor-save' && this.props.autoSave == true) {
// window.localDB//start20Text
// let Data=PRO_COMMON.localDB.getter("grab_news_data") || [];
const rawContentState = editorState.getCurrentContent();
let content = '',
newText = '';
const ConvertFormatProps = this.props.convertFormat;
if (ConvertFormatProps === 'html') {
content = stateToHTML(rawContentState);
newText = content.replace(/<[^>]*>|&[^;]*;/g, '');
} else if (ConvertFormatProps === 'markdown') {
content = stateToMD(rawContentState);
} else if (ConvertFormatProps === 'raw') {
const rawContent = convertToRaw(rawContentState);
content = JSON.stringify(rawContent);
}
if (newText.length < 30) {
return false;
}
const start30Text = newText.substr(0, 30);
PRO_COMMON.localDB.setter(`$d${start30Text}`, content);
message.success(lang[this.state.language].successToDraftBox, 5);
return true;
} else if (command === 'editor-paste') {
return true;
}
if (newState) {
this.onChange(newState);
return true;
}
return false;
}
_customKeyBinding(e) {
const { hasCommandModifier } = KeyBindingUtil;
if (e.keyCode === 83/* `S` key */ && hasCommandModifier(e)) {
return 'editor-save';
} else if (e.keyCode === 86/* `V` key */ && hasCommandModifier(e)) {
}
return getDefaultKeyBinding(e);
}
_solidHtml(html) {
// html=html.replace(/"((?:\\.|[^"\\])*)"/g,"");
// Remove all content in quote. e.g. style="" class=""
// 去掉所有英文单引号里面的内容,比如 style="" class=""
const walk_the_DOM = function walk(node, func) {
func(node);
node = node.firstChild;
while (node) {
walk(node, func);
node = node.nextSibling;
}
};
const wrapper = document.createElement('div');
wrapper.innerHTML = html;
walk_the_DOM(wrapper.firstChild, (element) => {
if (element.removeAttribute) {
element.removeAttribute('id');
element.removeAttribute('style');
element.removeAttribute('class');
}
});
return wrapper.innerHTML;
}
_handlePastedText(text, sourceString) {
sourceString = this.solidHtml(sourceString);
// console.log("_handlePastedText text",text);
// console.log("_handlePastedText sourceString",typeof(sourceString),sourceString);
if (text == 'undefined' && sourceString == 'undefined') {
// console.log("_handlePastedText return false");
return false;
}
if (sourceString == 'undefined' || !sourceString) {
this.pasteNoStyle(text);
return false;
}
const { editorState } = this.state;
const rawContentState = editorState.getCurrentContent();
let content = '',
newText = '';
const ConvertFormatProps = this.props.convertFormat;
if (ConvertFormatProps === 'html') {
content = stateToHTML(rawContentState);
newText = content.replace(/<[^>]*>|&[^;]*;/g, '');
} else if (ConvertFormatProps === 'markdown') {
content = stateToMD(rawContentState);
} else if (ConvertFormatProps === 'raw') {
const rawContent = convertToRaw(rawContentState);
content = JSON.stringify(rawContent);
}
if (this.state.hasPasted === true || trim(newText).length > 0) {
const blockMap = ContentState.createFromText(text.trim()).blockMap;
const newState = Modifier.replaceWithFragment(editorState.getCurrentContent(), editorState.getSelection(), blockMap);
this.onChange(EditorState.push(editorState, newState, 'insert-fragment'));
return true;
}
this.state.hasPasted = true;
const decorator = new CompositeDecorator([
LinkDecorator,
ImageDecorator,
VideoDecorator,
AudioDecorator
]);
let contentState = '';
if (ConvertFormatProps === 'html') {
contentState = stateFromHTML(sourceString);
} else if (ConvertFormatProps === 'markdown') {
contentState = stateFromMD(sourceString);
} else if (ConvertFormatProps === 'raw') {
contentState = convertFromRaw(sourceString);
}
const values = EditorState.createWithContent(contentState, decorator);
this.state.editorState = values;
message.success(lang[this.state.language].successPasteCleanText, 5);
this.forceUpdate();
return true;
// Override the default paste behavior of the editor
// 覆盖编辑器的默认粘贴行为
}
_toggleBlockType(blockType) {
this.onChange(RichUtils.toggleBlockType(this.state.editorState, blockType));
}
_toggleAlignment(alignment) {
// This method only supports the data type like:
// 这种方式仅支持的数据类型:
// https://github.com/facebook/draft-js/blob/master/src/model/constants/DraftBlockType.js
// const {editorState} = this.state;
// const selection = editorState.getSelection();
// const contentState = editorState.getCurrentContent();
// const alignBlock = Modifier.setBlockType(contentState, selection, alignment)
// this.setState({
// editorState: EditorState.push(editorState, alignBlock)
// })
// right way:
this.onChange(ExtendedRichUtils.toggleAlignment(this.state.editorState, alignment));
}
_toggleInlineStyle(inlineStyle) {
this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, inlineStyle));
}
/* VIDEO/AUDIO/IMAGE */
_addMedia(type, Object) {
const src = Object.url;
if (!src) {
throw new Error(lang[this.state.language].errorUploadingFile);
return false;
}
const entityKey = Entity.create(type, 'IMMUTABLE', { src });
return AtomicBlockUtils.insertAtomicBlock(this.state.editorState, entityKey, ' ');
}
_addAudio(Objects) {
const that = this;
Objects.map((item, i) => {
setTimeout(() => that.onChange(that.addMedia('audio', item)), i * 100);
});
}
_addImage(Objects) {
const that = this;
// console.log("Objects Objects", Objects);
Objects.map((item, i) => {
setTimeout(() => {
const imageObj = that.addMedia('image', item);
// console.log("imageObj",imageObj,JSON.stringify(imageObj));
return that.onChange(imageObj);
}, i * 100);
});
}
_addVideo(Objects) {
const that = this;
Objects.map((item, i) => {
setTimeout(() => that.onChange(that.addMedia('video', item)), i * 100);
});
}
_pasteNoStyle(sourceString) {
const decorator = new CompositeDecorator([
LinkDecorator,
ImageDecorator,
VideoDecorator,
AudioDecorator
]);
let contentState = '';
const ConvertFormatProps = this.props.convertFormat;
if (ConvertFormatProps === 'html') {
sourceString = `<p>${sourceString.replace(/\n([ \t]*\n)+/g, '</p><p>')
.replace('\n', '<br />')}</p>`;
contentState = stateFromHTML(sourceString);
} else if (ConvertFormatProps === 'markdown') {
contentState = stateFromMD(sourceString);
} else if (ConvertFormatProps === 'raw') {
contentState = convertFromRaw(sourceString);
}
// console.log("_pasteNoStyle sourceString",sourceString);
// console.log("_pasteNoStyle contentState",JSON.stringify(contentState));
const values = EditorState.createWithContent(contentState, decorator);
this.state.editorState = values;
this.forceUpdate();
}
_undoRedo(type) {
if (this.state.editorState) {
let newEditorState = null;
if (type == 'undo') {
newEditorState = EditorState.undo(this.state.editorState);
} else {
newEditorState = EditorState.redo(this.state.editorState);
}
this.setState({ editorState: newEditorState });
}
}
_removeStyle() {
const { editorState } = this.state;
const selection = editorState.getSelection();
const contentState = editorState.getCurrentContent();
const styles = editorState.getCurrentInlineStyle();
const removeStyles = styles.reduce((state, style) => Modifier.removeInlineStyle(state, selection, style), contentState);
const removeBlock = Modifier.setBlockType(removeStyles, selection, 'unstyled');
this.setState({
editorState: EditorState.push(editorState, removeBlock)
});
}
_choiceAutoSave(savedImportContent) {
const decorator = new CompositeDecorator([
LinkDecorator,
ImageDecorator,
VideoDecorator,
AudioDecorator
]);
const ConvertFormatProps = this.props.convertFormat;
let contentState = '';
if (ConvertFormatProps === 'html') {
contentState = stateFromHTML(savedImportContent);
} else if (ConvertFormatProps === 'markdown') {
contentState = stateFromMD(savedImportContent);
} else if (ConvertFormatProps === 'raw') {
const rawContent = JSON.parse(savedImportContent);
contentState = convertFromRaw(rawContent);
}
const values = EditorState.createWithContent(contentState, decorator);
this.state.editorState = values;
this.forceUpdate();
}
_toggleColor(toggledColor) {
const { editorState } = this.state;
const selection = editorState.getSelection();
// Let's just allow one color at a time. Turn off all active colors.
const nextContentState = Object.keys(colorStyleMap).reduce((contentState, color) => Modifier.removeInlineStyle(contentState, selection, color), editorState.getCurrentContent());
let nextEditorState = EditorState.push(editorState, nextContentState, 'change-inline-style');
const currentStyle = editorState.getCurrentInlineStyle();
// Unset style override for current color.
if (selection.isCollapsed()) {
nextEditorState = currentStyle.reduce((state, color) => RichUtils.toggleInlineStyle(state, color), nextEditorState);
}
// If the color is being toggled on, apply it.
if (!currentStyle.has(toggledColor)) {
nextEditorState = RichUtils.toggleInlineStyle(nextEditorState, toggledColor);
}
this.onChange(nextEditorState);
}
renderToolBar(editorState) {
return (
<div className='RichEditor-toolbar'>
{this.state.showMarkdownSource == false && this.props.undoRedo && <UndoRedo onToggle={this.undoRedo} lang={lang[this.state.language]} />}
{this.state.showMarkdownSource == false && this.props.removeStyle && <RemoveStyleControls onToggle={this.removeStyle} lang={lang[this.state.language]} />}
{this.state.showMarkdownSource == false && this.props.pasteNoStyle && <PasteNoStyleControls receiveText={this.pasteNoStyle} lang={lang[this.state.language]} />}
{this.state.showMarkdownSource == false && this.props.blockStyle && <BlockStyleControls editorState={editorState} onToggle={this.toggleBlockType} lang={lang[this.state.language]} />}
{this.props.alignment && this.props.convertFormat !== 'markdown' && <AlignmentControls editorState={editorState} onToggle={this.toggleAlignment} lang={lang[this.state.language]} />}
{this.state.showMarkdownSource == false && this.props.inlineStyle && <InlineStyleControls editorState={editorState} onToggle={this.toggleInlineStyle} lang={lang[this.state.language]} />}
{this.props.color && this.props.convertFormat !== 'markdown' && <ColorControls editorState={editorState} onToggle={this.toggleColor} lang={lang[this.state.language]} />}
{this.state.showMarkdownSource == false && this.props.image && <ImgStyleControls
uploadConfig={this.props.uploadConfig}
receiveImage={this.addImage}
watermarkImage={this.props.watermarkImage}
lang={lang[this.state.language]}
uploadProps={this.props.uploadProps}
/>}
{this.state.showMarkdownSource == false && this.props.video && <VideoStyleControls
uploadConfig={this.props.uploadConfig}
receiveVideo={this.addVideo}
lang={lang[this.state.language]}
uploadProps={this.props.uploadProps}
/>}
{this.state.showMarkdownSource == false && this.props.audio && <AudioStyleControls
uploadConfig={this.props.uploadConfig}
receiveAudio={this.addAudio}
lang={lang[this.state.language]}
uploadProps={this.props.uploadProps}
/>}
{this.state.showMarkdownSource == false && this.props.urls && <AddUrl editorState={editorState} onToggle={this.promptForLink} lang={lang[this.state.language]} />}
{this.state.showMarkdownSource == false && this.props.urls && <CloseUrl editorState={editorState} onToggle={this.removeLink} lang={lang[this.state.language]} />}
{this.state.showMarkdownSource == false && this.props.autoSave && <AutoSaveControls receiveSavedItem={this.choiceAutoSave} lang={lang[this.state.language]} />}
{this.props.fullScreen && <OpenFull editorState={editorState} onToggle={this.openFull} coverTitle={this.state.openFullTest} lang={lang[this.state.language]} />}
{this.props.convertFormat == 'markdown' && <SourceEditor editorState={editorState} onToggle={this.toggleSource} coverTitle={this.state.showSourceEditor} lang={lang[this.state.language]} />}
</div>
);
}
render() {
let urlInput;
// ref="urltext"
if (this.state.showURLInput) {
urlInput = (
<Modal
title={lang[this.state.language].directToURL}
visible={this.state.visible}
onOk={this.confirmLink}
onCancel={this.handleCancel}
closable={false}
okText={lang[this.state.language].OKText}
cancelText={lang[this.state.language].cancelText}
>
<Input
type='text'
onChange={this.onURLChange}
value={this.state.urlValue}
placeholder='http:// or https://'
onKeyDown={this.onLinkInputKeyDown}
/>
<span style={{ color: 'red' }}>{lang[this.state.language].directToURLTip}</span>
</Modal>
);
}
const { editorState } = this.state;
// If the user changes block type before entering any text, we can either style the placeholder or hide it. Let's just
// hide it now.
let className = 'RichEditor-editor';
const contentState = editorState.getCurrentContent();
if (!contentState.hasText()) {
if (contentState.getBlockMap().first().getType() !== 'unstyled') {
className += ' RichEditor-hidePlaceholder';
}
}
// console.log("this.props.undoRedo",this.props.undoRedo)//https://gist.github.com/deanmcpherson/69f9962b744b273ffb64fe294ab71bc4
// <Affix offsetTop={0} id="text-editor-affix">
// </Affix>
return (
<div className={`RichEditor-root ${this.props.wrapperClass} editorHidden ${this.props.disabled ? 'disabled-editor' : ''}`} content={this.state.HTML} id='text-editor-container'>
{ !this.props.readOnly && this.renderToolBar(editorState)}
<div className={`editor-board ${className}`} onClick={this.focus} style={{ display: this.state.showMarkdownSource == true ? 'none' : 'block' }}>
<Editor
className=''
blockRendererFn={mediaBlockRenderer}
editorState={this.state.editorState}
blockStyleFn={getBlockStyle}
customStyleMap={styleMap}
customStyleMap={colorStyleMap}
editorState={editorState}
handleKeyCommand={this.handleKeyCommand}
keyBindingFn={this.customKeyBinding}
onChange={this.onChange}
handlePastedText={this.handlePastedText}
spellCheck
readOnly={this.props.readOnly}
{...this.props.editorProps}
/>
</div>
<div style={{ display: this.state.showMarkdownSource == true ? 'block' : 'none', height: '500px', width: '100%' }}>
<textarea
style={{ height: '100%', width: '100%', overflowY: 'visible' }}
onChange={this.changeMrakdownContent}
value={this.state.tempSouceContent || this.props.importContent}
placeholder={lang[this.state.language].markdownTip}
/>
</div>
<div className='disabled-mask' style={{ display: this.props.disabled ? 'block' : 'none' }} />
{urlInput}
</div>
);
// ref="editor"
}
}
// Custom overrides for "code" style.
const styleMap = {
CODE: {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
fontSize: 16,
padding: 2
}
};
function getBlockStyle(block) {
// console.log("getBlockStyle block",block,JSON.stringify(block))
const type = block.getType();
const data = block.getData();
const text = block.getText();
// console.log("getBlockStyle get data",JSON.stringify(data))
let mergedStyle = '';
switch (type) {
case 'blockquote':
mergedStyle = 'RichEditor-blockquote';
break;
}
// console.log("getBlockStyle mergingStyle",mergedStyle)
if (!data.has('textAlignment')) {
return mergedStyle;
}
switch (data.get('textAlignment')) {
case 'left':
mergedStyle += ' RichEditor-alignment-left';
break;
case 'center':
mergedStyle += ' RichEditor-alignment-center';
break;
case 'right':
mergedStyle += ' RichEditor-alignment-right';
break;
case 'justify':
mergedStyle += ' RichEditor-alignment-justify';
break;
}
// console.log("getBlockStyle mergedStyle",mergedStyle)
return mergedStyle;
}
function mediaBlockRenderer(block) {
// console.log("block",block); console.log("1111111block.getType() ",block.getType());
if (block.getType() === 'atomic') {
// console.log("11112222block.getType() ",block.getType());
return { component: Media, editable: false };
}
return null;
}
const Audio = props => <audio controls src={props.src} className='media' />;
const Image = props =>
// console.log("props",props.src);
<img src={props.src} className='media' />;
const Video = props => <video controls src={props.src} className='media' />;
const Media = (props) => {
const entity = Entity.get(props.block.getEntityAt(0));
const { src } = entity.getData();
const type = entity.getType();
// console.log("Media type",src);
// console.log("Media entity",type);
let media;
if (type === 'audio') {
media = <Audio src={src} />;
} else if (type === 'image') {
media = <Image src={src} />;
} else if (type === 'video') {
media = <Video src={src} />;
}
return media;
};
EditorConcist.propTypes = {
wrapperClass: PropTypes.string,
disabled: PropTypes.bool,
active: PropTypes.bool,
importContent: PropTypes.string,
cbReceiver: PropTypes.func.isRequired,
undoRedo: PropTypes.bool,
removeStyle: PropTypes.bool,
pasteNoStyle: PropTypes.bool,
blockStyle: PropTypes.bool,
alignment: PropTypes.bool,
inlineStyle: PropTypes.bool,
color: PropTypes.bool,
image: PropTypes.bool,
video: PropTypes.bool,
audio: PropTypes.bool,
urls: PropTypes.bool,
autoSave: PropTypes.bool,
fullScreen: PropTypes.bool,
readOnly: PropTypes.bool,
uploadConfig: PropTypes.shape({
QINIU_URL: PropTypes.string.isRequired,
QINIU_IMG_TOKEN_URL: PropTypes.string.isRequired,
QINIU_PFOP: PropTypes.shape({
url: PropTypes.string.isRequired
}),
QINIU_VIDEO_TOKEN_URL: PropTypes.string.isRequired,
QINIU_FILE_TOKEN_URL: PropTypes.string.isRequired,
QINIU_DOMAIN_IMG_URL: PropTypes.string.isRequired,
QINIU_DOMAIN_VIDEO_URL: PropTypes.string.isRequired,
QINIU_DOMAIN_FILE_URL: PropTypes.string.isRequired
}),
convertFormat: PropTypes.oneOf(['html', 'markdown', 'raw']),
};
EditorConcist.defaultProps = {
undoRedo: true,
removeStyle: true,
pasteNoStyle: true,
blockStyle: true,
alignment: true,
inlineStyle: true,
color: true,
image: true,
video: true,
audio: true,
urls: true,
autoSave: true,
fullScreen: true,
convertFormat: 'html',
readOnly: false,
editorProps: {},
};
// export default EditorConcist;
module.exports = EditorConcist;