lokiuz/redraft

Improvements

aight8 opened this issue · 7 comments

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.

1. Decorators

I need to look into this but as it's supported by draft.js it should be possible to render those as well with redraft. Thank's for pointing this out. 👍

2. Interface nearer to draft.js

That's an interesting idea. I like the idea of making the api compatible with draft.js though, here are my few worries about this approach:

  • Both functions (blockStyleFn, blockRendererFn) assume contentBlock as their input but in docs those are instances of ContentBlock that would probably require to import parts of draft.js
  • If I understand correctly in draft.js blockMap is an Immutable.js Map - I would prefer to avoid dependencies (especially big ones like Immutable) as at this point redraft can be used without draft.js provided you have the raws stored in a DB.
  • One of the use cases is rendering to a different format - would this be still possible while using same api as draft.js?

arrr :/ decorators need content block as well in the strategy signature (contentBlock, callback).

I try to make a post which describes my idea later.

I currently implemented some wrapper functions which converts a more abstract configuration to all type of renderer which redraft needs and Editor configuration. So it's definitely possible to a certain degree.

About decorators I must make some more investigations before I can write something about it.

blockMap is the smallest problem since it's just an object, I just wrap the object before I pass it to the Editor component. blockRenderMap = Immutable.Map(blockMap)

In my current implementation I have abstracted the configuration and generate the configuration for redraft AND draft.js. (it's not perfect since there is a special use case with entities which not work. ALL entities are rendered by redraft, but the editor renders entities only when they are in atomic blocks - generated decorators which use entityMap can solve this... - but for this one it must be possible to use decorators in redraft)

The abstracted configuration:

Definition:

  • entityMap:Object<entityType:string, Component>
  • customStyleMap:<inlineStyle:string, Object<key:string, value:string>>
  • blockMap:<blockType:string, {element:string[, wrapper:ReactElement]}>
  • blockClassNames:Object<blockType:string, className:string>

Example:

const entityMap = {
  IMAGE: Image,
};

const customStyleMap = {
  BOLD: {
    fontWeight: 'bold',
  },
  ITALIC: {
    fontStyle: 'italic',
  },
  UNDERLINE: {
    textDecoration: 'underline',
  },
};

const blockMap = {
  'unstyled': {
    element: 'div',
  },
  'header-one': {
    element: 'h3',
  },
  'unordered-list-item': {
    element: 'li',
    wrapper: <ul className="RichText__block__unorderedList" />,
  },
  'ordered-list-item': {
    element: 'li',
    wrapper: <ol className="RichText__block__orderedList" />,
  },
  'atomic': {
    element: 'figure',
  },
};

const blockClassNames = {
  'unstyled': 'RichText__block__unstyled',
  'header-one': 'RichText__block__headerOne',
  'unordered-list-item': 'RichText__block__unorderedList__item',
  'ordered-list-item': 'RichText__block__orderedList__item',
  'atomic': 'RichText__block__atomic',
};

This generates input for the Editor and also for redraft.

Redraft

const renderers = {
  inline: inlineStyleRenderers, // generated by customStyleMap
  blocks: blockRenderers,       // generated by blockMap AND blockClassNames
  entities: entityRenderers,     // generated by entityMap
};

Editor

<Editor
      blockStyleFn={blockStyleFn}                      // function generated by blockClassNames
      blockRendererFn={blockRendererFn}        // generated by entityMap (atomic block handling)
      blockRenderMap={this.blockRenderMap} // just wrap blockMap with Immutable.Map() - aliasedElements not required!
      customStyleMap={customStyleMap}         // just take customStyleMap as it is
/>

Entity components

Entity Components which are defined in entityMap receives the entity data in blockProp property (in draft.js it's however a getter, in redraft it's a plain object - but this shouldn't matter).

In addition when an Entity component is rendered by draft.js then blockProp.editMode is true, in redraft it's false.

Limitations (of this abstraction)

Since there is an abstract configuration there are some limitations:

Bigger limitations

  • blockRendererFn - cannot have much magic since it's generated by the config.
    blockStyleFn - this too

Block data - are not handled since they are not in the content raw. But I currently don't see any reasons to use block data since you can achieve the same with atomic entities. (otherwise I would be interested on this special use cases)

Decorators: nope

Almost no limitations

  • customStyleFn - cannot be used since it's require ContentBlock but customStyleMap should be enough

No limitations

  • blockRenderMap - this is an object so it will not lose features
  • customStyleMap - this neither

So maybe a kind of abstraction (which is much Editor props oriented as possible) could be a good solution. And then generate configuration for both sides.

Btw what you mean with "One of the use cases is rendering to a different format - would this be still possible while using same api as draft.js?". Which formats you mean?

BTW: I just want to reference to this library for brainstorming:
https://github.com/icelab/draft-js-ast-exporter
The "bad" thing is it required the ContentState object instead of raw data - (strictly said it not requires draft-js but it requires it as peer dependency).
The good thing were: The whole rendering possibilities inclusive decorators, and also react-native rendering implementations.

Thank's for a lot of input.

As for what I meant as different formats - it can be anything. For example test cases use rendering to string (html). This is one of the reasons I'm bit reluctant to change current api.

I was wondering at some point if I should publish separately an extendable react component that would allow rendering the raw without the need for current configuration but uses redraft internally. Such component could implement an api more similar with draft.js as you describe. This way it might be possible to keep changes to the current api at minimum.

Finally got around to work on this. 😥

Seems to me I was able to make a working version with decorators using quite similar api to the original draft-js one, except for stubing the ContentBlock. For someone pulling whole draft-js in his code it should be possible to just create an actual ContentBlock instance providing a callback in options.

Also updated the readme. 😄

Closing this as decorators landed in 0.8.0 along with flatter render using style map. As for the rest of improvements I think the current api is quite suitable for its uses. I think creating a wrapper for redraft is a way to go if you want an API that's more like draf-js, tho might require a few PRs for it to work.