5.Draftjs 学习笔记-自定义控件(多媒体)

偷个懒先贴上两个文档

1.block-render主要用来自定义渲染的,一般是通过css,或者更换标签,当然也可以用自定义组件
在处理粘贴进来的内容时,有着映射,粘贴这块我没试过,render的实验在第2篇中最后那个例子中有。

2.block-component 用来做一些复杂的组件比如多媒体组件和一些有交互事件的组件。

这个就不想洗赘述了,贴上一个多媒体组件的,在2中例子基础上加的,添加了左中右对齐(但是有点bug,左中右通过替换标签方式实现的,不是叠加样式,所以标题和对齐冲突),还添加了个图片粘贴功能(图片不能和文本一起粘贴,分开粘贴没问题,一起粘贴图片就被忽略了,直接粘贴文件不知道为何出错,如果有人解决了可以告诉我下,拖放文件没测试成功

###components/MyEditor.jsx

import React from 'react';
import { Editor, EditorState, RichUtils, DefaultDraftBlockRenderMap, AtomicBlockUtils, convertToRaw, Entity, } from 'draft-js';
import Immutable from 'immutable';
import "draft-js/dist/Draft.css";
import styles from './Rich.less'
const blockRenderMap = Immutable.Map({
    'header-two': {
        element: 'h2',
        aliasedElements: ['p'],
    },
    'left': {
        element: 'div',
    },
    'right': {
        element: 'div',
    },
    'center': {
        element: 'div',
    },
});
class MyEditor extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            editorState: EditorState.createEmpty(),
            showURLInput: false,
            url: '',
            urlType: '',
        };

        this.focus = () => this.refs.editor.focus();
        this.logState = () => {
            const content = this.state.editorState.getCurrentContent();
            console.log(convertToRaw(content));
        };
        this.onURLChange = (e) => this.setState({ urlValue: e.target.value });

        this.addAudio = this._addAudio.bind(this);
        this.addImage = this._addImage.bind(this);
        this.addVideo = this._addVideo.bind(this);
        this.confirmMedia = this._confirmMedia.bind(this);
        this.pasteMedia = this._pasteImage.bind(this);
        this.onChange = (editorState) => this.setState({ editorState });
        this.handleKeyCommand = (command) => this._handleKeyCommand(command);
        this.onURLInputKeyDown = this._onURLInputKeyDown.bind(this);
        this.onTab = (e) => this._onTab(e);
        this.toggleBlockType = (type) => this._toggleBlockType(type);
        this.toggleInlineStyle = (style) => this._toggleInlineStyle(style);
    }

    _handleKeyCommand(command) {
        const {editorState} = this.state;
        const newState = RichUtils.handleKeyCommand(editorState, command);
        if (newState) {
            this.onChange(newState);
            return true;
        }
        return false;
    }

    _onTab(e) {
        const maxDepth = 4;
        this.onChange(RichUtils.onTab(e, this.state.editorState, maxDepth));
    }

    _toggleBlockType(blockType) {
        this.onChange(
            RichUtils.toggleBlockType(
                this.state.editorState,
                blockType
            )
        );
    }

    _toggleInlineStyle(inlineStyle) {
        this.onChange(
            RichUtils.toggleInlineStyle(
                this.state.editorState,
                inlineStyle
            )
        );
    }

    _confirmMedia(e) {
        e.preventDefault();
        const {editorState, urlValue, urlType} = this.state;
        const entityKey = Entity.create(urlType, 'IMMUTABLE', { src: urlValue })

        this.setState({
            editorState: AtomicBlockUtils.insertAtomicBlock(
                editorState,
                entityKey,
                ' '
            ),
            showURLInput: false,
            urlValue: '',
        }, () => {
            setTimeout(() => this.focus(), 0);
        });
    }

    _pasteImage(files) {
        console.log('files',files);
        const {editorState} = this.state;
        let _self = this;
        for (var i = 0; i < files.length; i++) {
            if (files[i].type.indexOf("image") !== -1) {
                // We need to represent the image as a file,
                var blob = files[i];
                let reader = new FileReader();
                reader.readAsDataURL(blob);
                reader.onloadend = function () {
                    let base64data = reader.result;
                    const entityKey = Entity.create('image', 'IMMUTABLE', { src: base64data })

                    _self.setState({
                        editorState: AtomicBlockUtils.insertAtomicBlock(
                            editorState,
                            entityKey,
                            ' '
                        ),
                        showURLInput: false,
                        urlValue: '',
                    }, () => {
                        setTimeout(() => _self.focus(), 0);
                    });
                }
            }
        }
    }

    _onURLInputKeyDown(e) {
        if (e.which === 13) {
            this._confirmMedia(e);
        }
    }

    _promptForMedia(type) {
        const {editorState} = this.state;
        this.setState({
            showURLInput: true,
            urlValue: '',
            urlType: type,
        }, () => {
            setTimeout(() => this.refs.url.focus(), 0);
        });
    }

    _addAudio() {
        this._promptForMedia('audio');
    }

    _addImage() {
        this._promptForMedia('image');
    }

    _addVideo() {
        this._promptForMedia('video');
    }

    render() {
        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 = styles['RichEditor-editor'];
        var contentState = editorState.getCurrentContent();
        if (!contentState.hasText()) {
            if (contentState.getBlockMap().first().getType() !== 'unstyled') {
                className += ' ' + styles['RichEditor-hidePlaceholder'];
            }
        }

        let urlInput;
        if (this.state.showURLInput) {
            urlInput =
                
; } return (
Use the buttons to add audio, image, or video.
Here are some local examples that can be entered as a URL:
  • media.mp3
  • media.png
  • media.mp4
{urlInput}
(console.log('paste', value))} handlePastedFiles={this.pasteMedia} handleDroppedFiles={this.pasteMedia} onChange={this.onChange} onTab={this.onTab} placeholder="Tell a story..." ref='editor' spellCheck={true} onPaste={(value) => (console.log('paste', value))} />
); } } const styleMap = { CODE: { backgroundColor: 'rgba(0, 0, 0, 0.05)', fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace', fontSize: 16, padding: 2, }, }; function getBlockStyle(block) { switch (block.getType()) { case 'blockquote': return styles['RichEditor-blockquote']; case 'left': return styles['align-left']; case 'center': return styles['align-center']; case 'right': return styles['align-right']; default: return null; } } class StyleButton extends React.Component { constructor() { super(); this.onToggle = (e) => { e.preventDefault(); this.props.onToggle(this.props.style); }; } render() { let className = styles['RichEditor-styleButton']; if (this.props.active) { className += ' ' + styles['RichEditor-activeButton']; } return ( {this.props.label} ); } } const BLOCK_TYPES = [ { label: 'H1', style: 'header-one' }, { label: 'H2', style: 'header-two' }, { label: 'H3', style: 'header-three' }, { label: 'H4', style: 'header-four' }, { label: 'H5', style: 'header-five' }, { label: 'H6', style: 'header-six' }, { label: 'Blockquote', style: 'blockquote' }, { label: 'left', style: 'left' }, { label: 'right', style: 'right' }, { label: 'center', style: 'center' }, { label: 'UL', style: 'unordered-list-item' }, { label: 'OL', style: 'ordered-list-item' }, { label: 'Code Block', style: 'code-block' }, ]; const BlockStyleControls = (props) => { const {editorState} = props; const selection = editorState.getSelection(); const blockType = editorState .getCurrentContent() .getBlockForKey(selection.getStartKey()) .getType(); return (
{BLOCK_TYPES.map((type) => )}
); }; var INLINE_STYLES = [ { label: 'Bold', style: 'BOLD' }, { label: 'Italic', style: 'ITALIC' }, { label: 'Underline', style: 'UNDERLINE' }, { label: 'Monospace', style: 'CODE' }, ]; const InlineStyleControls = (props) => { var currentStyle = props.editorState.getCurrentInlineStyle(); return (
{INLINE_STYLES.map(type => )}
); }; function mediaBlockRenderer(block) { if (block.getType() === 'atomic') { return { component: Media, editable: false, }; } return null; } const Audio = (props) => { return

###样式文件 components/Rich.less

.RichEditor-root {
  background: #fff;
  border: 1px solid #ddd;
  font-family: 'Georgia', serif;
  font-size: 14px;
  padding: 15px;
}

.RichEditor-editor {
  border-top: 1px solid #ddd;
  cursor: text;
  font-size: 16px;
  margin-top: 10px;
}

.RichEditor-editor .public-DraftEditorPlaceholder-root,
.RichEditor-editor .public-DraftEditor-content {
  margin: 0 -15px -15px;
  padding: 15px;
}

.RichEditor-editor .public-DraftEditor-content {
  min-height: 100px;
}

.RichEditor-hidePlaceholder .public-DraftEditorPlaceholder-root {
  display: none;
}

.RichEditor-editor .RichEditor-blockquote {
  border-left: 5px solid #eee;
  color: #666;
  font-family: 'Hoefler Text', 'Georgia', serif;
  font-style: italic;
  margin: 16px 0;
  padding: 10px 20px;
}

.RichEditor-editor .public-DraftStyleDefault-pre {
  background-color: rgba(0, 0, 0, 0.05);
  font-family: 'Inconsolata', 'Menlo', 'Consolas', monospace;
  font-size: 16px;
  padding: 20px;
}

.RichEditor-controls {
  font-family: 'Helvetica', sans-serif;
  font-size: 14px;
  margin-bottom: 5px;
  user-select: none;
}

.RichEditor-styleButton {
  color: #999;
  cursor: pointer;
  margin-right: 16px;
  padding: 2px 0;
  display: inline-block;
}

.RichEditor-activeButton {
  color: #5890ff;
}

.align-right div {
    text-align: right;
}
.align-center div {
    text-align: center;
}
.align-left div {
    text-align: left;
}

#运行效果
5.Draftjs 学习笔记-自定义控件(多媒体)_第1张图片

你可能感兴趣的:(draftjs,多媒体,控件,react,draftjs,文档)