jpuri / react-draft-wysiwyg

A Wysiwyg editor build on top of ReactJS and DraftJS. https://jpuri.github.io/react-draft-wysiwyg
MIT License
6.43k stars 1.16k forks source link

Edit html #315

Open nojaf opened 7 years ago

nojaf commented 7 years ago

Is it possible to add a button to edit the html source directly like a toggle mode?

jpuri commented 7 years ago

Hi @nojaf,

HTML editing is very hard with DraftJS, and atm not possible with react-draft-wysiwyg.

abologna-r7 commented 7 years ago

@nojaf you could potentially use draftToHtml and htmltoDraft plugins to achieve the toggling somehow. From raw HTML to DraftJS you will need to have maybe just a plain textbox (no toolbar), and somehow hide the editor while on "expert/html mode"

JosephScript commented 6 years ago

I just solved this problem myself. Here's a full working example, including using autosize to resize the editor:

import React, { PureComponent } from 'react'
import autosize from 'autosize'
import { Editor } from 'react-draft-wysiwyg'
import { EditorState, ContentState, convertToRaw } from 'draft-js'
import draftToHtml from 'draftjs-to-html'
import htmlToDraft from 'html-to-draftjs'
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'

export default class EditBlog extends PureComponent {
  state = {
    editor: EditorState.createEmpty(),
    editorHTML: '',
    showCode: false
  }

  onEditorStateChange = editor => {
    const editorHTML = draftToHtml(convertToRaw(editor.getCurrentContent()))
    this.setState({ editor, editorHTML })
  }

  onEditEditorHTML = e => {
    const editorHTML = e.target.value

    let editor
    const contentBlock = htmlToDraft(editorHTML)
    if (contentBlock) {
      const contentState = ContentState.createFromBlockArray(contentBlock.contentBlocks)
      editor = EditorState.createWithContent(contentState)
    } else {
      editor = EditorState.createEmpty()
    }
    this.setState({ editor, editorHTML })
  }

  toggleEditorCode = () => {
    const { showEditorCode } = this.state
    this.setState({ showEditorCode: !showEditorCode }, () => {
      if (!showEditorCode) {
        autosize(this.textareaEditor)
        autosize.update(this.textareaEditor)
      } else {
        autosize.destroy(this.textareaEditor)
      }
    })
  }

  submit = e => {
    e.preventDefault()
    const { editorHTML } = this.state
    console.log(editorHTML)
  }

  render () {
    const {
      editor,
      editorHTML,
      showEditorCode
    } = this.state

    const ShowEditorCode = () => (
      <div className="rdw-option-wrapper"
        onClick={this.toggleEditorCode}>
        {showEditorCode ? 'Hide' : 'Show'} Code
      </div>)

    return (
      <form name="form" onSubmit={this.submit} autoComplete="off">
        <div>
              <Editor
                editorState={editor}
                editorClassName={showEditorCode ? 'editor' : 'editorHide'}
                onEditorStateChange={this.onEditorStateChange}
                toolbarCustomButtons={[<ShowEditorCode />]}
              />
            { showEditorCode && <textarea
              ref={c => { this.textareaEditor = c }}
              value={editorHTML}
              onChange={this.onEditEditorHTML}
            /> }
          </div>
        <div>
            <button type="submit">Submit</button>
        </div>
      </form>
    )
  }
}
.editor {
  display: 'inherit';
}

.editorHide {
  display: 'none';
}
Huespal commented 5 years ago

Thank you for your example @NukaPunk.

I want to improve it.

Html set in 'code view' will be formatted by the editor. You set in 'code' view:

<div>Test</div> and you get

<p>Test</p>.

in result.

How to keep html set in 'code' view? Maybe using another function instead of htmlToDraft(editorHTML)?

Thanks

claytonrothschild commented 5 years ago

@Huespal I am a little confused as to the state of HTML editing in react-draft-wysiwyg due to your comments on the issue in multiple places.

Can you describe in further detail the problem on this particular implementation? Is it as follows: Can edit WYSIWYG content. Can then toggle to code view. Can then enter raw html and save. However, if you click back into WYSIWYG view after entering HTML content, some tags will be lost.

Is that correct?

hodgef commented 4 years ago

I would recommend anyone looking for this feature to use Jodit instead. It has built-in HTML editor and the experience is very good out of the box.

Sadly react-draft-wysiwyg has been a painful experience for me. Hours upon hours of trying to make things work. I hope to spare someone the hassle.

galoberlyn commented 3 years ago

react-draft-wysiwyg is a great editor but sadly it has also limitations.

I would also recommend using react-quill (it's free). This editor will pass in strings that are in html format then from there you can manipulate your wysiwyg content.

vermarohitrk1 commented 2 years ago

Here is full code of customised react-draftjs, But this is still useless, you can not save your own HTML as you expect.

import React, { PureComponent } from 'react'
import autosize from 'autosize'
import { Editor } from 'react-draft-wysiwyg'
import { EditorState, ContentState, convertToRaw } from 'draft-js'
import draftToHtml from 'draftjs-to-html'
import htmlToDraft from 'html-to-draftjs'
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
import './style.css'
interface IProps {
    onChange?: Function;
    html?: string;
  }

export default class WYSIWYG extends PureComponent<IProps> {
    textareaEditor: any

    state = {
        editor: '',
        editorHTML:'',
        showCode: false,
        showEditorCode: false
      }
    constructor(props: any) {
        super(props);
        this.textareaEditor = React.createRef();
      }

      componentDidMount(){
        const { html } = this.props;
        if (html) {
          this.setState({
              editorHTML: html });
          const blocksFromHtml = htmlToDraft(html);
          const { contentBlocks, entityMap } = blocksFromHtml;
          const contentState = ContentState.createFromBlockArray(
            contentBlocks,
            entityMap
          );
          console.log(contentState);
          this.setState({
              editor: EditorState.createWithContent(contentState)
          });
        } else {
          this.setState({
              editor: EditorState.createEmpty()
          });
        }
      }

  onEditorStateChange = editor => {
    const { onChange } = this.props;
    const editorHTML = draftToHtml(convertToRaw(editor.getCurrentContent()))
    onChange && onChange({
      html: editorHTML
    });
    console.log(this.props.html);
    this.setState({ editor, editorHTML })
  }

  onEditEditorHTML = e => {
    const editorHTML = e.target.value
    const { onChange } = this.props;
    let editor
    const contentBlock = htmlToDraft(editorHTML)
    if (contentBlock) {
      const contentState = ContentState.createFromBlockArray(contentBlock.contentBlocks)
      editor = EditorState.createWithContent(contentState)
    } else {
      editor = EditorState.createEmpty()
    }
    onChange && onChange({
      html: editorHTML
    });
    this.setState({ editor, editorHTML })
  }

  toggleEditorCode = () => {
    const { showEditorCode } = this.state
    this.setState({ showEditorCode: !showEditorCode })
  }

  submit = e => {
    e.preventDefault()
    const { editorHTML } = this.state
    console.log(editorHTML)
  }

  render () {
    const {
      editor,
      editorHTML,
      showEditorCode
    } = this.state

    const ShowEditorCode = () => (
      <div className="rdw-option-wrapper"
        onClick={this.toggleEditorCode}>
        {showEditorCode ? 'Hide' : 'Show'} Code
      </div>)

    return (
        <div className="editor">
              <Editor
                editorState={editor}
                wrapperClassName="wysityg-wrapper"
                editorClassName={showEditorCode ? 'editorHide wysityg-editor' : 'editor wysityg-editor'}
                onEditorStateChange={this.onEditorStateChange}
                toolbarCustomButtons={[<ShowEditorCode />]}
              />
            { showEditorCode && <textarea className="wysityg-editor rdw-editor-main" style={{width:'100%'}}
              ref={this.textareaEditor}
              value={editorHTML}
              onChange={this.onEditEditorHTML}
            /> }
          </div>
    )
  }
}

import in your page

const WYSIWYG = dynamic(() => import('@components/wysiwyg/wysiwyg'), {
  ssr: false
});
export class component extends PureComponent {
   render() {
    const { post, submitting } = this.state;
    return (
      <>
      <WYSIWYG onChange={this.contentChange.bind(this)} html={this._content} />
      </>
      )}
}