Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for images #49

Closed
wants to merge 1 commit into from
Closed

Add support for images #49

wants to merge 1 commit into from

Conversation

tlackemann
Copy link

@tlackemann tlackemann commented Feb 5, 2018

Hey,

Thank you sincerely for this wonderfully written package. No rush in merging or reviewing but I wanted to extend the functionality to allow the translation of images from markdown -> draft and from draft -> markdown.

Todo

  • Support alt/title statements

@Rosey
Copy link
Owner

Rosey commented Feb 6, 2018

Thank you!! I will take a look when I have a chance!

@@ -147,7 +156,7 @@ function parseInline(inlineItem, BlockEntities, BlockStyles) {

blockEntityRanges.push({
offset: strlen(content) || 0,
length: 0,
length: 1,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello! Sorry for the delay in review and unfortunately I can't guarantee that I'll reply to your response in a timely manner, things have been very very busy for me with the baby, much busier than I expected!

Anyway...

I am wondering about the change here and the change at https://github.com/Rosey/markdown-draft-js/pull/49/files#diff-6ed54c3ce52b221b0eb0f7b22578bfe4R232

If I revert this back to 0 and the other line back to '' the tests still pass (provided I update the tests as well), so I was curious if you could provide an explanation for why we need the change to length/content? I haven't used images at all in draftjs so I'm not familiar with the details 😄

@Rosey
Copy link
Owner

Rosey commented Feb 23, 2018

A general question about this PR - it looks to me that images aren't by default supported in draftjs so you need to define them as atomic blocks yourself, for which there are a few plugins people can use if they need?

So I'm wondering if this change is best written as a plugin for markdown-draft-js instead of as part of the default implementation, in case people use different draftjs plugins with different block types that potentially don't jive with what is here?

Please let me know if I'm wrong, I like the idea of supporting images out-of-the-box but want to make sure it's "proper" 🙃

@Rosey
Copy link
Owner

Rosey commented Feb 23, 2018

by the way, in spite of my questions I do love that you did this and I think it will be useful for people, just want to make sure that everything is 👌 and I don't have as much time to research and ensure that is the case myself 🙂

@nicholaswbowen
Copy link

I can confirm that this code would be very useful to me inside or outside the main repo. I would be willing to commit some of my time to helping out if you decided to split this off into a separate repo. In response to what should be supported. There could be an argument for supporting everything in the commonjs markdown spec in this library. Are we supporting all the "out-of-the-box" features of markdown, or of draftjs? I'd say markdown. Either way Rosey, thanks for this library. I hope to contribute some code soon.

@Rosey
Copy link
Owner

Rosey commented Mar 1, 2018

@nicholaswbowen while you're waiting on this, it should be possible to easily steal this code and use it as a plugin - plugins are documented in the README, but it should be something like this (warning: untested example code, so I may have messed up with a typo or silly mistake!) -

Draft to markdown -

draftToMarkdown(rawDraftJsObject, 
  entityItems: {
    image: {
      open: function (entity) {
        return '';
    },

      close: function (entity) {
        return `![](${entity.data.src})`;
      }
  }
)

Markdown to draft -

markdownToDraft(markdownString, 
  blockEntities: {
    image: function (item) {
      return {
        type: 'atomic',
        mutability: 'IMMUTABLE',
        data: {
          src: item.src,
        },
      }
  }
)

@Rosey
Copy link
Owner

Rosey commented Apr 18, 2018

So I spent a bit of time getting my previous comment to work with the draftjs image plugin (https://www.npmjs.com/package/draft-js-image-plugin) and I've found that the drafttomarkdown works great but not so much the other way around. Going to look into further 😊

@Rosey
Copy link
Owner

Rosey commented Apr 18, 2018

In further testing, a similar (but slightly different) problem exists with this PR. I think because the plugin expects an image to be the only thing in a block, whereas in markdown they can be inline with other content.

@Rosey
Copy link
Owner

Rosey commented Apr 19, 2018

It also looks like @tpreusse has dealt with this - https://github.com/orbiting/cms-draft/blob/fc6d509b61eb7fd0ad9429fae5e06fd11929c5f5/src/utils/markdown.js and ended up writing a remarkable plugin for images as well.

@benjick
Copy link

benjick commented Sep 17, 2018

@Rosey is it possible to use that plugin somehow?

@Rosey
Copy link
Owner

Rosey commented Sep 17, 2018

@benjick if you take a look at the readme it explains how to use plugins. The solution I pasted in the comment (#49 (comment)) should work out-of-the-box for draftToMarkdown but there is some bugginess going the other way, as mentioned. You could spend a bit of time trying to solve that, I have at least provided a starting point and a few hints... I believe it requires a remarkablePlugin as well (again, see readme!). I do intend to see to this eventually myself but haven't gotten around to it yet, sorry!

@Rosey Rosey closed this Jun 14, 2020
@clemente-xyz
Copy link

Hi! Any updates here? Also need support for images conversion :/

@humont
Copy link

humont commented Feb 8, 2021

This thread has been sleeping for a while, but this library is really quite lovely so thought i'd add an update!

Rosey's example above is nearly there, just needs the following:

  1. Encode an alt attribute on your image data in your draftjs custom plugin:
const contentStateWithEntity = contentState.createEntity(
    constants.BlockTypes.IMAGE,
    'IMMUTABLE',
    {
      src: imageUrl,
      alt: imageAlt,
    }
  );
  1. When converting to markdown:
export const convertToMD = (state: EditorState): string => {
  return draftToMarkdown(convertToRaw(state.getCurrentContent()), {
    entityItems: {
      image: {
        open: function (entity) {
          return '';
        },
        close: function (entity) {
          return `![${entity['data'].alt}](${entity['data'].src})`;
        },
      },
    },
  });
};
  1. When converting back to draft:
export const convertToDraft = (markdown: string): RawDraftContentState => {
  return markdownToDraft(markdown, {
    blockEntities: {
      image: function (item) {
        return {
          type: 'atomic',
          mutability: 'IMMUTABLE',
          data: {
            src: item.src,
            alt: item.alt,
          },
        };
      },
    },
  });
};

There isn't too much data you can extract back out of markdown, but you could add an image "title" and de-serialize it,

@Rosey
Copy link
Owner

Rosey commented Feb 8, 2021

Thank you @humont

@vabatta
Copy link

vabatta commented Feb 24, 2021

I don't understand how can I make this working. I copied these snippets (#49 (comment)) in the place where I have the editor, yet nothing happens. No text is shown for the markdown image nor an image is displayed. Can you please describe how to make this happen?

@humont
Copy link

humont commented Feb 26, 2021

I don't understand how can I make this working. I copied these snippets (#49 (comment)) in the place where I have the editor, yet nothing happens. No text is shown for the markdown image nor an image is displayed. Can you please describe how to make this happen?

There's a bit of work that you need to do to get Draft JS showing images first - which is a little out of scope for this repo. You can find a working example on the draftJS repo or alternatively implement with a plugin - up to you!

@vabatta
Copy link

vabatta commented Feb 26, 2021

@humont I figure that out and I suppose https://www.draft-js-plugins.com/plugin/image does the job right? What I don't understand is how to convert from markdown to draftjs the "block" to the plugin format:

const initialState = {
  entityMap: {
    0: {
      type: 'IMAGE',
      mutability: 'IMMUTABLE',
      data: {
        src: '/images/canada-landscape-small.jpg',
      },
    },
  },
  blocks: [
    {
      key: 'ov7r',
      text: ' ',
      type: 'atomic',
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [
        {
          offset: 0,
          length: 1,
          key: 0,
        },
      ],
      data: {},
    }
    ...
  }
}

@humont
Copy link

humont commented Feb 26, 2021

You should only need the entity data itself, the blocks array will take care of itself as it's ordered.

In this case, the example I gave above requires a:

data: {
src: 'YOUR_IMAGE_SRC',
alt: 'AN_ALT_TEXT'
}

whereas yours only has src. Conditionally returning the alt text should work.

To be honest - I'm not familiar with the plugin structure, so there may be something else going on... I built it from scratch using the example from the Draftjs repo. sorry I can't be of more help!

are you successfully rendering it into markdown?

@vabatta
Copy link

vabatta commented Feb 26, 2021

No, the image link gets somehow "double" rendered (![<alt text>](![<link>](<link>))). We don't really understand how this can be so complex, so we'll backoff and implement a simple feature with regexp.
MD source -> regexp to extract images -> draftjs -> MD source -> regexp to reinsert the images. We are really sad that this entire thing turned out to be so complex but we cannot spend more time on it.

@vmc08
Copy link

vmc08 commented Mar 2, 2021

@Rosey thank you for this great library, but I need help. Need your help and insight too @humont

I used @humont's answer above, the editorState -> markdown conversion works fine, but I can't make the reverse conversion work. Here's what I have.

draftjs state to markdown (works fine as expected)

export const draftToMarkdownConversion = (editorState: EditorState) => {
  const rawObject = editorStateToRaw(editorState)
  return draftToMarkdown(rawObject, {
    entityItems: {
      image: {
        open: function() {
          return ''
        },
        close: function (entity: any) {
          if (!entity) return ''
          return `![${entity.data.alt}](${entity.data.src})`;
        },
      },
    },
    styleItems: {
      UNDERLINE: {
        open: function () {
          return '++';
        },
        close: function () {
          return '++';
        },
      },
    }
  })
}

markdown to draftjs state (doesn't work)

export const markdownToDraftConversion = (markdownString: string) => {
  const rawData = markdownToDraft(markdownString, {
    blockEntities: {
      image: function (item: any) {
        return {
          type: 'atomic',
          mutability: 'IMMUTABLE',
          data: {
            src: item.src,
            alt: item.alt,
          },
        };
      },
    },
    blockStyles: {
      'del_open': 'STRIKETHROUGH',
      'ins_open': 'UNDERLINE',
    },
    remarkableOptions: {
      enable: {
        inline: "ins",
      },
    },
  })
  const contentState = convertFromRaw(rawData)
  return EditorState.createWithContent(contentState)
}

Here is the markdown string I'm processing via markdownToDraftConversion.

![https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/NGC_4414_%28NASA-med%29.jpg/1200px-NGC_4414_%28NASA-med%29.jpg](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/NGC_4414_%28NASA-med%29.jpg/1200px-NGC_4414_%28NASA-med%29.jpg)

Galaxy

but consoling log the rawData before processing it back to editorState looked like this.

image

I am expecting the blocks should contain the atomic type as the image should be mapped on that block, but no luck :(

I hope you can point out what I missed or which part I did wrong.

@humont
Copy link

humont commented Mar 2, 2021

@vmc08 are you sure you’re passing the right data? The blocks array seems to include a block with the word “galaxy” which is not in the md string you provided.

@vmc08
Copy link

vmc08 commented Mar 2, 2021

@humont , sorry I pasted the wrong data. Here is the post content I am trying to parse. Apologies. Updated the md string

@tanvesh01
Copy link

@vmc08 Hey did you figure this out? I am having the same problem. Can't convert images in markdown.

@saintego
Copy link

saintego commented Jan 22, 2022

@vmc08 @tanvesh01 did you find any solution? I have the same issue

@saintego
Copy link

saintego commented Jan 23, 2022

I need transform raw like this

{
    ...raw,
    blocks: raw.blocks.map((block) => {
      const entityKey = block.entityRanges?.length && block.entityRanges[0].key
      const hasImage = !!entityKey && raw.entityMap[entityKey].type === imageType
      return {
        ...block,
        ...(hasImage
          ? { type: 'atomic', text: ' ', entityRanges: block.entityRanges?.map((range) => ({ ...range, length: 1 })) }
          : {}),
      }
    })

@robertaistleitner
Copy link

I need transform raw like this

{
    ...raw,
    blocks: raw.blocks.map((block) => {
      const entityKey = block.entityRanges?.length && block.entityRanges[0].key
      const hasImage = !!entityKey && raw.entityMap[entityKey].type === imageType
      return {
        ...block,
        ...(hasImage
          ? { type: 'atomic', text: ' ', entityRanges: block.entityRanges?.map((range) => ({ ...range, length: 1 })) }
          : {}),
      }
    })

@saintego I came to the same conclusion. Without the intermediate atomic block all the previous suggestions do not work out properly.

@tamerin-tech
Copy link

Just in case someone is looking for a good solution to convert MD to Draft with images, I found this library which is working great for images https://github.com/kadikraman/draftjs-md-converter

@cachafla
Copy link

I wanted to share what worked for me. I followed the instructions on this comment: #49 (comment) but they weren't working for me. After some head scratching I was wondering if the fact that entities had names in all uppercase letters was the source of the issue. With this single change I got everything to work, basically the entity name should be IMAGE instead of image:

    const markdownString = draftToMarkdown(rawObject, {
      entityItems: {
        IMAGE: {
          open: function (entity) {
            return '';
          },

          close: function (entity: any) {
            return `![${entity.data.alt}](${entity.data.src})`;
          },
        },
      },
    });

And in my testing I found that the markdownToDraft snippet was not necessary. Hope this helps anyone else looking for a solution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet