Reputation: 20431
Using DraftJs with WYSWYG: I need to create a custom button in the toolbar. On clicking the button, some custom HTML (which includes <button>
) is added to the content itself.
I know how to add a custom button, the docs are there. The button gets added and onclick I call the defined method insertText
. But with insertText
(a method in the DraftModifier class of draft.js) it only seems to add my HTML code as a string. Is there a different method I should use to add custom HTML into the content. I want to add real HTML.
Here is the TS declaration:
static insertText(
contentState: ContentState,
targetRange: SelectionState,
text: string,
inlineStyle?: DraftInlineStyle,
entityKey?: string
): ContentState;
Upvotes: 0
Views: 2156
Reputation: 20775
You can use replaceWithFragment. To build the fragment you want to insert, first convert the string to HTML blocks with the utility convertFromHTML
const htmlContent = convertFromHTML(text);
then, use another utility to pass the array of content blocks to a map
const htmlContentMap = BlockMapBuilder.createFromArray(
htmlContent.contentBlocks
);
and call replaceWithFragment
the same way you were calling insertText
, but with the map of HTML content you built
const newContent = Modifier.replaceWithFragment(
currentContent,
currentSelection,
htmlContentMap
);
UPDATE
DraftJS doesn't support the button tag by default but you can add it as a custom block render
first, create a map of types by merging the default supported tags with the button tag
import {
...
DefaultDraftBlockRenderMap,
...
} from "draft-js";
...
const mapWithButton = Map({
button: {
element: "button"
}
}).merge(DefaultDraftBlockRenderMap);
...
and pass it as the third argument to the convertFromHTML
function
const htmlContent = convertFromHTML(
text,
undefined,
mapWithButton
);
create a custom block renderer function and pass it to the Editor
function myBlockRenderer(contentBlock: ContentBlock) {
const type = contentBlock.getType();
if (type === "button") {
return {
component: () => {
return (
<button onClick={() => console.log("doesn't seem to work :(")}>
{contentBlock.getText()}
</button>
);
},
editable: false,
};
}
}
...
<Editor
...
customBlockRenderFunc={myBlockRenderer}
/>
It kind of works because it shows the button, although sometimes when there is text before the point when you insert it, it merges the previous text with the button text without showing the button. The click doesn't work either probably because of this
If your custom block renderer requires mouse interaction, it is often wise to temporarily set your Editor to readOnly={true} during this interaction. In this way, the user does not trigger any selection changes within the editor while interacting with the custom block.
https://codesandbox.io/s/insert-html-draftjs-73rmc?file=/src/Editor.tsx
Upvotes: 2
Reputation: 10460
In the documentation of react-draft-wysiwyg, there's a dedicated part for using html, cited below. The essential steps are:
ContentBlock
.
convertFromHtml
.ContentState
with that ContentBlock
.ContentEditor
with that ContentState
Editor
's editorState
with that ContentEditor
object.Here are two live examples for achieving that: 1, 2.
Citation from the documentation of react-draft-wysiwyg:
draftjs-to-html can be used to convert
RawDraftContentState
to html.
html-to-draftjs provides the option to convert HTML generated by react-draft-wysiwyg back to draftJSContentState
which can be used to initialize the Editor.import React, { Component } from 'react'; import { EditorState, convertToRaw, ContentState } from 'draft-js'; import { Editor } from 'react-draft-wysiwyg'; import draftToHtml from 'draftjs-to-html'; import htmlToDraft from 'html-to-draftjs'; class EditorConvertToHTML extends Component { constructor(props) { super(props); const html = '<p>Hey this <strong>editor</strong> rocks 😀</p>'; const contentBlock = htmlToDraft(html); if (contentBlock) { const contentState = ContentState.createFromBlockArray(contentBlock.contentBlocks); const editorState = EditorState.createWithContent(contentState); this.state = { editorState, }; } } onEditorStateChange: Function = (editorState) => { this.setState({ editorState, }); }; render() { const { editorState } = this.state; return ( <div> <Editor editorState={editorState} wrapperClassName="demo-wrapper" editorClassName="demo-editor" onEditorStateChange={this.onEditorStateChange} /> <textarea disabled value={draftToHtml(convertToRaw(editorState.getCurrentContent()))} /> </div> ); } }
Upvotes: 0