Search code examples
javascriptreactjsdraftjsdraft-js-pluginsreact-draft-wysiwyg

React Draft.js Wysiwyg: How to programmatically insert text at the cursor location?


I am using the React Draft Wysiwyg and I need to insert arbitrary text from one component of my application into the editor component. I am doing this via the clipboard as an intermediary for the transfer from one component to the other. But the document.execCommand('paste') fails.

Does anybody know how to do this?

My sample code is here; the third console log emits false for the paste result.

import React, { Component } from 'react';
import { EditorState, convertToRaw, Modifier } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
import draftToHtml from 'draftjs-to-html';
import htmlToDraft from 'html-to-draftjs';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import styled from 'styled-components';

class EditorConvertToHTML extends Component {
  constructor(props) {
    super(props);
    this.state = {editorState: EditorState.createEmpty()};
    this.onChange = (editorState) => this.setState({editorState});
    this.setEditor = (editor) => {
      this.editor = editor;
    };
    this.focusEditor = () => {
      if (this.editor) {
        this.editor.focusEditor();
        console.log("1. Editor has the focus now");
      }
    };
  }

  componentDidMount() {
    this.focusEditor();
  }

  onEditorStateChange = (editorState) => {
    this.setState({
      editorState,
    });
  };

  sendTextToEditor = (text) => {
    var input = document.createElement('input');
    input.setAttribute('value', text);
    document.body.appendChild(input);
    input.select();
    var result = document.execCommand('copy');
    document.body.removeChild(input);

    navigator.clipboard.readText()
    .then(text => {
      this.focusEditor();
      console.log('2. Retrieved pasted text from clipboard = ', text);
      result = document.execCommand('paste');
      console.log("3. Paste command's result = ", result);
    })
    .catch(err => {
      console.error('Failed to read clipboard contents: ', err);
    });

    return result;
  }

  render() {
    const { editorState } = this.state;
    return (
      <>
        <EditorStyled>
          <Editor
            ref={this.setEditor}
            editorState={editorState}
            wrapperClassName="demo-wrapper"
            editorClassName="demo-editor"
            onEditorStateChange={this.onEditorStateChange}
          />
        </EditorStyled>
        <button type="button" onClick={this.sendTextToEditor.bind(this, 'Sample text to put in editor')}>Copy sample text to editor</button>
      </>
    );
  }
}

export default EditorConvertToHTML;

const EditorStyled = styled.div`
  width: ${() => "calc(100% - 40px)"};
  min-height: 400px;
  margin: 20px;
  border: 1px solid black;
`;

Thank you in advance for your help!


Solution

  • Thanks to Rosemarie Robertson's explanations/article @ https://dev.to/rose/draft-js-simple-content-manipulation-b7a I got the sample working. Here is the code:

    import React, { Component } from 'react';
    import { EditorState, Modifier } from 'draft-js';
    import { Editor } from 'react-draft-wysiwyg';
    import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
    import styled from 'styled-components';
    
    // Following sample is based the article https://dev.to/rose/draft-js-simple-content-manipulation-b7a
    
    class EditorConvertToHTML extends Component {
      constructor(props) {
        super(props);
        this.state = {editorState: EditorState.createEmpty()};
      }
    
      componentDidMount() {
        this.focusEditor();
      }
    
      setEditor = (editor) => {
        this.editor = editor;
      };
    
      focusEditor = () => {
        if (this.editor) {
          this.editor.focusEditor();
          console.log("1. Editor has the focus now");
        }
      };
    
      onEditorStateChange = (editorState) => {
        this.setState({
          editorState,
        });
      };
    
      sendTextToEditor = (text) => {
        this.setState({editorState: this.insertText(text, this.state.editorState)});
        this.focusEditor();
      }
    
      insertText = (text, editorState) => {
        const currentContent = editorState.getCurrentContent(),
              currentSelection = editorState.getSelection();
    
        const newContent = Modifier.replaceText(
          currentContent,
          currentSelection,
          text
        );
    
        const newEditorState = EditorState.push(editorState, newContent, 'insert-characters');
        return  EditorState.forceSelection(newEditorState, newContent.getSelectionAfter());
      }
    
      render() {
        const { editorState } = this.state;
        return (
          <>
            <EditorStyled>
              <Editor
                ref={this.setEditor}
                editorState={editorState}
                wrapperClassName="demo-wrapper"
                editorClassName="demo-editor"
                onEditorStateChange={this.onEditorStateChange}
              />
            </EditorStyled>
            <button type="button" onClick={this.sendTextToEditor.bind(this, 'Sample text to put in editor')}>Copy sample text to editor</button>
          </>
        );
      }
    }
    
    export default EditorConvertToHTML;
    
    const EditorStyled = styled.div`
      width: ${() => "calc(100% - 40px)"};
      min-height: 400px;
      margin: 20px;
      border: 1px solid black;
    `;