GithubHelp home page GithubHelp logo

Handling block level data about redraft HOT 9 CLOSED

lokiuz avatar lokiuz commented on July 17, 2024 1
Handling block level data

from redraft.

Comments (9)

lokiuz avatar lokiuz commented on July 17, 2024 1

Your right my method didn't account for 2 atomic blocks getting grouped. While it still might be possible to render all of the entities passed to it by mapping over them, adding a proper support would be preferable.

In terms of API I wonder if it wouldn't be better to handle atomic blocks bit differently. The atomic block accepts a entity key and a string as per APIReference-AtomicBlockUtils.md the render API in redraft might look like that:

const atomicBlocks = {
  IMAGE: (content, entity)=> `<figure><img src="${entity.src}" alt="${content}" /></figure>`
}

This would require to check for the atomic type while rendering blocks and handle them differently from the other types.

from redraft.

lokiuz avatar lokiuz commented on July 17, 2024

I didn't try using atomic blocks myself so the rest of this is just a bit of guessing.

From what I understand about atomic blocks, it would require accessing data of the associated entity at block render level. While this is not possible at the moment i think it might be possible to do something like rendering it using just the entity callback for the entity type.

Assuming you have raw that looks something like that:

const raw = {
  blocks: [
    {
      text: ' ',
      type: 'atomic',
      entityRanges: [{offset: 0, length: 1, key: 'first'}],
    },
  ],
  entityMap: {
    first: {
      type: 'IMAGE',
      mutability: 'IMMUTABLE',
      data: {
        src: 'img.png',
      },
    },
  },
};

It should be possible to render just the entity like this:

const blocks = {
  atomic: children => children[0],
};

const entities = {
  IMAGE: (children, entity) => <Image src={entity.src} />,
};

const renderers = {
  blocks,
  entities,
};

const rendered = redraft(raw, renderers);

Let me know if this helps. Also it might be a good idea to add support for atomic blocks in a more specific way. If you want to add a PR for it it would be most welcome. 😄

from redraft.

cjhveal avatar cjhveal commented on July 17, 2024

Hey, thanks for the response, it definitely helps.

You're definitely right, the associated data is stored within an Entity. I don't know why I thought it was stored directly on the block. Perhaps from a careless interpretation of the RawDraftContentBlock flow type, despite having implemented it by creating/reading from an entity.

I tested the renderers configuration you sent along and it mostly works, with a few reservations:

  1. The method above discards subsequent adjacent atomic blocks. From a quick check in my implementation (using draft-js-plugins), my raw content seems to have an unstyled block inserted between the atomic blocks, but I'm not sure if it's impossible to encounter multiple adjacent atomic blocks in the raw data.
  2. It may be possible that an entity should render slightly differently if it is in an atomic block or an inline entity. For instance, from what I understand when rendering to DOM, Draft.js wraps block level images in a <figure> tag.

Since 1. is a non-issue (at least for me), and 2. can be worked around by spliting images into inline IMAGE entities and block level FIGURE entities, this solution works just fine for me.

However, I'm still interested in contributing. The docs say that the atomic blocks are likely to be revisited in the future to avoid the heavy-handedness of having to get the entity for an atomic block, but I think it'd still be useful to support in the meantime.

The API I was thinking was adding atomicBlocks to the renderers, which would be an object mapping entity names onto the block level representation of them. Something like below:

const atomicBlocks = {
  IMAGE: children => `<figure>${children}</figure>`
}

const entities = {
  IMAGE: (children, data) => `<img src="${data.src}">`
}

const renderers = {
  atomicBlocks, entities
}

const rendered = redraft(raw, renderers);

A couple questions on the above:

  1. Can atomic blocks have more than a single entity? If that's the case then the above API is definitely broken.
  2. Would the atomic block renderer still be fired?
  3. Am I missing an obviously better way to handle these, or misunderstanding how redraft currently works?

from redraft.

cjhveal avatar cjhveal commented on July 17, 2024

Interesting, could you clarify for me what content and entity are in your proposed API above? I'm guessing that entity is the data of the entity keyed by the entityKey, and content is the character parameter to insertAtomicBlock?

If that's the case, I don't know if putting the content in alt makes sense as in your example, as the character parameter seems to define how the block behaves as text. For instance, I use a single space for my image block to allow backspacing over the image block.

There definitely are still cases where having that text is useful, though. The docs list an example of implementing a TeX block using the atomic blocks, in which case I could imagine using that field for the input and using a custom backspace handler to allow backing over individual parts of the equation.

I spent a bit of time looking through the docs, and it seems like the insertAtomicBlock modifier does indeed place a blank unstyled block directly after the atomic block, so I'm not sure you'd ever encounter two of them in a row. Might be good to support just in case, but it seems they're not intended to rely on being adjacent to the way that the *-list-item blocks are.

I'm going to do a bit of a deeper dive into the redraft code, and hopefully I'll feel comfortable enough to submit a PR soon. Thanks for talking through this and being open to the collaboration.

from redraft.

lokiuz avatar lokiuz commented on July 17, 2024

Yeah that example might not been the best one. I meant it exactly as you guessed content is the character passed to insertAtomicBlock, also maybe it would be better to just call it character. The whole point being that the data passed while creating the block should be available on render should you need it.

I'm bit worried about those empty unstyled blocks. In this case it looks like it's a hack to properly display the atomic blocks in the editor. If that's the case it might be worth filtering them out before render, what do you think?

from redraft.

cjhveal avatar cjhveal commented on July 17, 2024

I definitely agree with providing the user with all the information they used to create the block.

Frankly, the entire atomic blocks design is a bit of a hack. I think Draft.js puts them there so that when the user starts typing it goes into an unstyled block, rather than the text section of the atomic block, where it won't be rendered correctly. But inserting two images back to back in my editor produces 5 blocks:

unstyled
atomic
unstyled
atomic
unstyled

You bring up an interesting point about filtering them out. Without doing so, people might end up with a bunch of <p> tags after every atomic block, but what would the filter condition be? Don't render empty unstyled blocks that occur directly after an atomic block?

If users would rather not render empty unstyled blocks, can they check each of the children in the block renderer, or are those nodes opaque to the end user? However, with that approach it both pushes the responsibility onto the user, and it doesn't handle the use case of wanting to keep empty blocks except for the ones directly after an atomic block.

I'd be happy with filtering them out if it would be what the user would want to do in the overwhelming majority of cases anyway, but discarding part of their content without letting them control that would make me a little uneasy.

from redraft.

lokiuz avatar lokiuz commented on July 17, 2024

We could do both. Let's say filter empty unstyled blocks after atomic by default, and provide a flag to disable it. Something like:

const options = {
  cleanupAtomic: false // coundn't figure out a better name 
}

redraft(raw, renderers, options)

Also this could enable passing more options in the future if there's a need for them.

from redraft.

cjhveal avatar cjhveal commented on July 17, 2024

Awesome. I was wondering if adding rendering options was something you wanted to do. I really liked the declarative nature of providing lots of hooks for users, but this makes perfect sense to me.

from redraft.

aight8 avatar aight8 commented on July 17, 2024

cleanupAtomic removes all line breaks as well btw.

from redraft.

Related Issues (20)

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.