{"id":13725343,"url":"https://github.com/lokiuz/redraft","last_synced_at":"2025-04-12T21:20:01.833Z","repository":{"id":5734323,"uuid":"53794948","full_name":"lokiuz/redraft","owner":"lokiuz","description":"Renders the result of Draft.js convertToRaw using provided callbacks, works well with React","archived":false,"fork":false,"pushed_at":"2023-01-04T02:40:57.000Z","size":3730,"stargazers_count":282,"open_issues_count":47,"forks_count":34,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-04T00:47:03.893Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://lokiuz.github.io/redraft/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lokiuz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-13T16:23:55.000Z","updated_at":"2024-08-19T07:53:51.000Z","dependencies_parsed_at":"2023-01-13T13:45:08.793Z","dependency_job_id":null,"html_url":"https://github.com/lokiuz/redraft","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lokiuz%2Fredraft","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lokiuz%2Fredraft/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lokiuz%2Fredraft/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lokiuz%2Fredraft/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lokiuz","download_url":"https://codeload.github.com/lokiuz/redraft/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248632189,"owners_count":21136641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T01:02:20.118Z","updated_at":"2025-04-12T21:20:01.808Z","avatar_url":"https://github.com/lokiuz.png","language":"JavaScript","funding_links":[],"categories":["Common Utilities","JavaScript"],"sub_categories":[],"readme":"# Redraft\n\nRenders the result of Draft.js convertToRaw, works well with React and with minimal dependencies!\n\n[![Version](https://img.shields.io/npm/v/redraft.svg?style=flat-square)](https://www.npmjs.com/package/redraft)\n[![Build Status](https://img.shields.io/travis/lokiuz/redraft/master.svg?style=flat-square)](https://travis-ci.org/lokiuz/redraft)\n\n## What does it do?\n\nIt can convert whole raw state or just specific parts to desired output like React components or an html string.\n\nAdditionally you could just parse the raw using provided RawParser to get a nested structure for a specific block.\n\n## Install\n\n```sh\n$ npm install --save redraft\n```\n\n## Demo\n\nA live version of the example source is available [here](https://lokiuz.github.io/redraft/).\n\n## Rendering to React - simplified example\n\nDefine all the extra bits:\n\n```js\n/**\n *  You can use inline styles or classNames inside your callbacks\n */\nconst styles = {\n  code: {\n    backgroundColor: 'rgba(0, 0, 0, 0.05)',\n    fontFamily: '\"Inconsolata\", \"Menlo\", \"Consolas\", monospace',\n    fontSize: 16,\n    padding: 2,\n  },\n  codeBlock: {\n    backgroundColor: 'rgba(0, 0, 0, 0.05)',\n    fontFamily: '\"Inconsolata\", \"Menlo\", \"Consolas\", monospace',\n    fontSize: 16,\n    padding: 20,\n  },\n};\n\n// just a helper to add a \u003cbr /\u003e after a block\nconst addBreaklines = (children) =\u003e children.map(child =\u003e [child, \u003cbr /\u003e]);\n\n/**\n * Define the renderers\n */\nconst renderers = {\n  /**\n   * Those callbacks will be called recursively to render a nested structure\n   */\n  inline: {\n    // The key passed here is just an index based on rendering order inside a block\n    BOLD: (children, { key }) =\u003e \u003cstrong key={key}\u003e{children}\u003c/strong\u003e,\n    ITALIC: (children, { key }) =\u003e \u003cem key={key}\u003e{children}\u003c/em\u003e,\n    UNDERLINE: (children, { key }) =\u003e \u003cu key={key}\u003e{children}\u003c/u\u003e,\n    CODE: (children, { key }) =\u003e \u003cspan key={key} style={styles.code}\u003e{children}\u003c/span\u003e,\n  },\n  /**\n   * Blocks receive children and depth\n   * Note that children are an array of blocks with same styling,\n   */\n  blocks: {\n    unstyled: (children) =\u003e children.map(child =\u003e \u003cp\u003e{child}\u003c/p\u003e),\n    blockquote: (children) =\u003e \u003cblockquote \u003e{addBreaklines(children)}\u003c/blockquote\u003e,\n    'header-one': (children) =\u003e children.map(child =\u003e \u003ch1\u003e{child}\u003c/h1\u003e),\n    'header-two': (children) =\u003e children.map(child =\u003e \u003ch2\u003e{child}\u003c/h2\u003e),\n    // You can also access the original keys of the blocks\n    'code-block': (children, { keys }) =\u003e \u003cpre style={styles.codeBlock} key={keys[0]}\u003e{addBreaklines(children)}\u003c/pre\u003e,\n    // or depth for nested lists\n    'unordered-list-item': (children, { depth, keys }) =\u003e \u003cul key={keys[keys.length - 1]} className={`ul-level-${depth}`}\u003e{children.map(child =\u003e \u003cli\u003e{child}\u003c/li\u003e)}\u003c/ul\u003e,\n    'ordered-list-item': (children, { depth, keys }) =\u003e \u003col key={keys.join('|')} className={`ol-level-${depth}`}\u003e{children.map((child, index) =\u003e \u003cli key={keys[index]}\u003e{child}\u003c/li\u003e)}\u003c/ol\u003e,\n    // If your blocks use meta data it can also be accessed like keys\n    atomic: (children, { keys, data }) =\u003e children.map((child, i) =\u003e \u003cAtomic key={keys[i]} {...data[i]} /\u003e),\n  },\n  /**\n   * Entities receive children and the entity data\n   */\n  entities: {\n    // key is the entity key value from raw\n    LINK: (children, data, { key }) =\u003e \u003cLink key={key} to={data.url}\u003e{children}\u003c/Link\u003e,\n  },\n  /**\n   * Array of decorators,\n   * Entities receive children and the entity data,\n   * inspired by https://facebook.github.io/draft-js/docs/advanced-topics-decorators.html\n   * it's also possible to pass a custom Decorator class that matches the [DraftDecoratorType](https://github.com/facebook/draft-js/blob/master/src/model/decorators/DraftDecoratorType.js)\n   */\n  decorators: [\n    {\n      // by default linkStrategy receives a ContentBlock stub (more info under Creating the ContentBlock)\n      // strategy only receives first two arguments, contentState is yet not provided\n      strategy: linkStrategy,\n      // component - a callback as with other renderers\n      // decoratedText a plain string matched by the strategy\n      // if your decorator depends on draft-js contentState you need to provide convertFromRaw in redraft options\n      component: ({ children, decoratedText }) =\u003e \u003ca href={decoratedText}\u003e{children}\u003c/a\u003e,\n    },\n    new CustomDecorator(someOptions),\n  ],\n}\n```\n\nNow what is needed is a simple renderer component to wrap it all.\n\n```js\nexport default class Renderer extends Component {\n\n  static propTypes = {\n    raw: PropTypes.object\n  }\n\n  renderWarning() {\n    return \u003cdiv\u003eNothing to render.\u003c/div\u003e;\n  }\n\n  render() {\n    const { raw } = this.props;\n    if (!raw) {\n      return this.renderWarning();\n    }\n    const rendered = redraft(raw, renderers);\n    // redraft returns a null if there's nothing to render\n    if (!rendered) {\n      return this.renderWarning();\n    }\n    return (\n      \u003cdiv\u003e\n        {rendered}\n      \u003c/div\u003e\n    );\n  }\n}\n```\n\n## API\n\n```js\nredraft(Object:raw, Object:renderers, Object:options)\n```\n\nReturns an array of rendered blocks.\n\n-   **raw** - result of the Draft.js convertToRaw\n-   **renderers** - object with 3 groups of renders inline (or style), blocks and entities refer to example for more info\n-   **options** - optional settings\n\n#### Using styleMap and blockRenderMap instead of inline and block renders\n\nIf provided with a styles renderer in the renders, redraft will use it instead of the inline one. This allows a flatter render more like draft.js does in the editor. Redraft also exposes a helper to create the styles and block renderers.\n\n```js\nimport React from 'react';\nimport redraft, { createStylesRenderer, createBlockRenderer } from 'redraft';\n\nconst styleMap = {\n  BOLD: {\n    fontWeight: 'bold',\n  },\n  ITALIC: {\n    fontStyle: 'italic',\n  },\n  UNDERLINE: {\n    textDecoration: 'underline',\n  },\n};\n\n// This is a wrapper callback for the inline styles\n// the style object contains all the relevant styles from the styleMap\n// it needs a key as redraft returns arrays not Components\nconst InlineWrapper = ({ children, style, key }) =\u003e \u003cspan key={key} style={style}\u003e{children}\u003c/span\u003e\n// this Component results in a flatter output as it can have multiple styles (also possibly less semantic)\n\n// Api aligned w draft-js, aliasedElements are not required as draft-js uses them for parsing pasted html \nconst blockRenderMap = {\n  unstyled: {\n    element: 'div',\n  },\n  blockquote: {\n    element: 'blockquote',\n  },\n  'ordered-list-item': {\n    element: 'li',\n    wrapper: 'ol',\n  },\n  'unordered-list-item': {\n    element: 'li',\n    wrapper: 'ul',\n  },\n};\n\nconst renderers = {\n  // note the styles key and createStylesRenderer helper\n  styles: createStylesRenderer(InlineWrapper, styleMap),\n  blocks: createBlockRenderer(React.createElement, blockRenderMap),\n  ...\n};\n```\n\n## Options\n\n### Cleanup\n\n`cleanup` - cleans up blocks with no text or data (metadata or entities), by default cleanup only removes empty `unstyled` blocks inserted directly after `atomic`. Accepts false or an object containing cleanup settings:\n\n-   `after` - array of block types that are followed by cleanup checks, or `'all'` (default: `['atomic']`)\n-   `types` - array of block types that are checked, or `'all'` (default: `['unstyled']`)\n-   `except` - array of block types that are omitted during cleanup - passing this is same as setting types to `'all'` (default: `undefined`)\n-   `trim` - boolean, should the block text be trimmed when checking if its empty (default: `false`)\n-   `split` - boolean, splits groups after cleanup, works best when cleanup is enabled for and after all types - more info in the example (default: `true`)\n\n### Joining the output\n\n`joinOutput` - used when rendering to string, joins the output and the children of all the inline and entity renderers, it expects that all renderers return strings, you still have to join the at block level (default: `false`)\n\n### Render fallback for missing block type\n\n`blockFallback` - redraft will render this block type if its missing a block renderer for a specific type (default: `'unstyled'`)\n\n### Accessing contentState\n\n`convertFromRaw` - pass the draft-js convertFromRaw to provide the contentState object to both the components in your decorators and the custom Decorator class getDecorations method.\n\n### Creating the ContentBlock\n\n `createContentBlock` - a function that receives a block and returns a draft-js ContentBlock, if not provided when using decorators redraft will create a ContentBlock stub with only some basic ContentBlock functionality\n\n_Example usage with ContentBlock from draft-js_\n\n```js\nimport { ContentBlock } from 'draft-js'\n\nconst createContentBlock = block =\u003e new ContentBlock(block)\n```\n\n## Common issues\n\n#### Missing String.fromCodePoint in React Native\n\nConsider using a polyfill like [`String.fromCodePoint`](https://github.com/mathiasbynens/String.fromCodePoint) or [`babel-polyfill`](https://babeljs.io/docs/usage/polyfill/)\n\n#### Can the multiple spaces between text be persisted?\n\nAdd `white-space: pre-wrap` to a parent div, this way it will preserve spaces and wrap to new lines (as editor js does)\n\n## Changelog\n\nThe changelog is available here [CHANGELOG](CHANGELOG.md)\n\n## Credits\n\n-   [backdraft-js](https://github.com/evanc/backdraft-js) - For providing a general method of parsing raw state\n-   [Draft.js](https://facebook.github.io/draft-js) - Well for Draft\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flokiuz%2Fredraft","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flokiuz%2Fredraft","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flokiuz%2Fredraft/lists"}