Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does convertFromHTML support custom block types in Draft.js?

Here's a codepen example of the issue.

I have added a custom block type called section, which wraps up selected text in red. It works fine when you click on the section in the edit toolbar. However, when used with convertFromHTML to render initial content,

const sampleMarkup = '<b>Bold text</b><br/>Section Testing:<section>dsasdasad</section><br/><i>Italic text</i>';

the editor is still treating section type as unstyle type. Does convertFromHTML support custom block types for initial rendering? Is there a solution for this?

Code:

      const {Editor,convertFromHTML,ContentState, EditorState,DefaultDraftBlockRenderMap, RichUtils} = Draft;

      const blockRenderMap = Immutable.Map({
    'section': {
        element: 'section'
    }
});

     const extendedBlockRenderMap = DefaultDraftBlockRenderMap.merge(blockRenderMap);

      class RichEditorExample extends React.Component {
        constructor(props) {
          super(props);
          const sampleMarkup =
  '<b>Bold text</b><br/>Section Testing:<section>dsasdasad</section><br/><i>Italic text</i>';

          const blocksFromHTML = convertFromHTML(sampleMarkup);
          const state = ContentState.createFromBlockArray(
            blocksFromHTML.contentBlocks,
            blocksFromHTML.entityMap
          );

          this.state = {editorState: EditorState.createWithContent(state)};

          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
                  blockRenderMap={extendedBlockRenderMap}
                  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'},
        {label:'section',style:'section'},

      ];

      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')
      );
like image 204
RedGiant Avatar asked May 01 '17 09:05

RedGiant


People also ask

How does draft work Javascript?

To be efficient, Draft. js organizes set of consecutive characters w/ same styles(or same set of styles) in a single object called inlineStyleRanges. To be clear, this is ONLY organized like this in the “convertToRaw” output (for brevity). In actual contentState it is stored the long way for each character.

What is RawDraftContentState?

The Flow type RawDraftContentState denotes the expected structure of the raw format of the contents. The raw state contains a list of content blocks, as well as a map of all relevant entity objects.


2 Answers

I know its a little late but I found a solution for that here. I couldn't find a way to set it at the initial render but if you couldn't set it at all, you can use this.

Example:

// convertToHtml is a custom function which returns convertFromHTML(html)
let htmlTranscript = convertToHtml(paragraphsArray)

// important part
htmlTranscript.contentBlocks[0] = htmlTranscript.contentBlocks[0].set('type', 'myType')

const contentState = ContentState.createFromBlockArray(
                        htmlTranscript.contentBlocks,
                        htmlTranscript.entityMap
                    )

this.setState({
    editorState: EditorState.createWithContent(contentState)
})
like image 72
batatop Avatar answered Oct 22 '22 18:10

batatop


Yes it does.

It's not well documented, but you need to pass the same blockRenderMap that you passed to the Editor, to the third argument of the convertFromHTML function.

like image 42
AaronHS Avatar answered Oct 22 '22 17:10

AaronHS