Skip to content

Instantly share code, notes, and snippets.

@ds604
Created October 27, 2018 22:42
Show Gist options
  • Save ds604/6e82cdbf4225b0fe1e7d9917e6bd3b1a to your computer and use it in GitHub Desktop.
Save ds604/6e82cdbf4225b0fe1e7d9917e6bd3b1a to your computer and use it in GitHub Desktop.

Revisions

  1. ds604 created this gist Oct 27, 2018.
    16 changes: 16 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    <!--
    Source https://github.com/facebook/draft-js/tree/master/examples/draft-0-10-0/rich
    -->
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <title>Draft • Rich Text</title>
    <style>
    #target { width: 600px; }
    </style>
    </head>
    <body>
    <div id="target"></div>
    </body>
    </html>
    7 changes: 7 additions & 0 deletions rich-text-editor-draft-js-example.markdown
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    Rich Text Editor - Draft.js example
    -----------------------------------


    A [Pen](https://codepen.io/ds604/pen/pxYwrg) by [ds604](https://codepen.io/ds604) on [CodePen](https://codepen.io).

    [License](https://codepen.io/ds604/pen/pxYwrg/license).
    196 changes: 196 additions & 0 deletions script.babel
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,196 @@
    'use strict';

    const {Editor, EditorState, RichUtils} = Draft;

    class RichEditorExample extends React.Component {
    constructor(props) {
    super(props);
    this.state = {editorState: EditorState.createEmpty()};

    this.focus = () => this.refs.editor.focus();
    this.onChange = (editorState) => this.setState({editorState});

    this.handleKeyCommand = (command) => this._handleKeyCommand(command);
    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
    )
    );
    }

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

    return (
    <div className="RichEditor-root">
    <BlockStyleControls
    editorState={editorState}
    onToggle={this.toggleBlockType}
    />
    <InlineStyleControls
    editorState={editorState}
    onToggle={this.toggleInlineStyle}
    />
    <div className={className} onClick={this.focus}>
    <Editor
    blockStyleFn={getBlockStyle}
    customStyleMap={styleMap}
    editorState={editorState}
    handleKeyCommand={this.handleKeyCommand}
    onChange={this.onChange}
    onTab={this.onTab}
    placeholder="Tell a story..."
    ref="editor"
    spellCheck={true}
    />
    </div>
    </div>
    );
    }
    }

    // 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) {
    switch (block.getType()) {
    case 'blockquote': return 'RichEditor-blockquote';
    default: return null;
    }
    }

    class StyleButton extends React.Component {
    constructor() {
    super();
    this.onToggle = (e) => {
    e.preventDefault();
    this.props.onToggle(this.props.style);
    };
    }

    render() {
    let className = 'RichEditor-styleButton';
    if (this.props.active) {
    className += ' RichEditor-activeButton';
    }

    return (
    <span className={className} onMouseDown={this.onToggle}>
    {this.props.label}
    </span>
    );
    }
    }

    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: '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 (
    <div className="RichEditor-controls">
    {BLOCK_TYPES.map((type) =>
    <StyleButton
    key={type.label}
    active={type.style === blockType}
    label={type.label}
    onToggle={props.onToggle}
    style={type.style}
    />
    )}
    </div>
    );
    };

    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 (
    <div className="RichEditor-controls">
    {INLINE_STYLES.map(type =>
    <StyleButton
    key={type.label}
    active={currentStyle.has(type.style)}
    label={type.label}
    onToggle={props.onToggle}
    style={type.style}
    />
    )}
    </div>
    );
    };

    ReactDOM.render(
    <RichEditorExample />,
    document.getElementById('target')
    );
    6 changes: 6 additions & 0 deletions scripts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.3/es6-shim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/draft-js/0.10.0/Draft.js"></script>
    63 changes: 63 additions & 0 deletions style.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,63 @@
    .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;
    }
    1 change: 1 addition & 0 deletions styles
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    <link href="https://cdnjs.cloudflare.com/ajax/libs/draft-js/0.10.0/Draft.min.css" rel="stylesheet" />