将Draft.js原始ContentState中的富文本转换为HTML的库
项目描述
Draft.js导出器
将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]
。它还需要您的系统上安装libxml2
和libxslt
。 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制作的。
项目详情
下载文件
下载适合您平台文件的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。