Giter VIP home page Giter VIP logo

draft-convert's Introduction

draft-convert

npm version License

Extensibly serialize & deserialize Draft.js content with HTML

See draft-extend for more on how to use draft-convert with plugins

Installation

npm install draft-convert --save or yarn add draft-convert

Jump to:

convertToHTML

Extensibly serialize Draft.js ContentState to HTML.

Basic usage:

const html = convertToHTML(editorState.getCurrentContent());

Advanced usage:

// convert to HTML with blue text, paragraphs, and links
const html = convertToHTML({
  styleToHTML: (style) => {
    if (style === 'BOLD') {
      return <span style={{color: 'blue'}} />;
    }
  },
  blockToHTML: (block) => {
    if (block.type === 'PARAGRAPH') {
      return <p />;
    }
  },
  entityToHTML: (entity, originalText) => {
    if (entity.type === 'LINK') {
      return <a href={entity.data.url}>{originalText}</a>;
    }
    return originalText;
  }
})(editorState.getCurrentContent());

// convert content state to HTML with functionality defined in the plugins applied
const html = compose(
    FirstPlugin,
    SecondPlugin,
    ThirdPlugin
)(convertToHTML)(editorState.getCurrentContent());

styleToHTML, blockToHtml, and entityToHTML are functions that take Draft content data and may return a ReactElement or an object of shape {start, end} defining strings for the beginning and end tags of the style, block, or entity. entityToHTML may return either a string with or without HTML if the use case demands it. blockToHTML also may return an optional empty property to handle alternative behavior for empty blocks. To use this along with a ReactElement return value an object of shape {element: ReactElement, empty: ReactElement} may be returned. If no additional functionality is necessary convertToHTML can be invoked with just a ContentState to serialize using just the default Draft functionality. convertToHTML can be passed as an argument to a plugin to modularly augment its functionality.

Legacy alternative conversion options styleToHTML and blockToHTML options may be plain objects keyed by style or block type with values of ReactElement s or {start, end} objects. These objects will eventually be removed in favor of the functions described above.

Type info:

type ContentStateConverter = (contentState: ContentState) => string

type Tag =
  ReactElement |
  {start: string, end: string, empty?: string} |
  {element: ReactElement, empty?: ReactElement}

type RawEntity = {
    type: string,
    mutability: DraftEntityMutability,
    data: Object
}

type RawBlock = {
    type: string,
    depth: number,
    data?: object,
    text: string
}

type convertToHTML = ContentStateConverter | ({
    styleToHTML?: (style: string) => Tag,
    blockToHTML?: (block: RawBlock) => Tag),
    entityToHTML?: (entity: RawEntity, originalText: string) => Tag | string
}) => ContentStateConverter

convertFromHTML

Extensibly deserialize HTML to Draft.js ContentState.

Basic usage:

const editorState = EditorState.createWithContent(convertFromHTML(html));

Advanced usage:

// convert HTML to ContentState with blue text, links, and at-mentions
const contentState = convertFromHTML({
    htmlToStyle: (nodeName, node, currentStyle) => {
        if (nodeName === 'span' && node.style.color === 'blue') {
            return currentStyle.add('BLUE');
        } else {
            return currentStyle;
        }
    },
    htmlToEntity: (nodeName, node, createEntity) => {
        if (nodeName === 'a') {
            return createEntity(
                'LINK',
                'MUTABLE',
                {url: node.href}
            )
        }
    },
    textToEntity: (text, createEntity) => {
        const result = [];
        text.replace(/\@(\w+)/g, (match, name, offset) => {
            const entityKey = createEntity(
                'AT-MENTION',
                'IMMUTABLE',
                {name}
            );
            result.push({
                entity: entityKey,
                offset,
                length: match.length,
                result: match
            });
        });
        return result;
    },
    htmlToBlock: (nodeName, node) => {
        if (nodeName === 'blockquote') {
            return {
                type: 'blockquote',
                data: {}
            };
        }
    }
})(html);

// convert HTML to ContentState with functionality defined in the draft-extend plugins applied
const fromHTML = compose(
    FirstPlugin,
    SecondPlugin,
    ThirdPlugin
)(convertFromHTML);
const contentState = fromHTML(html);

If no additional functionality is necessary convertToHTML can be invoked with just an HTML string to deserialize using just the default Draft functionality. Any convertFromHTML can be passed as an argument to a plugin to modularly augment its functionality. A flat option may be provided to force nested block elements to split into flat, separate blocks. For example, the HTML input <p>line one<br />linetwo</p> will produce two unstyled blocks in flat mode.

Type info:

type HTMLConverter = (html: string, {flat: ?boolean}, DOMBuilder: ?Function, generateKey: ?Function) => ContentState

type EntityKey = string

type convertFromHTML = HTMLConverter | ({
    htmlToStyle: ?(nodeName: string, node: Node) => DraftInlineStyle,
    htmlToBlock: ?(nodeName: string, node: Node) => ?(DraftBlockType | {type: DraftBlockType, data: object} | false),
    htmlToEntity: ?(
        nodeName: string,
        node: Node,
        createEntity: (type: string, mutability: string, data: object) => EntityKey,
        getEntity: (key: EntityKey) => Entity,
        mergeEntityData: (key: string, data: object) => void,
        replaceEntityData: (key: string, data: object) => void
    ): ?EntityKey,
    textToEntity: ?(
        text: string,
        createEntity: (type: string, mutability: string, data: object) => EntityKey,
        getEntity: (key: EntityKey) => Entity,
        mergeEntityData: (key: string, data: object) => void,
        replaceEntityData: (key: string, data: object) => void
    ) => Array<{entity: EntityKey, offset: number, length: number, result: ?string}>
}) => HTMLConverter

Middleware functions

Any conversion option for convertToHTML or convertFromHTML may also accept a middleware function of shape (next) => (…args) => result , where …args are the normal configuration function paramaters and result is the expected return type for that function. These functions can transform results of the default conversion included in convertToHTML or convertFromHTML by leveraging the result of next(...args). These middleware functions are most useful when passed as the result of composition of draft-extend plugins. If you choose to use them independently, a __isMiddleware property must be set to true on the function for draft-convert to properly handle it.

draft-convert's People

Contributors

0rvar avatar benbriggs avatar brycedorn avatar eugenijusr avatar hardfist avatar herr-vogel avatar ltazmin avatar maxwellskala avatar nghuuphuoc avatar oscarmorrison avatar rayd avatar sugarshin avatar tomkelsey avatar trysound avatar tu4mo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

draft-convert's Issues

Stuck in endless loop (crashes browser)

Hey Guys,

I'm using the convertToHTML method and I managed to get stuck into an endless loop with it, that crashes the browser. Just wanted you to be aware of it.

This is how I used it:

import{ convertToHTML } from "draft-convert";
const html = convertToHTML(this.state.editorState.getCurrentContent());

The only special thing I do before calling this method was to initialize Draft.js with a raw document ({blocks: [...], entityMap: {}}) that I had stored in my DB (contains bulletpoints). Like this:

var jsonFromDB = {blocks: [...], entityMap: {}};
var blocks = convertFromRaw(jsonFromDB);
var editorState = EditorState.createWithContent(
     blocks, decorator
);

this.setState({
    editorState: editorState,
});

HTML Node to multiple Content blocks

I would like to solve the following issue:

Using convertFromHTML I can convert an image tag to an atomic content block, however I am trying to introduce an unstyled block before and after the image.

htmlToBlock only allows for returning a 1 - to - 1 translation. Is there any way to achieve this 1 - to - many translation, i.e. one HTML node translates into multiple content blocks.

Thanks

installation with webpack fails

Hi all, I dont know much abourt webpack and babel, but I'm using it for a while and this is the first time i encounter a problem.

Packaging draft-convert 1.4.3 works fine, but since 1.4.4-1.4.6 I get the following webpack error. Do I need to configure a babel loader to get it working? Any hint how to get it working is much appreciated!

ERROR in ./~/draft-convert/package.json
Module parse failed: <somePath>/frontendClient/resources/node_modules/draft-convert/package.json Unexpected token (2:9)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected token (2:9)
    at Parser.pp$4.raise (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:2221:15)
    at Parser.pp.unexpected (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:603:10)
    at Parser.pp.semicolon (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:581:61)
    at Parser.pp$1.parseExpressionStatement (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:966:10)
    at Parser.pp$1.parseStatement (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:730:24)
    at Parser.pp$1.parseBlock (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:981:25)
    at Parser.pp$1.parseStatement (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:709:33)
    at Parser.pp$1.parseTopLevel (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:638:25)
    at Parser.parse (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:516:17)
    at Object.parse (<somePath>/frontendClient/resources/node_modules/acorn/dist/acorn.js:3098:39)
    at Parser.parse (<somePath>/frontendClient/resources/node_modules/webpack/lib/Parser.js:902:15)
    at DependenciesBlock.<anonymous> (<somePath>/frontendClient/resources/node_modules/webpack/lib/NormalModule.js:104:16)
    at DependenciesBlock.onModuleBuild (<somePath>/frontendClient/resources/node_modules/webpack-core/lib/NormalModuleMixin.js:310:10)
    at nextLoader (<somePath>/frontendClient/resources/node_modules/webpack-core/lib/NormalModuleMixin.js:275:25)
    at <somePath>/frontendClient/resources/node_modules/webpack-core/lib/NormalModuleMixin.js:259:5
    at Storage.provide (<somePath>/frontendClient/resources/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:52:20)
    at CachedInputFileSystem.readFile (<somePath>/frontendClient/resources/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:140:24)
    at DependenciesBlock.onLoadPitchDone (<somePath>/frontendClient/resources/node_modules/webpack-core/lib/NormalModuleMixin.js:255:7)
    at DependenciesBlock.loadPitch (<somePath>/frontendClient/resources/node_modules/webpack-core/lib/NormalModuleMixin.js:182:27)
    at DependenciesBlock.doBuild (<somePath>/frontendClient/resources/node_modules/webpack-core/lib/NormalModuleMixin.js:241:4)
    at DependenciesBlock.build (<somePath>/frontendClient/resources/node_modules/webpack/lib/NormalModule.js:84:14)
    at Compilation.buildModule (<somePath>/frontendClient/resources/node_modules/webpack/lib/Compilation.js:126:9)
    at <somePath>/frontendClient/resources/node_modules/webpack/lib/Compilation.js:309:10
    at <somePath>/frontendClient/resources/node_modules/webpack/lib/NormalModuleFactory.js:58:13
    at NormalModuleFactory.applyPluginsAsyncWaterfall (<somePath>/frontendClient/resources/node_modules/tapable/lib/Tapable.js:75:69)
    at onDoneResolving (<somePath>/frontendClient/resources/node_modules/webpack/lib/NormalModuleFactory.js:38:11)
    at onDoneResolving (<somePath>/frontendClient/resources/node_modules/webpack/lib/NormalModuleFactory.js:121:6)
    at <somePath>/frontendClient/resources/node_modules/webpack/lib/NormalModuleFactory.js:116:7
    at <somePath>/frontendClient/resources/node_modules/webpack/node_modules/async/lib/async.js:726:13
    at <somePath>/frontendClient/resources/node_modules/webpack/node_modules/async/lib/async.js:52:16
 @ ./~/draft-convert/lib/index.js 20:15-41

Text formatting can be quite different from HTML to DraftJS

I have made an email signature editor in DraftJS.
Users can pull in their current signature from Gmail, which comes in HTML.
When they open the editor we convert the HTML to DraftJS state using draft-convert but the formatting can often be quite off.
Sometimes it will have new lines where none were before or text will all be on the same line.

Here is a simplistic example where we have how text looks in HTML and when converted to DraftJS.
https://codepen.io/JohnMaguir/pen/OQbgLZ

An idea might be to try and manipulate the HTML before it goes to draft-convert but it would be hard to manipulate it and catch all scenarios without losing any of the users formatting.

I've also tried using custom htmlToBlock method, where I return false to ignore any node that is just an empty div or a div that only includes a br tag, based on this - #62
This will stop the insertion of some blank lines but doesn't fix the issue of the first two lines being pushed together and I'm not sure if it will work for more complex HTML.

If this is a bug that needs to be fixed I can propose a pull request, just point me in the right direction, otherwise any and all ideas would be much appreciated. If you need any more info please let me know

Overlapping styles and entities in convertToHTML not working when intersecting no style

I just noticed the release of #43 which is super cool. Unfortunately, it does not work for intersections situations, which is problematic.

Here is my code:

convertToHTML({
    blockToHTML: { // I put this here to have <div> in my output instead of <p>
        unstyled: {
            start: '<div>',
            end: '</div>',
            empty: '<br>',
        }
    },
    entityToHTML: (entity, originalText) => {
        if (entity.type === 'link') {
            return {
                start: `<a href="${entity.data.url}" target="_blank">`,
                end: '</a>',
            }
        }
        return originalText
    }
})(contentState)

Overlapping styles

1. Add link

screen shot 2017-02-22 at 11 49 21

2. Add bold intersecting link

screen shot 2017-02-22 at 11 49 29

3. Result

<div>t<strong>e<</strong>a href="http://google.com" target="_blank">st test</a></div>

4. Render

screen shot 2017-02-22 at 11 51 33

Situation

  1. Style strictly inside link βœ…

screen shot 2017-02-22 at 11 58 18

  1. Style over link βœ…

screen shot 2017-02-22 at 11 58 11

  1. Style outside link βœ…

screen shot 2017-02-22 at 11 58 21

  1. Style intersecting link πŸ”΄

Intersect start

screen shot 2017-02-22 at 12 01 41

Result:

screen shot 2017-02-22 at 12 01 44

Code:

<div>t<strong>e<</strong>a href="http://google.com" target="_blank">st test te</a>st</div>

Intersect end

screen shot 2017-02-22 at 11 56 14

Result:

screen shot 2017-02-22 at 11 58 14

Code:

<div>te<a href="http://google.com" target="_blank">st test t<strong>e<</strong>/a>st</div>

convertFromHTML/convertToHTML removes <a> tags and styles

I'm using convertFromHTML and convertToHTML as documented in the README, but the resulting EditorState and HTML, respectively, seem to remove any applied inline styles and tags. See screenshots.
The original HTML has a red paragraph:
screen shot 2017-04-25 at 8 48 20 pm

After running const textState = EditorState.createWithContent(convertFromHTML(defaultText)) where defaultText is an HTML string, the EditorState in react-draft-wysiwyg doesn't have the red color applied anymore:
screen shot 2017-04-25 at 8 42 32 pm
So then when using convertToHTML, the result doesn't have the red color attribute either.

Similarly, if I create some colored text or a link in the editor directly, and use convertToHTML, the resulting HTML still doesn't have the text color, and the link tag is removed:
screen shot 2017-04-25 at 9 07 50 pm
with convertToHTML(snakeCaseEditors.registration_form_html.getCurrentContent())
results in <p>...We invite you to register to join our...</p>

As far as I can tell, react-draft-wysiwyg isn't doing anything strange with EditorState. Am I missing some configuration? I'm not trying to do any custom formatting, just trying to maintain integrity through the conversion.

draft-convert 1.4.3
draft-js 0.10.0
react-draft-wysiwyg 1.9.4

Uncaught Error: Expected block HTML value to be non-null | Happens when an image is present in the editor

Hi community,

When I call convertToHtml on ContentState it works only if no <img> tag is present (or better to say that no 'IMAGE' entity is present inside the blocks).

If the IMAGE entity is present, I get the above error. A console screenshot is attached below :
Console Error

I'm calling the following function to generate html.

 getInnerHtml() {
    return convertToHTML({
      entityToHTML: (entity, originalText) => {
        if (entity.type === 'IMAGE') {
          return `<span><img src='${entity.data.src}' /></span>`;
        }
        return originalText;
      }
    })(this.state.editorState.getCurrentContent());
  }

How to go about this ?

I have the following versions of relavent packages.
"draft-convert": "^1.4.2",
"draft-js": "^0.10.0",
"react": "^15.3.2",
"react-dom": "^15.3.2",

on Chrome 56 on Macbook AIR.

#30 is a similar issue but doesn't solves my problem.

Thanks.

Ability to ignore a block in convertToHtml

Hi,

I couldn't figure out how to do this, but basically whenever a user creates a blank new line in the editor, I want to ignore the output (Block type = unstyled, text = ''), but I can't seem to change the block output to be null or ''. I've resorted to using for now, but would love to get rid of the hack.

Thanks!

convertToHTML outputting unexpected character

Hey,

Nice job with this plugin, just trying it at the moment and seems to work nicely except for one small issue...

I've using the draft-js-plugins-emoji plugin and so have code to replace the emoji entities:

if (entity.type === 'emoji') {
  const unicode = escapeMap[entity.data.emojiUnicode];
  return `<img class="emoji" alt="${entity.data.emojiUnicode}" src="${EMOJI_BASE_URL}${unicode}.png" />`;
}

The raw state I'm putting in (I convert it from the JS object using convertFromRaw first) is:

{
   "entityMap":{
      "0":{
         "type":"emoji",
         "mutability":"IMMUTABLE",
         "data":{
            "emojiUnicode":"πŸ‘"
         }
      }
   },
   "blocks":[
      {
         "key":"69o72",
         "text":"πŸ‘ ",
         "type":"unstyled",
         "depth":0,
         "inlineStyleRanges":[

         ],
         "entityRanges":[
            {
               "offset":0,
               "length":1,
               "key":0
            }
         ],
         "data":{

         }
      }
   ]
}

The output I get is the expected img tag followed by a οΏ½ character. Google informs me it is the "OBJECT REPLACEMENT CHARACTER".

I was just wondering if this is me misusing draft-convert or if something odd is going on during the conversion process?

entityToHTML doesn't work for element like "img"

Assuming I have such an entityToHTML

      entityToHTML: (entity, originalText) => {
        if(entity.type === "IMAGE") {
          return (
             <img src={entity.data.src} style={{width: "100%"}}/>
          )
        }
      }

It goes to the https://github.com/HubSpot/draft-convert/blob/master/src/blockEntities.js#L34 to get tag element length:

      const entityHTML = getEntityHTML(entity, originalText);
      const converted = [...getElementHTML(entityHTML, originalText)
                        || originalText];

      const prefixLength = getElementTagLength(entityHTML, 'start');
      const suffixLength = getElementTagLength(entityHTML, 'end');

The getElementTagLength (https://github.com/HubSpot/draft-convert/blob/master/src/util/getElementTagLength.js#L6) use splitReactElement:

const getElementTagLength = (element, type = 'start') => {
  if (React.isValidElement(element)) {
    const length = splitReactElement(element)[type].length;

    const child = React.Children.toArray(element.props.children)[0];
    return length + (child && React.isValidElement(child)
      ? getElementTagLength(child, type)
      : 0
    );
  }
  ......

However the splitReactElement than treat the as "void element" (https://github.com/HubSpot/draft-convert/blob/master/src/util/splitReactElement.js#L25) , where it directly returns a String, rather than {start: <sth>, end: <sth>}:

if (VOID_TAGS.indexOf(element.type) !== -1) {
    return ReactDOMServer.renderToStaticMarkup(element);
  }

Then the splitReactElement(element)[type].length report an error.

Isn't it an issue, or am I missing something?

convertToHTML tag <iframe /> problem

hi, here is my code, i get nothing when i convert to html. But display is fine. here is the same problem(facebookarchive/draft-js#609) Any suggestion? thanks!

import React, { Component } from 'react'
import { Entity, Modifier, EditorState } from 'draft-js'
import { createPlugin, pluginUtils } from 'draft-extend'
import { Button, Modal, message } from 'antd'
import VideoSelect from '../../Resource/Video/Select'
import { videoUrlConvert } from '../../../utils'

const ENTITY_TYPE = 'VIDEO'
// const BLOCK_TYPE = 'atomic'
class VideoButton extends Component {
    constructor() {
        super()
        this.state = {
            visible: false,
            record: null
        }
    }
    toggleVisible = () => this.setState({ visible: !this.state.visible })
    okHandler = () => {
        const { record } = this.state
        if (record) {
            const { editorState, onChange } = this.props
            const src = videoUrlConvert(record.path)
            if (src === '') {
                this.setState({ record : null })
                return message.error('θ§†ι’‘εœ°ε€ζœ‰θ――,请重新选择!')
            }
            const entityKey = Entity.create(
                ENTITY_TYPE,
                'IMMUTABLE',
                { src }
            )
            const contentState = Modifier.insertText(
                editorState.getCurrentContent(),
                editorState.getSelection(),
                'ζ΅θ§ˆε™¨δΈζ”―ζŒ',
                null,
                entityKey
            )
            onChange(EditorState.push(editorState, contentState, 'apply-entity'))
            this.toggleVisible()
        } else {
            message.warn('请选择一鑹!')
        }
    }
    render() {
        const { visible } = this.state
        return (
            <div>
                <Modal
                    visible={visible}
                    title="选择视钑"
                    onOk={this.okHandler}
                    onCancel={this.toggleVisible}
                    maskClosable={false}
                    width={720}
                >
                    <VideoSelect onChange={val => this.setState({ record: val })}/>
                </Modal>
                <Button onClick={this.toggleVisible}>ζ·»εŠ θ§†ι’‘</Button>
            </div>
        )
    }
}

const VideoDecorator = {
    strategy: pluginUtils.entityStrategy(ENTITY_TYPE),
    component: props => {
        const entity = Entity.get(props.entityKey)
        const { src } = entity.getData()
        return (
            <iframe allowFullScreen frameBorder={0} width={300} height={200} src={src} controls />
        );
    }
}

const htmlToEntity = (nodeName, node) => {
    if (nodeName === 'iframe') {
        return Entity.create(
            ENTITY_TYPE,
            'IMMUTABLE',
            {
                src: node.getAttribute('src'),
            }
        )
    }
}

const entityToHTML = (entity, originalText) => {
    if (entity.type === ENTITY_TYPE) {
        return `<iframe allowfullscreen frameborder=0 width="300" height="200"  src="${entity.data.src}" controls>${originalText}</iframe>`
    }
    return originalText
};

// const blockRendererFn = (block) => {
//     if (block.getType() === 'atomic' && block.size > 0 && Entity.get(block.getEntityAt(0)).getType() === ENTITY_TYPE) {
//       return {
//         component: ({ block }) => {
//           const {src} = Entity.get(block.getEntityAt(0)).getData()
//           return <iframe allowFullScreen frameBorder={0} width={300} height={200} src={src} controls>ζ΅θ§ˆε™¨δΈζ”―ζŒ</iframe>
//         },
//         editable: false
//       };
//     }
// }

const VideoPlugin = createPlugin({
    displayName: 'VideoPlugin',
    buttons: VideoButton,
    decorators: VideoDecorator,
    htmlToEntity,
    entityToHTML,
    // blockRendererFn,
    // htmlToBlock: (nodeName) => {
    //     if (nodeName === 'figure') {
    //       return BLOCK_TYPE;
    //     }
    // },
    // blockToHTML: {
    //     'atomic': {
    //       start: '<figure>',
    //       end: '</figure>'
    //     }
    // }
});

export default VideoPlugin

Is there any way to avoid errors on server side?

I'm trying to implement an isomorphic render, including Draft.js + draft-convert. Client-side works fine, but I'm facing a problem on server-side.

/src/utils/parseHTML.js contains a function fallback() which using document. Of course there is no document when you on server-side, so the question is – how to avoid that problem?

Single line break should not be converted to paragraph

Hello, I've just installed this converter and I've found an issue.
How do I prevent single line breaks to being turned into new paragraphs?

Right now writing

Example Text\n
Second Line

gets turned into

<p>Example Text</p><p>Second Line</p>

whereas the result I expect should be

<p>Example Text<br>Second Line</p>

Two consecutive line breaks instead should be turned into a new paragraph.

Any ideas on how I could achieve this?

Using convertToHTML returning an <img> tag in blockToHTML gives error

I get the following error when I try to use draft-convert convertToHTML returning an <img> tag in blockToHTML gives error

Uncaught Error: img is a void element tag and must neither have 'children' nor use 'dangerouslySetInnerHTML'

Here is my function for exporting to html.

export function editorStateToHtml (editorState) {
  if (editorState) {
    const html = convertToHTML({
      styleToHTML: (style) => {
        if (style === 'BOLD') {
          return <span style={{color: 'blue'}} />;
        }
      },
      blockToHTML: (block) => {
        const type = block.type
        if (type === 'atomic') {
          let url = block.data.src
          return <img src={url} />
        }
        if (type === 'unstyled') {
          return <p />
        }
      },
      entityToHTML: (entity, originalText) => {
        if (entity.type === 'LINK') {
          return <a href={entity.data.url}>{originalText}</a>;
        }
        return originalText;
      }
    })(editorState.getCurrentContent());

    return html
  }
}

Can't convert entities from and then to HTML

So I'm saving these variable entities in my HTML.

I initially load the HTML into my editor using convertFromHTML, where I use htmlToEntity to convert that HTML entity into an actual entity by calling Entity.create(), and then I return that as indicated by the docs.

Now my HTML is loaded into the editor with the entities as desired. But when I go to convertToHTML on this exact same content state, I get this error:

Invariant Violation: Unknown DraftEntity key.

I put a breakpoint there and the instances variable is empty.. when it should be full of the entities I imported earlier. What gives?

Unless I'm misinterpreting this, I think it's because your package has its own version of Draft, and Entities are part of the global state within Draft. When loading, I was using my version of Draft, so my Entities are in my global state, but not yours. This doesn't show up in tests because your tests are using the same version of Draft in and out.

This won't be an issue once this is resolved.

Solution

The simplest solution I can think of is to allow me to pass in convertToRaw, so that your package uses my global Draft state instead of yours. What do you think?

Incorrect offsets in entities after emojis

If there is an emoji (with .length >= 2) before an entity. The entity originalText argument in the entityToHTML is incorrect. Here's a breaking test:

  it('handles offset of entities after an emoji', () => {
    const contentState = buildContentState([
      {
        text: 'πŸ‘ Santi Albo',
        type: 'unstyled',
        depth: 0,
        entityRanges: [{
          offset: 0,
          length: 1,
          key: 0,
        }, {
          offset: 2,
          length: 10,
          key: 1,
        }],
      }
    ], {
      0: {
        type: 'emoji',
        mutability: 'IMMUTABLE',
        data: {
          emojiUnicode: 'πŸ‘'
        }
      },
      1: {
        type: 'mention',
        mutability: 'SEGMENTED',
        data: {
          href: '/users/1'
        }
      }
    });

    const result = convertToHTML({
      entityToHTML(entity, originalText) {
        if (entity.type === 'emoji') {
          return entity.data.emojiUnicode;
        } else if (entity.type === 'mention') {
          return <a href={entity.data.href}>{originalText}</a> // <-- originalText here is "anti Albo"
        }
      }
    })(contentState);
    // result === '<p>πŸ‘ S<a href="/users/1">anti Albo</a></p>'
    expect(result).toBe('<p>πŸ‘ <a href="/users/1">Santi Albo</a></p>');
  });

Custom Inline style's HTML is produced inconsistently

Hey,

I've added a custom inline style for coloring some text. Using styleToHTML I create a <span> with some inline style.
If the same text range also has 'underline' style there is no way to control the processing order of the styles and they are treated in the order they were applied.
This may cause issues as
<span style="color: blue"><u>text</u></span>
and
<u><span style="color: blue">text</span></u>
are different. In the latter case the color style doesn't affect the underline.

I've tried changing the way underlines are treated to create a <span style="text-decoration: underline">
but unfortunately the spans don't merge even if they have the same range so I'm facing the same problem.

Is there a way around this ?
I imagine merging identical tags (and the style attribute) is a possible solution.
Another solution is somehow controlling the style processing order by somehow specifying priority.

A temporary workaround is to remove the underline before I apply a color and then set it again. If the underline is the last added style it's always handled first when the custom HTML is generated, but that feels really hacky.

Thanks

convertToHTML with nested list output

Hi all, thanks for this great library!

I'm playing with generating nested list by convertToHTML.
Say I have

  • 1
    • 2

The genereated nested uls are as in the test
<ul><li>1</li><ul><li>2</li></ul></ul>
Shouldn't it be
<ul><li>1<ul><li>2</li></ul></li></ul>
Meaning the ul is inside li?
Some discussion about it here

Is it because of the way draft js organizes its blocks?

Many thanks

Support block wrapper

When setting a wrapper to the blockRenderMap, draft-js will wrap the block with a custom component.
It will be great if supporting this.
In draft-js docs:

const blockRenderMap = Immutable.Map({
'MyCustomBlock': {
// element is used during paste or html conversion to auto match your component;
// it is also retained as part of this.props.children and not stripped out
element: 'section',
wrapper: <MyCustomBlock {...this.props} />
}
});

convertFromHTML is processing newlines in lists wrong

When having a <ol> and in that a <li> that has newlines in form of a <br> in it, convertFromHTML will make every line in that <li> it's own list item, that appears in the surrounding list, which is not the expected result.

Example:

<ol>
  <li>first</li>
  <li> second<br>
with<br>
newlines</li>
</ol>

will be converted to a list with the structure:

1. first
2. second
3. with
4. newlines

Every line of the second item is a ContentBlock with type 'ordered-list-item'

strange πŸ“· appears when paste text

Hello. I was trying to use draft-convert to transform HTML to content when user paste text.
Here is my current code:

class Draft extends Component {
   pasteText(text, html) {
      const editorState = this.getter();
      const blockMap = convertFromHTML(config)(html).blockMap;
      const newState = Modifier.replaceWithFragment(
         editorState.getCurrentContent(),
         editorState.getSelection(),
         blockMap
      );
      this.setter(EditorState.push(editorState, newState, 'insert-fragment'));
      return false;
   }
   render() {
      return (
         <div className="">
            <Editor
               handlePastedText={(text, html) => this.pasteText(text, html)}
            />
         </div>
      );
   }
}

const config = {
   htmlToBlock: (nodeName, node) => {},
   htmlToStyle: (nodeName, node, currentStyle) => currentStyle,
   htmlToEntity: (nodeName, node, createEntity) => {
      if (nodeName === 'img' && node.getAttribute('data-image-id')) {
         return createEntity('image', 'IMMUTABLE', { src: node.src );
      }
      // In the case this is pasted image (it has not attribute 'data-image-id') I just return null
      else if (nodeName === 'img') {
         return null
      }
   },
   textToEntity: (text, createEntity) => {}
}

As you can see, I try to return null if this is pasted image. But it doesn't works. As the result I got πŸ“· symbol instead of images.
How can I handle this? Maybe there is some way to escape this symbol?

Multiple emoji followed by a styled apostrophe or quote render HTML entity

I've found a strange bug where if there are multiple emoji in a single block and they are followed by an apostrophe or quote which has an inline style, the HTML entity for the apostrophe/quote is split by the styling tag (ie <strong>). This results in text which should be rendered like this:
aaaπŸ˜₯πŸ˜₯a'aa

rendering like this (okay apparently this makes markdown sad but you can imagine that the * rendered as bold):
aaaπŸ˜₯πŸ˜₯**a&#**x27;aa

I haven't had time to dig into this but I did write tests on to demonstrate this bug on this branch. My initial thought was that it was being caused by something in blockInlineStyles, but as you see if you run the (nearly identical) tests, the one for just blockInlineStyles passes but the test on convertToHTML fails.

Custom content blocks are being flattened

I'm really sorry about this. I'm at a total loss here. The closest I've come to finding a resolution to this is here.

I am trying to convert

<div class="signature">
  <p>Emperor Palpatine</p>
  <p>Lord of the Sith</p>
  <p>Totally framed the Jedi</p>
</div>

into a single content block, but when I try to convert it I end up with what is essentially

<div class="signature">Emperor Palpatine</div>
<p>Lord of the Sith</p>
<p>Totally framed the Jedi</p>

I feel like I'm missing something incredibly simple here. I recreated it outside of my application here
in this webpackbin

convertFromHTML and nested html in atomic blocks

Hello! First, great project -- thank you for this @benbriggs etal πŸ’―

I'm running into an issue w/convertFromHTML where giving it an atomic block that contains a figcaption is producing some odd output.

What I'm feeding it is, essentially:

// outer figure is draftJS atomic wrapper, inner is React component wrapper
<figure>
  <figure>
    <img src="foo.png" />
   <figcaption>This is an image</figcaption>
  </figure>
</figure>

What I'm getting out is:

<figure>
  <figure>
    <img src="foo.png" />
   <figcaption>This is an image</figcaption>
  </figure>
  This is an image.
</figure>

On each successive import, it will append the "This is an image" string again, so I end up with ...</figure>This is an image.This is an image.This is an image.</figure> after 3 imports, and so on.

Any ideas what could be going on here? It looks like this line was introduced to fix rendering of nested elements within atomic blocks, but wondering if it shouldn't ignore (inner)text? Or maybe it's something I'm doing/not doing either here or in convertToHTML?

convertFromHTML img tags don't add content to editor

In my editor, when you add an image. It adds placeholder text like {{image}} and applies an immutable entity to that with the url set to the image URL.

I am converting to HTML like so:

entityToHTML(entity, originalText) {
    if (entity.type === 'IMG') {
      return <span><img src={entity.data.url} /></span>
    }
    return originalText
  }

Converting to HTML works great.

However when I convert back FROM html it is not adding the original text back:

htmlToEntity(nodeName, node) {
    if (nodeName === 'img') {
      return Entity.create(
        'IMG',
        'IMMUTABLE',
        { url: node.src }
      )
    }
  }

It seems like htmlToEntity gets back the entityID and then applies to the the content of the node that produced it. However bc the img is a void element and has not content, no content gets added back to the editor to apply the entity to.

Maybe it might make sense that if htmlToEntity returns an object with key and originalText it uses the key as the entity key and the originalText as the content instead of the content of the node.

Support for mapping br tags to paragraph blocks

Perhaps there is already built in logic for this, but I'm not able to map
tags to paragraph blocks when using the htmlToBlock function in convertFromHTML

These tags are not being parsed as nodes so wondering if there is a way to achieve this?

Project status?

Generally, it seems issues and pull requests are not given much attention in this repository. I am not whining, but as a user of this repository, it puts me (and the products I work on) in an awkward spot: should we wait, or create a temporary fork? Or a permanent?

I'd love to keep contributing to this library.
If you do not have a lot of time to maintain the project, would you be willing to take on more maintainers?

Support React elements

It will be great if supporting React elements, so we can define a parser like

const renderMention = (entity) => {
  const { id, username, name } = entity.data.mention
  return (  
    <Link to={`/user/${id}/${username}`}>
      {name}
    </Link>
  )
}

convertFromHTML tag <img /> problem

Hi! In convertFromHTML for links i can do this:

    htmlToEntity: (nodeName, node) => {
        if (nodeName === 'a') {
            return Entity.create(
                'LINK',
                'MUTABLE',
                {url: node.href}
            )
        }
    }

For images ( tag) I trying:

    htmlToEntity: (nodeName, node) => {
        if (nodeName === 'img') {
            return Entity.create(
                'IMAGE',
                'MUTABLE',
                {src: node.src}
            )
        }
    }

But it doesn't work. In draft-js editor img not showed.

updated peerDependencies - React v16

Can we expect updated peerDependencies for the draft-convert project in the near future?

I'am specifically thinking of the react and react-dom packages which are both locked to ^15.0.2. If you can see that the upgrade is working fine, maybe you can change the peerDependencies to something like ^15.0.2 || ^16.0.

At the moment, we can not upgrade to React v16 due to the locked version.

What's the best way to convert <hr> elements

Currently I'm converting to HTML like this which works fine:

const entityToHTML = (entity, originalText) => {
    if (entity.type === BLOCK.SEPARATOR) return '<hr>';
}

And I convert from HTML the hr like this:

htmlToEntity: (nodeName, node) => {
    if (nodeName === 'hr') {
        return Entity.create(BLOCK.SEPARATOR, IMMUTABLE, {});
    }
}

htmlToBlock: (nodeName, node, lastList, inBlock) => {
    if (nodeName === 'hr' && inBlock !== BLOCK.ATOMIC) {
        return BLOCK.ATOMIC;
    }
}

Then when I convert to HTML again, I end up with a hr wrapped in an atomic block.
I think this is because the text inside the atomic needs to be a '-' to be ahr but turns into 'a' when we convert from HTML. Is there a way to set the text of an atomic on when converting from HTML?

Ability to ignore a elements on convertFromHTML

Content copied from Apple Notes may contain <style> elements. These elements should be completely ignore but I can't find a wait to do it.

Am I missing something?

A solution to add that to the API would be to ignore the element if htmlToBlock returns false.

Empty space in block text brakes rendering

Hi guys

Having "text": " " in a block (Empty space in the value), makes convertToHTML method return empty on version 1.4.9.

Example block data below:

{
    "data" : {},
    "entityRanges" : [ 
        {
            "key" : 0,
            "length" : 1,
            "offset" : 0
        }
    ],
    "inlineStyleRanges" : [],
    "depth" : 0,
    "type" : "unstyled",
    "text" : " ",
    "key" : "6k1g"
}, 

This didn't happen on prior versions.
Any ideas?

Add ability not to trim trailing\leading spaces in text nodes in convertFromHtml

Currently empty text nodes are trimmed to one whitespace char. I assume it makes sense in some cases but if an entity is padded with some whitespaces, they are removed, and the appearance of the text is not as expected.

Example : convertFromHtml("<p>&nbps;&nbps;&nbps;&nbps;<a href='/home'>Link</a>&nbps;&nbps;&nbps;&nbps;</p>") results in<p> <a href='/home'>Link</a> </p>
The 'link' word that was supposed to be padded with spaces is now aligned to the left with a single spaces.

This is the relevant code :

    if (text.trim() === '' && inBlock !== 'code-block') {
      return getWhitespaceChunk(inEntity);
    }

It would be useful to have control over this functionality.

Typescript typings

Hello,
do you have any typescript support for this plugin?

PS. this plugin saved my life! Thanks! ;)

Overlapping styles and entities render improperly

For an entity that overlaps with a style:
input: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
expected: <a href="http://test.com">Lorem ipsum dolor sit <i>amet</i></a><i>, consectetur adipiscing elit.</i>
actual: <a href="http://test.c<i>om">Lorem ipsum dolor sit amet</a></i>, consectetur adipiscing elit.

For an style that overlaps with an entity:
input: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
expected: <i>Lorem ipsum dolor sit <a href="http://test.com">amet</a></i><a href="http://test.com">, consectetur adipiscing elit.</a>
actual: <a href="http://test.c<i>om">Lorem ipsum dolor sit amet</a></i>, consectetur adipiscing elit.

Seems like this has something to do with prefixLength and suffixLength of entities not working correctly.

Lists (ul and ol) are broken

Invariant Violation: convertToHTML: received block information without either a ReactElement or an object with start/end tags
at invariant (/project/node_modules/invariant/invariant.js:42:15)
    at getNestedBlockTags (/project/node_modules/draft-convert/lib/util/getNestedBlockTags.js:37:27)
    at /project/node_modules/draft-convert/lib/convertToHTML.js:111:64
 at Array.map (native)
    at /project/node_modules/draft-convert/lib/convertToHTML.js:83:34

This happens whenever there's an ordered or unordered list. Adding the block types to the blockToHTML function has no effect. It still throws that error.

blockToHTML without encoding

Hi,

Not sure if this is in-scope with draft-convert, but I'm trying to get a code block into my editor that renders as pure HTML. The editor is a pure internal tool (so we're not worried about malicious actors).

Things I've tried

{blockToHTML: (block) => {
    if (block.type === 'code-block') {
      return <div dangerouslySetInnerHTML={{ __html: block.text }}></div>
    }
}

Can't set children and innerHTML

{blockToHTML: (block) => {
    if (block.type === 'code-block') {
      return {
        start: block.text,
        end: ''
      }
    }
}

Outputs: Correct output + text is repeated

It would be nice if there's a non-default way to avoid encoding the blocks so we can pass unsafe HTML in convertToHTML.

unable to serialize contentState returned from convertFromHTML with entity using DraftJS.convertFromRaw

When attempting to generate a content state that has a link entity, convertFromHTML seems to work up until I try to serialize the contentState to JSON using DraftJS.convertToRaw.

The code I have is:

    const newContentState = convertFromHTML({
      htmlToEntity: (nodeName, node, createEntity) => {
        if (nodeName === 'a') {
          return createEntity(
            'LINK',
            'MUTABLE',
            { url: node.href },
          );
        }
      },

This works great as long as I don't try to serialize it.

The error I get on serialization is:

convertFromDraftStateToRaw.js?f453:52 Uncaught TypeError: contentState.getEntity is not a function
    at eval (convertFromDraftStateToRaw.js?f453:52)
    at Array.forEach (<anonymous>)
    at Object.convertFromDraftStateToRaw [as convertToRaw] (convertFromDraftStateToRaw.js?f453:51)
    at htmlToBlock (EditDocument.jsx?1364:365)
    at genFragment (convertFromHTML.js?2ed9:372)
    at genFragment (convertFromHTML.js?2ed9:427)
    at genFragment (convertFromHTML.js?2ed9:427)
    at genFragment (convertFromHTML.js?2ed9:427)
    at genFragment (convertFromHTML.js?2ed9:427)
    at getChunkForHTML (convertFromHTML.js?2ed9:481)

I'm using Draft JS 0.10.1 from Megadraft, so I think there might be some sort of version incompatiblity between Draft 0.10.1 and the contentState generated by convertFromHTML. But draft-convert uses draft-js as a peer dependency so I'm a little unsure on how this could happen.

Code blocks on separate lines

Is there a way to use the convertToHTML function to get 'code-block' elements that are next to each other to be rendered in one pre tag?

I was able to solve this problem with a different library for converting Draft blocks to html (sstur/draft-js-utils#26) but I haven't been able to figure it out with draft-convert.

DraftEntity.create will be deprecated soon.

While using convertFromHTML, in htmlToEntity method, we need to use Entity.create to create one draft entity. But console report a warning that "DraftEntity.create will be deprecated soon!" and suggest use contentState.createEntity instead.

Question is that there is no contentState yet at the moment... Any solution or workaround ?

use of htmlToStyle results in error

Given the following in convertFromHTML

const newContentState = convertFromHTML({
      htmlToStyle: (nodeName, node, currentStyle) => {
        console.log(nodeName);
        console.log(node);
      },
  };

I get the following error on console:

convertFromHTML.js?2ed9:202 Uncaught TypeError: Cannot read property 'withMutations' of undefined
    at eval (convertFromHTML.js?2ed9:202)
    at processInlineTag (convertFromHTML.js?2ed9:219)
    at genFragment (convertFromHTML.js?2ed9:359)
    at genFragment (convertFromHTML.js?2ed9:427)
    at getChunkForHTML (convertFromHTML.js?2ed9:481)
    at convertFromHTMLtoContentBlocks (convertFromHTML.js?2ed9:521)
    at eval (convertFromHTML.js?2ed9:611)
    at EditDocument.handlePastedText (EditDocument.jsx?1364:311)
    at editOnPaste (editOnPaste.js?e835:75)
    at eval (DraftEditor.react.js?61d6:147)

is it possible to support <sup> and <sub> tags using this library?

Since Draft.js only supports bold, italics, underline, code, and strikethrough natively, I've been trying to add subcript and superscript as well.

This seems like what I'm looking for, but so far I haven't gotten things to work.

This is what <sub>subscript</sub> and <sup>superscript</sup> tags look like

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.