Open nojaf opened 7 years ago
Hi @nojaf,
HTML editing is very hard with DraftJS, and atm not possible with react-draft-wysiwyg.
@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"
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';
}
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
@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?
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.
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.
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} />
</>
)}
}
Is it possible to add a button to edit the html source directly like a toggle mode?