跳转到主要内容

将Draft.js原始ContentState中的富文本转换为HTML的库

项目描述

Draft.js导出器

PyPI PyPI downloads Build status Coveralls

将Draft.js原始ContentState中的富文本转换为HTML的库。

它与Draftail富文本编辑器一起开发,用于Wagtail。查看在线演示,以及我们的入门博客文章

为什么

Draft.js 是一个用于 React 的富文本编辑器框架。它的方法与大多数富文本编辑器不同,因为它不存储数据为 HTML,而是存储在自己的表示形式 ContentState 中。当需要在 Python 生态系统中进行 ContentState 到 HTML 的转换时,此导出器非常有用。

最初的用例是为了在 Wagtail/Django 网站中更好地控制富文本编辑器管理的内容。如果你想了解完整的故事,请查看我们的博客文章: 重新思考 Draft.js 的富文本管道

功能

本项目遵循 语义化版本控制,并 测量性能代码覆盖率。代码通过 mypy 检查。

  • 生成的 HTML 的广泛配置。
  • 为常见的 HTML 元素提供默认的可扩展的块和内联样式映射。
  • 将换行符转换为 <br> 元素。
  • 在块映射中定义任何属性 - 元素的自定义类名。
  • 创建自定义组件的类似 React 的 API。
  • 自动将实体数据转换为 HTML 属性(整数和布尔值转换为字符串,样式对象转换为样式字符串)。
  • 嵌套列表(<li> 元素位于 <ul><ol> 内,具有多级)。
  • 将内联样式作为内联元素输出(<em><strong>、任意属性,任选其一)。
  • 重叠的内联样式和实体范围。
  • 静态类型注解。

用法

Draft.js 以基于块的 JSON 表示形式存储数据,表示编辑器中的内容行,并注释以表示富文本的实体和样式。有关更多信息,请参阅这篇文章: Draft.js 如何表示富文本数据

入门

此导出器以 Draft.js ContentState 数据作为输入,并基于其配置输出 HTML。要开始,请安装此包

pip install draftjs_exporter

我们支持以下 Python 版本:3.7、3.8、3.9、3.10、3.11。对于旧版 Python 版本,请在 变更日志 中查找兼容版本。

在你的代码中,创建一个导出器并使用 render 方法创建 HTML

from draftjs_exporter.dom import DOM
from draftjs_exporter.html import HTML

# Configuration options are detailed below.
config = {}

# Initialise the exporter.
exporter = HTML(config)

# Render a Draft.js `contentState`
html = exporter.render({
    'entityMap': {},
    'blocks': [{
        'key': '6mgfh',
        'text': 'Hello, world!',
        'type': 'unstyled',
        'depth': 0,
        'inlineStyleRanges': [],
        'entityRanges': []
    }]
})

print(html)

你也可以通过下载此存储库并运行 python example.py 或使用我们的 在线 Draft.js 演示 来运行示例。

配置

导出器的输出可广泛配置,以满足各种富文本需求。

# draftjs_exporter provides default configurations and predefined constants for reuse.
from draftjs_exporter.constants import BLOCK_TYPES, ENTITY_TYPES
from draftjs_exporter.defaults import BLOCK_MAP, STYLE_MAP
from draftjs_exporter.dom import DOM

config = {
    # `block_map` is a mapping from Draft.js block types to a definition of their HTML representation.
    # Extend BLOCK_MAP to start with sane defaults, or make your own from scratch.
    'block_map': dict(BLOCK_MAP, **{
        # The most basic mapping format, block type to tag name.
        BLOCK_TYPES.HEADER_TWO: 'h2',
        # Use a dict to define props on the block.
        BLOCK_TYPES.HEADER_THREE: {'element': 'h3', 'props': {'class': 'u-text-center'}},
        # Add a wrapper (and wrapper_props) to wrap adjacent blocks.
        BLOCK_TYPES.UNORDERED_LIST_ITEM: {
            'element': 'li',
            'wrapper': 'ul',
            'wrapper_props': {'class': 'bullet-list'},
        },
        # Use a custom component for more flexibility (reading block data or depth).
        BLOCK_TYPES.BLOCKQUOTE: blockquote,
        BLOCK_TYPES.ORDERED_LIST_ITEM: {
            'element': list_item,
            'wrapper': ordered_list,
        },
        # Provide a fallback component (advanced).
        BLOCK_TYPES.FALLBACK: block_fallback
    }),
    # `style_map` defines the HTML representation of inline elements.
    # Extend STYLE_MAP to start with sane defaults, or make your own from scratch.
    'style_map': dict(STYLE_MAP, **{
        # Use the same mapping format as in the `block_map`.
        'KBD': 'kbd',
        # The `style` prop can be defined as a dict, that will automatically be converted to a string.
        'HIGHLIGHT': {'element': 'strong', 'props': {'style': {'textDecoration': 'underline'}}},
        # Provide a fallback component (advanced).
        INLINE_STYLES.FALLBACK: style_fallback,
    }),
    'entity_decorators': {
        # Map entities to components so they can be rendered with their data.
        ENTITY_TYPES.IMAGE: image,
        ENTITY_TYPES.LINK: link
        # Lambdas work too.
        ENTITY_TYPES.HORIZONTAL_RULE: lambda props: DOM.create_element('hr'),
        # Discard those entities.
        ENTITY_TYPES.EMBED: None,
        # Provide a fallback component (advanced).
        ENTITY_TYPES.FALLBACK: entity_fallback,
    },
    'composite_decorators': [
        # Use composite decorators to replace text based on a regular expression.
        {
            'strategy': re.compile(r'\n'),
            'component': br,
        },
        {
            'strategy': re.compile(r'#\w+'),
            'component': hashtag,
        },
        {
            'strategy': LINKIFY_RE,
            'component': linkify,
        },
    ],
}

有关更多详细信息,请参阅 examples.py

高级用法

自定义组件

要使用动态数据生成任意标记,导出器提供了一个用于创建渲染组件的 API。此 API 与 React 的 createElement API 相似(JSX 编译成什么)。

# All of the API is available from a single `DOM` namespace
from draftjs_exporter.dom import DOM


# Components are simple functions that take `props` as parameter and return DOM elements.
def image(props):
    # This component creates an image element, with the relevant attributes.
    return DOM.create_element('img', {
        'src': props.get('src'),
        'width': props.get('width'),
        'height': props.get('height'),
        'alt': props.get('alt'),
    })


def blockquote(props):
    # This component uses block data to render a blockquote.
    block_data = props['block']['data']

    # Here, we want to display the block's content so we pass the `children` prop as the last parameter.
    return DOM.create_element('blockquote', {
        'cite': block_data.get('cite')
    }, props['children'])


def button(props):
    href = props.get('href', '#')
    icon_name = props.get('icon', None)
    text = props.get('text', '')

    return DOM.create_element('a', {
            'class': 'icon-text' if icon_name else None,
            'href': href,
        },
        # There can be as many `children` as required.
        # It is also possible to reuse other components and render them instead of HTML tags.
        DOM.create_element(icon, {'name': icon_name}) if icon_name else None,
        DOM.create_element('span', {'class': 'icon-text'}, text) if icon_name else text
    )

除了 create_element,还有一个 parse_html 方法。使用它与其他 HTML 生成器(如模板引擎)进行接口。

有关更多详细信息,请参阅存储库中的 examples.py

回退组件

在处理内容模式变更时(作为持续开发或迁移的一部分),某些内容可能会过时。为了解决这个问题,导出器允许为块、样式和实体定义回退组件。目前,此功能仅用于开发,如果您在生产环境中使用此功能,我们将非常乐意听取您的意见。请与我们联系!

将以下内容添加到导出器配置中,

config = {
    'block_map': dict(BLOCK_MAP, **{
        # Provide a fallback for block types.
        BLOCK_TYPES.FALLBACK: block_fallback
    }),
}

此回退组件现在可以控制导出器在找不到正常组件时的行为。以下是一个示例

def block_fallback(props):
    type_ = props['block']['type']

    if type_ == 'example-discard':
        logging.warn(f'Missing config for "{type_}". Discarding block, keeping content.')
        # Directly return the block's children to keep its content.
        return props['children']
    elif type_ == 'example-delete':
        logging.error(f'Missing config for "{type_}". Deleting block.')
        # Return None to not render anything, removing the whole block.
        return None
    else:
        logging.warn(f'Missing config for "{type_}". Using div instead.')
        # Provide a fallback.
        return DOM.create_element('div', {}, props['children'])

有关更多详细信息,请参阅存储库中的 examples.py

替代后端引擎

默认情况下,导出器使用一个无依赖项的引擎 string 来构建 DOM 树。有其他替代方案

  • html5lib(通过BeautifulSoup)
  • lxml.
  • string_compat(自首次发布以来没有向后不兼容变化的string变体)。

string引擎速度最快,没有任何依赖项。它的唯一缺点是parse_html方法不像其他引擎那样转义/净化HTML。

  • 对于html5lib,请执行pip install draftjs_exporter[html5lib]
  • 对于lxml,请执行pip install draftjs_exporter[lxml]。它还需要您的系统上安装libxml2libxslt
  • string_compat没有额外的依赖项。

然后,使用导出器配置的engine属性。

config = {
    # Specify which DOM backing engine to use.
    'engine': DOM.HTML5LIB,
    # Or for lxml:
    'engine': DOM.LXML,
    # Or to use the "maximum output stability" string_compat engine:
    'engine': DOM.STRING_COMPAT,
}

自定义后备引擎

导出器支持通过DOM API使用自定义引擎生成输出。这可以用于实现自定义导出格式,例如,到Markdown(实验性):Markdown

以下是一个示例实现

from draftjs_exporter import DOMEngine

class DOMListTree(DOMEngine):
    """
    Element tree using nested lists.
    """

    @staticmethod
    def create_tag(t, attr=None):
        return [t, attr, []]

    @staticmethod
    def append_child(elt, child):
        elt[2].append(child)

    @staticmethod
    def render(elt):
        return elt


exporter = HTML({
    # Use the dotted module syntax to point to the DOMEngine implementation.
    'engine': 'myproject.example.DOMListTree'
})

类型注解

导出器代码库使用静态类型注解,并使用mypy进行校验。可重用类型被提供。

from draftjs_exporter.dom import DOM
from draftjs_exporter.types import Element, Props


# Components are simple functions that take `props` as parameter and return DOM elements.
def image(props: Props) -> Element:
    # This component creates an image element, with the relevant attributes.
    return DOM.create_element('img', {
        'src': props.get('src'),
        'width': props.get('width'),
        'height': props.get('height'),
        'alt': props.get('alt'),
    })

贡献

您喜欢这里的任何内容吗?有任何缺失的吗?我们欢迎所有支持,无论是错误报告、功能请求、代码、设计、审查、测试、文档等等。请查看我们的贡献指南

如果您只想在自己的电脑上设置项目,贡献指南还包含所有设置命令。

致谢

此项目得以由新西兰数字代理机构Springload的工作实现。该美丽的演示网站是由@thibaudcolas制作的。

查看完整的贡献者列表:贡献者。采用MIT许可

下载文件

下载适合您平台文件的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

draftjs_exporter-5.0.0.tar.gz (33.3 kB 查看哈希

上传时间: 源代码

构建分布

draftjs_exporter-5.0.0-py3-none-any.whl (26.3 kB 查看哈希

上传时间: Python 3

由以下支持