lokiuz / redraft Goto Github PK
View Code? Open in Web Editor NEWRenders the result of Draft.js convertToRaw using provided callbacks, works well with React
Home Page: https://lokiuz.github.io/redraft/
License: MIT License
Renders the result of Draft.js convertToRaw using provided callbacks, works well with React
Home Page: https://lokiuz.github.io/redraft/
License: MIT License
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).
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:
EditorState
is the entity key exists. Say the Entity key in question is 123
.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.
Using Redraft with React Native gives trouble and throws a TransformError. The solution is to ignore .babelrc
in .npmignore
.
See the similar issues michaelcontento/redux-storage#65 and goatslacker/alt#558 for an explanation/reasoning.
Alternatively whitelisting instead of blacklisting via .npmignore
is also an option.
For reference, using these version:
$ react-native --version
react-native-cli: 2.0.1
react-native: 0.43.3
$ node -v
v6.9.1
$ npm -v
3.10.8
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.
v0.10.0
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:
Comparing equality between two arrays, regardless of their size, should return the correct boolean
.
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.
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()
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
Unsure if this works right now, but does redraft have ReactNative support? Can I just return <View />
s instead of <div />
s?
It would be nice if the redraft
could accept customStyleFn
that maps inline styles to css rules dynamically, just like in draft-js.
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!
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
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)
Hey there. Thank you for the plugin.
Can the multiple spaces between text be persisted? I could not find any options.
Here is a GIF that shows what I mean.
We're running into some bugs around decorating text that has emojis in it. Since a screenshot is worth a thousand words:
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:
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
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?
Is there a way to add empty lines as <br />
tag?
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?
in case the input contains an empty line in the first line, the result is null.
This is (probably) caused by https://github.com/lokiuz/redraft/blob/master/src/render.js#L141 which only checks the first line for text.
DraftJS supports more than just simple { strategy, component }
decorators, specifically it supports anything matching the DraftDecoratorType
:
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!
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.
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
Then reuse those for the draft.js config for the renderer.
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.
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!
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:
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: {}
}
]
};
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));
<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>
<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.
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.
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.
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.
the content array is empty when blocks have only 1 character in them.
There is no way to render lists with depth.
i.e. like:
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":[]
}]
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.