GithubHelp home page GithubHelp logo

lokiuz / redraft Goto Github PK

View Code? Open in Web Editor NEW
281.0 281.0 34.0 3.64 MB

Renders the result of Draft.js convertToRaw using provided callbacks, works well with React

Home Page: https://lokiuz.github.io/redraft/

License: MIT License

JavaScript 95.88% HTML 0.31% CSS 3.82%

redraft's People

Contributors

alejandronanez avatar avdeev avatar bramus avatar danez avatar flyingmana avatar grrowl avatar kawamurakazushi avatar lokiuz avatar marcelometal avatar mxstbr avatar savardc avatar tonypee 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

redraft's Issues

Redraft keeps duplicating entities when entity is copy-pasted from editor

redraftjs-duplication

Because Redraft uses the entity key as is from the raw ContentState, a weird duplication behavior is introduced when an entity is copy-pasted into the same editor (see demo above with bug repro on the official Demo page).

Screenshot 2022-04-08 at 12 56 05 PM

The because the entity key also serves as the React key, it confuses React and upon every blur of the editor the entity gets duplicated.

One hacky workaround is to add unique random strings to all Entity renderers like so (excuse the poor code quality of invoking uuid directly the render):

entities: {
    // key is the entity key value from raw
    LINK: (children, data, { key }) => <Link key={`${key}-${uuid()}`} to={data.url}>{children}</Link>,
  },

But this cannot be good for React performance. Ideally:

Each such item should have a permanent and unique property.

Therefore, a more elegant solution here would be:

  1. Whenever a new Entity is introduced, check within the same Editor's EditorState is the entity key exists. Say the Entity key in question is 123.
  2. If the key exists, concatenate the entity key of the new Entity with a randomly generated string, delimited by another identifiable string like 123__redraft____foo (the rationale for the 'identifiable' part being that if at any point, the original entity key needs to be filtered out for whatever reason, we could always parse through and just remove everything from the __redraft. If this is not a concern, then the suffix can be completely random).

PS: I don't foresee this as an issue between multiple editors on the same page, so checking within the same Editor should suffice.

PPS: DraftJS does handle duplicate entities in it's rendering through sequentially numbered keys. I will try and link to the relevant code once I manage to find time to dive into the DraftJS codebase.

Screenshot 2022-04-08 at 1 51 46 PM

[Question] How do I retain inline styles of HTML tags?

Hello,
I have the following HTML <span style="color:#ff4500">Inspecter </span> which I'd like to parse, but I would also like to retain the inline styling, in this case the text color.
How could I do that? None of the blocks or renderers seem to hit on it.
Please see this codesandbox for an example.

Thank you.

Content Style Not Being Applied Correctly

Affects Version

v0.10.0

User Story

When comparing the new styles stack with the current one, while both having the same amount of items, the new string gets concat with the previous one.

This leading to styles not being applied correctly when rendering the content...

e.g.:

arraysEqual(['a'], ['b']) // true

References:

Conditions of Satisfaction

Comparing equality between two arrays, regardless of their size, should return the correct boolean.

redraft merges disparate blocks into one

it seems the redraft merges two blocks of the same type into one.

So a set of blocks with [ {type:"unstyled"}, {type:"unstyled"}] gets rendered with

<block><children1><children1></block>

instead of what I would expect:

<block1><children1></block1><block2><children2></block2>

Is that intentional?

I tried setting the options.cleanup.split=false, but it doesn't seem to have any effect.

How to get the HTML output after ```convertToRaw()```

Hello,

I was trying to get the content output as html and attached custom styles to the convertToRaw() results. But cant find a way to retreive HTML output, it just returns react nodes.

How to get the HTML output after convertToRaw()

entities block key

Is there a way of accessing the block key of the block that refers to the entity from entities?
I have got several uses for it and less inclined towards hacking my way around it

ReactNative support

Unsure if this works right now, but does redraft have ReactNative support? Can I just return <View />s instead of <div />s?

Add support for draft-js-plugins

It'd be super rad if I could do

const renderer = {
  plugins: [mentionsPlugin]
}

or something to that effect. That way I basically wouldn't have to change anything about my setup between rendering a DraftJS plugins editor and redraft!

/cc @juliankrispel @nikgraf

Issue with style renderer and collision in style key

When using createStylesRenderer, if there's a conflict between 2 overlapping styles, only the last will be applied.

Example:

UNDERLINE: {
  textDecoration: 'underline'
},
STRIKETHROUGH: {
  textDecoration: 'line-through'
}

The rendered text will only have textDecoration: 'line-through'

I isolated the problem with the way the styles are merged in reduceStyles but my fix involved adding a dependency on lodash.assignWith.

Let me know if you're interested in a PR

Multibyte characters (emoji) are considered multiple characters

for the ContentState:

{ "blocks": [ { "data": {}, "depth": 0, "entityRanges": [ { "key": 0, "length": 12, "offset": 0 } ], "inlineStyleRanges": [], "key": "471ua", "text": "be a fish 🐡~", "type": "header-one" } ], "entityMap": { "0": { "data": { "data": { "color": "#ffffff" } }, "mutability": "MUTABLE", "type": "ENTITY" } } }

the entity "ENTITY with { data: { color: "#ffffff" } } is only applied to the emoji, missing the trailing "" character. Removing the trailing "" character splits the multibyte emoji into halves (half white, half default color).

> "be a fish 🐡~".length // = 13, but is really 12 multibyte characters. The fix would be to treat multibyte characters as single characters, which would mean manually implementing multibyte functions for calclulating length and assessing offsets. (similar to this)

Decorators incorrectly handle emojis

We're running into some bugs around decorating text that has emojis in it. Since a screenshot is worth a thousand words:

screen shot 2017-11-20 at 12 13 00 pm

Note how the @rauchg mention is misaligned by a couple of characters. However, if the emoji is removed from the initial text it all works perfectly:

screen shot 2017-11-20 at 12 14 35 pm

Running [email protected]. Since I'm a nice open source citizen™️ here you have a full reproduction of the bug to play around with: https://codesandbox.io/s/y2rpn3py3x

PrismDecorator doesn't work

I just tried upgrading to 0.9.0 for complex decorator support (#30), but for some reason I'm still getting an error with draft-js-prism:

import Prism from 'prismjs';
import PrismDecorator from 'draft-js-prism';

const renderer = {
  decorators: [new PrismDecorator({
    prism: Prism,
    render: ({ type, children }) => <span className={`prism prism-token ${type}`}>{children}</span>
  })]
} 

Throws:

TypeError: strategy is not a function (CompositeDecorator.js:112)
  getDecorations (CompositeDecorator.js:112)
  forEach self-hosted:273:13 getDecorations (CompositeDecorator.js:98)
  decorateBlock (withDecorators.js:34)
  withDecorators (withDecorators.js:67)
  map self-hosted:297:17 withDecorators (withDecorators.js:66)
  render (render.js:231)

The draft-js-prism decorator is a complex decorator though, with getComponentForKey, getDecorations, etc.

Any ideas?

rendering empty line as `<br />`

Is there a way to add empty lines as <br /> tag?

screen shot 2018-11-23 at 9 13 05 pm

Here's the raw contentState for an empty line.

{
  data: {},
  depth: 0,
  entityRanges: [],
  inlineStyleRanges: [],
  key: "4ltcn",
  text: "",
  type: "unstyled"
}

I tried to have the following renderer, but it only works in certain cases.

blocks: {
  unstyled: (children, { keys }) =>
    children.map((child, index) => {
      const last = child[child.length - 1];
      const render = last.length === 0 ? [child, <br key={0} />] : child;

      return <div key={keys[index]}>{render}</div>;
    }),
},

If it's possible, can you point me to the right direction?

Support complex decorators

DraftJS supports more than just simple { strategy, component } decorators, specifically it supports anything matching the DraftDecoratorType:

https://github.com/facebook/draft-js/blob/0c609d9d3671fdbbe2a290ed160a0537f846f08e/src/model/decorators/DraftDecoratorType.js#L25-L45

As you can see, decorators can also be objects that look like this: { getDecorations, getComponentForKey, getPropsForKey }. (I think interally the { strategy, component } composite decorators are converted to that more complex form)

This type of decorator is (for example) used in the draft-js-prism decorator, but also in many other libraries. It'd be great if redraft supported that!

Improvements

1. Decorators

Since redraft can output react component's it would be nice when you define decorators (same interface than in draft.js). On thin way you can use link decorator without create link entities. etc.

2. Interface nearer to draft.js

The current implementation of renderers differs in many ways to the draft.js definitions.
My idea is to define following object once:

blockMap:object // provide block type element type (and wrapper)
blockStyleFn:function(contentBlock) // provide class for a block type
blockRendererFn:function(contentBlock) // provides custom component (editable & props key not used)
customStyleMap:object // provide style object for a style

  • Decorators (see point 1.)

Then reuse those for the draft.js config for the renderer.

Handling block level data

First off, thanks a bunch for releasing this. I really like your approach here.

From looking through the code, it doesn't look like block level data is discarded without being passed through to the renderers.

I use atomic blocks to encode images, but find myself unable to access the src attribute I store in their data.

I'd be happy to make a PR adding for this, but wanted to reach out first, just in case there's a way that I missed or if that change wouldn't be welcome.

Flat rendering of inline style ranges with complex inline styles

Thanks so much for building this! We’ve implemented it in our app for rendering draft.js text content when that content is not in text editing mode, and when there are many separate pieces of text content on a page at once, it has been a considerable perf improvement (we serialize and store text content as the result of convertToRaw, so that’s how it lives in our Redux store). We are also using it for generating HTML for that content server-side, which proved necessary for us because we wanted to use <p> instead of <div> as our default block element, but draft.js renders <div>s inside the <p> block element, which is invalid HTML and which React’s renderToString function actually doesn’t allow (it creates an empty <p> element and takes the <div> with its children that was inside it and puts it after the empty <p>). Using redraft, we’ve been able to simplify and cleanup the output when not in text editing mode and render valid, semantic HTML on the server, so thank you very much for making it!

As described in the draft.js docs on complex inline styles, in editorState, draft.js stores inline styling on a character-by-character level, meaning that nested styles (bold text inside an italicized range, for example) are actually represented as a flat set of ['BOLD', 'ITALIC']; this means that when you use the customStyleMap to render those inline styles, draft converts the nested range into

<span style={ { fontWeight: 'bold', fontStyle: 'italic' } }>{ text }</span>

, as opposed to

<span style={ { fontStyle: 'italic' } }>`<span style={ { fontWeight: 'bold'  } }>{ text }</span></span>

The depth of inline element rendering never grows past 1. This doesn’t seem to be the case when using redraft, I’m assuming because convertToRaw takes the character-by-character OrderedSet and converts it to an array of inline style ranges. Can you think of a way I could use the current redraft API to ensure that styled inline elements never nest? If not, would you be willing to explore expanding or modifying the API to support a mode in which inline style ranges get rendered the way they are rendered in draft.js?

Thanks again!

Commas added to each tag when outputting to string

Hey great work on the lib!

I am running into an issue when trying to output my content to a string.

Its probably best to explain via an example:

rawContentState

const rawState = {
  entityMap: {
    "0": {
      type: "LINK",
      mutability: "MUTABLE",
      data: { url: "http://google.com" }
    }
  },
  blocks: [
    {
      key: "9rbap",
      text: "Well How are you today?",
      type: "unstyled",
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {}
    },
    {
      key: "28r0c",
      text: "Huh Can you tell me",
      type: "unstyled",
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {}
    },
    {
      key: "fjat8",
      text: "hgjhgg",
      type: "unstyled",
      depth: 0,
      inlineStyleRanges: [{ offset: 0, length: 6, style: "BOLD" }],
      entityRanges: [],
      data: {}
    },
    {
      key: "9a9iu",
      text: "something",
      type: "ordered-list-item",
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {}
    },
    {
      key: "bo5br",
      text: "I dont know",
      type: "ordered-list-item",
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [],
      data: {}
    },
    {
      key: "r4b2",
      text: "hello",
      type: "unstyled",
      depth: 0,
      inlineStyleRanges: [],
      entityRanges: [{ offset: 0, length: 5, key: 0 }],
      data: {}
    }
  ]
};

renderers and call to redraft

const styles = {
  code: {
    backgroundColor: "rgba(0, 0, 0, 0.05)",
    fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
    fontSize: 16,
    padding: 2
  },
  codeBlock: {
    backgroundColor: "rgba(0, 0, 0, 0.05)",
    fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
    fontSize: 16,
    padding: 20
  }
};

const inline = {
  BOLD: (children, { key }) => `<strong>${children}</strong>`,
  ITALIC: (children, { key }) => `<em>${children}</em>`,
  UNDERLINE: (children, { key }) => `<u>${children}</u>`,
  CODE: (children, { key }) =>
    `<span style=${styles.code}>
      ${children}
    </span>`
};

const addBreaklines = children => children.map(child => [child, "</br>"]);

/**
 * Note that children can be maped to render a list or do other cool stuff
 */
const blocks = {
  // Rendering blocks like this along with cleanup results in a single p tag for each paragraph
  // adding an empty block closes current paragraph and starts a new one
  unstyled: (children, { keys }) => `<p>${addBreaklines(children)}</p>`,
  blockquote: (children, { keys }) =>
    `<blockquote>${addBreaklines(children)}</blockquote>`,
  "header-one": (children, { keys }) =>
    children.map((child, i) => `<h1>${child}</h1>`),
  "header-two": (children, { keys }) =>
    children.map((child, i) => `<h2 >${child}</h2>`),
  "header-three": (children, { keys }) =>
    children.map((child, i) => `<h3>${child}</h3>`),
  "header-four": (children, { keys }) =>
    children.map((child, i) => `<h4>${child}</h4>`),
  "header-five": (children, { keys }) =>
    children.map((child, i) => `<h5 >${child}</h5>`),
  "header-six": (children, { keys }) =>
    children.map((child, i) => `<h6 >${child}</h6>`),
  "code-block": (children, { keys }) =>
    `<pre style=${styles.codeBlock}>
      ${addBreaklines(children)}
    </pre>`
};

const entities = {
  LINK: (children, entity, { key }) =>
    `<a href=${entity.url}>
      ${children}
    </a>`
};

const isEmptyRaw = raw =>
  !raw || !raw.blocks || (raw.blocks.length === 1 && raw.blocks[0].text === "");

const options = {
  cleanup: { trim: true },
  joinOutput: true
};

console.log(redraft(rawState, { inline, blocks, entities }, options));

Expected Output

<p>Well How are you today?</br>Huh Can you tell me,</br><strong>hgjhgg</strong></br></p><p>something </br> I dont know</br></p><p><a href=http://google.com>
      hello
    </a></br></p>

Actual Output

<p>,Well How are you today?,</br>,,Huh Can you tell me,</br>,,,<strong>hgjhgg</strong>,</br></p><p>,something,</br>,,I dont know,</br></p><p>,<a href=http://google.com>
      hello
    </a>,</br></p>

As we can see after every tag there is a , I am not sure if this is a bug? Or have I configured something incorrectly?

Thanks for the work
Martin.

travisCI support?

Hi, as there are already tests, I wonder if adding travisCI may be usefull?
If its ok, I can also take over the main work for this and do a PR with the travisCI config needed, so you only need to add the travisCI service via the repository config in github.

remove all the join('')

It'd be nice if redraft worked with string returning functions rather than requiring a bunch of join and recursive join functions tacked on. Happy to investigate this if there is interest.

fixed render entities

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch [email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/redraft/lib/render.js b/node_modules/redraft/lib/render.js
index 152aa99..8161376 100644
--- a/node_modules/redraft/lib/render.js
+++ b/node_modules/redraft/lib/render.js
@@ -63,7 +63,7 @@ var renderNode = exports.renderNode = function renderNode(node, inlineRenderers,
   if (node.entity !== null) {
     var entity = entityMap[node.entity];
     if (entity && entityRenderers[entity.type]) {
-      return entityRenderers[entity.type]((0, _checkJoin2.default)(children, options), entity.data, { key: node.entity });
+      return entityRenderers[entity.type]((0, _checkJoin2.default)(children, options), entity.data, {key: node.entity});
     }
   }
   if (node.decorator !== null) {
@@ -167,7 +167,7 @@ var renderBlocks = function renderBlocks(blocks) {
       return;
     }
     var node = Parser.parse(block);
-    var renderedNode = renderNode(node, inlineRenderers, entityRenderers, stylesRenderer, entityMap, options, (0, _getKeyGenerator2.default)());
+    var renderedNode = renderNode(node, inlineRenderers, entityRenderers, stylesRenderer, entityMap, options, (0, _getKeyGenerator2.default)()).filter(it => it !== undefined);
     // if type of the block has changed or the split flag is set
     // render and clear group
     if (prevType && prevType !== block.type || splitGroup) {
@@ -186,15 +186,19 @@ var renderBlocks = function renderBlocks(blocks) {
     }
     // push current node to group
     group.push(renderedNode);
-
+    if (!(renderedNode[0].length > 0)) {
+      rendered.push(renderedNode)
+    }
     // lastly save current type for refference
     prevType = block.type;
     prevDepth = block.depth;
     prevKeys.push(block.key);
     prevData.push(block.data);
   });
+
   // render last group
   renderGroup(group, blockRenderers, rendered, { prevType: prevType, prevDepth: prevDepth, prevKeys: prevKeys, prevData: prevData });
+  rendered.push()
   return (0, _checkJoin2.default)(rendered, options);
 };
 

This issue body was partially generated by patch-package.

depth is ignored

There is no way to render lists with depth.

i.e. like:

  • hey
    • ho
    • lets go

the JSON for this kind of list would be:

{
  "entityMap":{},
  "blocks":[{
    "key":"56q84",
    "text":"hey",
    "type":"ordered-list-item",
    "depth":0,
    "inlineStyleRanges":[],
    "entityRanges":[]
  }, {
    "key":"ff5fk",
    "text":"ho",
    "type":"ordered-list-item",
    "depth":1,
    "inlineStyleRanges":[],
    "entityRanges":[]
  }, {
    "key":"cjrt8",
    "text":"lets go",
    "type":"ordered-list-item",
    "depth":1,
    "inlineStyleRanges":[],
    "entityRanges":[]
  }]
}

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.