draft-convert icon indicating copy to clipboard operation
draft-convert copied to clipboard

convertFromHTML tag <img /> problem

Open Zhurbin opened this issue 9 years ago • 9 comments

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.

Zhurbin avatar Jul 22 '16 11:07 Zhurbin

Images get a bit more complicated because they're not strict decorations of text. An additional atomic block type is used to have a custom block renderer, then the entity can be used to store metadata within that block. In our internal image plugin our conversion options look something like this:

const ENTITY_TYPE = 'IMAGE';
const BLOCK_TYPE = 'atomic';

export default createPlugin({
  htmlToBlock: (nodeName, node) => {
    if (nodeName === 'figure') {
      return BLOCK_TYPE;
    }
  },
  htmlToEntity: (nodeName, node) => {
    if (nodeName === 'img') {
      return Entity.create(ENTITY_TYPE, ...)
    }
  },
  blockRendererFn: (block) => {
    if (block.getType() === 'atomic' && block.length > 0 && Entity.get(block.getEntityAt(0)).getType() === ENTITY_TYPE) {
      return {
        component: ({block}) => {
          const {src} = Entity.get(block.getEntityAt(0)).getData();
          return <img src={src} />;
        },
        editable: false
      };
    }
  },
  blockToHTML: {
    'atomic': {
      start: '<figure>',
      end: '</figure>'
    }
  },
  entityToHTML: (entity, originalText) => {
    if (entity.type === ENTITY_TYPE) {
      return `<img src="${entity.data.src}" />`;
    }
  }
 });

This way your HTML output looking like this:

<figure>
  <img src="..." />
</figure>

If you need to deal with any incoming <img> tags that don't have wrapping <figures> you can add the block type by changing htmlToBlock to add the atomic block type around any tags without it:

htmlToBlock: (nodeName, node, lastList, inBlock) => {
  if (nodeName === 'figure' && node.firstChild.nodeName === 'IMG' || (nodeName === 'img' && inBlock !== BLOCK_TYPE) {
  return BLOCK_TYPE;
  }
}

Let me know if this works @Zhurbin!

benbriggs avatar Aug 01 '16 19:08 benbriggs

I'm using draft-js-image-plugin and with these codes:

htmlToEntity: (nodeName, node) => {
  if(nodeName==='img'){
    return Entity.create('block-image', 'IMMUTABLE', {url: node.src})
  }
},
htmlToBlock: (nodeName, node, lastList, inBlock)=>{
  if(nodeName==='img'){
    return 'block-image';
  }
},

No image displayed. The result content state has no entity info in the entityMap and blocks.

After checking the code, I find that the chunk constructed has some problems so that it can't find the entity info when creating the content state. See codes below(convertFromHTML.js#L503):

      textBlock = sanitizeDraftText(textBlock);
      var end = start + textBlock.length;
      var inlines = nullthrows(chunk).inlines.slice(start, end);
      var entities = nullthrows(chunk).entities.slice(start, end);
      var characterList = List(
        inlines.map((style, ii) => {
          var data = {style, entity: null};
          if (entities[ii]) {
            data.entity = entities[ii];
          }
          return CharacterMetadata.create(data);
        })
      );

The textBlock is always empty, so start is always equal to end, then empty array will be returned by slice(start, end)

tianjianchn avatar Aug 22 '16 08:08 tianjianchn

@kiliwalk thanks for investigating - i had run into this issue as well and addressed it here for atomic blocks since that seemed to be the block type provided by Draft for these sorts of things. the image plugin clearly uses a different block type, though, so the character doesn't get added.

I can add a config option that allows you to specify other "atomic" block types that this hack should apply to, though I should note that the real answer here is both draft-convert and draft-js-image-plugin updating to support block metadata so that no text or entity is necessary.

benbriggs avatar Aug 22 '16 14:08 benbriggs

Hey @benbriggs! I am hitting this issue as well with using the current versions of draft-js, draft-convert and draft-js-image-plugin. Is there a current way to solve this issue?

If the solution is to avoid using that image plugin and use a HubSpot version of it instead (or build my own), let me know. The HTML converter is super useful for us so I'd love the use it if possible.

vm avatar Oct 13 '17 19:10 vm

@vm what issues are you running into specifically? depending on what you're looking for (e.g. images as inline entities, or just getting images parsed as block-level elements) the issue might be a problem with any of those three packages in play.

I think it would be worth considering for draft-js-image-plugin to use block metadata since it gets you some nice things like proper render tree updates when metadata changes, so it might be worth either the team integrating that (at the cost of compatibility with early versions) or to just fork their version and change the backing implementation. Unfortunately our internal implementation is pretty deeply tied into our internal UI components so we haven't open sourced it 😞

benbriggs avatar Oct 14 '17 01:10 benbriggs

i know its too late but i'm just trying to help those who have this issue in the future. just have this issue today for showing <img> tag and im using 'entityToHTML' and its fix the problem for me.

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

racmathafidz avatar Nov 30 '21 10:11 racmathafidz

Thank you @racmathafidz! You just made my day!

moconchobhair avatar Jan 21 '22 01:01 moconchobhair

Never too late @racmathafidz, you made my day too!

alx4329 avatar Jan 31 '23 00:01 alx4329

@racmathafidz than a lot for helping

basannofal avatar Jun 05 '23 16:06 basannofal