draftjs_exporter 5.2.0__tar.gz → 6.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- draftjs_exporter-6.0.0/PKG-INFO +75 -0
- draftjs_exporter-6.0.0/README.md +38 -0
- draftjs_exporter-6.0.0/draftjs_exporter/__init__.py +98 -0
- draftjs_exporter-6.0.0/draftjs_exporter/command.py +69 -0
- draftjs_exporter-6.0.0/draftjs_exporter/composite_decorators.py +137 -0
- {draftjs_exporter-5.2.0 → draftjs_exporter-6.0.0}/draftjs_exporter/constants.py +29 -0
- {draftjs_exporter-5.2.0 → draftjs_exporter-6.0.0}/draftjs_exporter/defaults.py +14 -7
- draftjs_exporter-6.0.0/draftjs_exporter/dom.py +183 -0
- draftjs_exporter-6.0.0/draftjs_exporter/engines/__init__.py +1 -0
- draftjs_exporter-6.0.0/draftjs_exporter/engines/base.py +79 -0
- draftjs_exporter-6.0.0/draftjs_exporter/engines/html5lib.py +89 -0
- draftjs_exporter-6.0.0/draftjs_exporter/engines/lxml.py +108 -0
- draftjs_exporter-6.0.0/draftjs_exporter/engines/markdown.py +172 -0
- draftjs_exporter-6.0.0/draftjs_exporter/engines/string.py +188 -0
- {draftjs_exporter-5.2.0 → draftjs_exporter-6.0.0}/draftjs_exporter/engines/string_compat.py +38 -7
- draftjs_exporter-6.0.0/draftjs_exporter/entity_state.py +155 -0
- draftjs_exporter-6.0.0/draftjs_exporter/error.py +9 -0
- draftjs_exporter-6.0.0/draftjs_exporter/html.py +263 -0
- draftjs_exporter-6.0.0/draftjs_exporter/markdown/__init__.py +222 -0
- draftjs_exporter-6.0.0/draftjs_exporter/markdown/blocks.py +62 -0
- draftjs_exporter-6.0.0/draftjs_exporter/markdown/code.py +42 -0
- draftjs_exporter-6.0.0/draftjs_exporter/markdown/entities.py +43 -0
- draftjs_exporter-6.0.0/draftjs_exporter/markdown/fallbacks.py +50 -0
- draftjs_exporter-6.0.0/draftjs_exporter/markdown/helpers.py +28 -0
- draftjs_exporter-6.0.0/draftjs_exporter/markdown/lists.py +106 -0
- draftjs_exporter-6.0.0/draftjs_exporter/markdown/styles.py +16 -0
- draftjs_exporter-6.0.0/draftjs_exporter/options.py +185 -0
- draftjs_exporter-6.0.0/draftjs_exporter/style_state.py +143 -0
- draftjs_exporter-6.0.0/draftjs_exporter/types.py +111 -0
- draftjs_exporter-6.0.0/draftjs_exporter/utils/__init__.py +1 -0
- {draftjs_exporter-5.2.0 → draftjs_exporter-6.0.0}/draftjs_exporter/utils/module_loading.py +16 -7
- {draftjs_exporter-5.2.0 → draftjs_exporter-6.0.0}/draftjs_exporter/wrapper_state.py +115 -30
- {draftjs_exporter-5.2.0 → draftjs_exporter-6.0.0}/pyproject.toml +56 -16
- draftjs_exporter-5.2.0/LICENSE +0 -9
- draftjs_exporter-5.2.0/PKG-INFO +0 -376
- draftjs_exporter-5.2.0/README.md +0 -330
- draftjs_exporter-5.2.0/draftjs_exporter/__init__.py +0 -5
- draftjs_exporter-5.2.0/draftjs_exporter/command.py +0 -56
- draftjs_exporter-5.2.0/draftjs_exporter/composite_decorators.py +0 -93
- draftjs_exporter-5.2.0/draftjs_exporter/dom.py +0 -129
- draftjs_exporter-5.2.0/draftjs_exporter/engines/__init__.py +0 -0
- draftjs_exporter-5.2.0/draftjs_exporter/engines/base.py +0 -50
- draftjs_exporter-5.2.0/draftjs_exporter/engines/html5lib.py +0 -45
- draftjs_exporter-5.2.0/draftjs_exporter/engines/lxml.py +0 -49
- draftjs_exporter-5.2.0/draftjs_exporter/engines/string.py +0 -116
- draftjs_exporter-5.2.0/draftjs_exporter/entity_state.py +0 -108
- draftjs_exporter-5.2.0/draftjs_exporter/error.py +0 -6
- draftjs_exporter-5.2.0/draftjs_exporter/html.py +0 -180
- draftjs_exporter-5.2.0/draftjs_exporter/options.py +0 -105
- draftjs_exporter-5.2.0/draftjs_exporter/style_state.py +0 -49
- draftjs_exporter-5.2.0/draftjs_exporter/types.py +0 -35
- draftjs_exporter-5.2.0/draftjs_exporter/utils/__init__.py +0 -0
- {draftjs_exporter-5.2.0 → draftjs_exporter-6.0.0}/draftjs_exporter/py.typed +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: draftjs_exporter
|
|
3
|
+
Version: 6.0.0
|
|
4
|
+
Summary: Library to convert rich text from Draft.js raw ContentState to HTML
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Classifier: Environment :: Web Environment
|
|
7
|
+
Classifier: Intended Audience :: Developers
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
19
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: Text Editors :: Word Processors
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Dist: beautifulsoup4>=4.4.1,<5 ; extra == 'html5lib'
|
|
26
|
+
Requires-Dist: html5lib>=0.999,<2 ; extra == 'html5lib'
|
|
27
|
+
Requires-Dist: lxml>=4.6.5,<7 ; extra == 'lxml'
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Project-URL: Documentation, https://wagtail.github.io/draftjs_exporter/
|
|
30
|
+
Project-URL: Source, https://github.com/wagtail/draftjs_exporter
|
|
31
|
+
Project-URL: Tracker, https://github.com/wagtail/draftjs_exporter/issues
|
|
32
|
+
Project-URL: Changelog, https://github.com/wagtail/draftjs_exporter/blob/main/CHANGELOG.md
|
|
33
|
+
Project-URL: llms.txt, https://wagtail.github.io/draftjs_exporter/llms.txt
|
|
34
|
+
Provides-Extra: html5lib
|
|
35
|
+
Provides-Extra: lxml
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# [Draft.js exporter](https://pypi.org/project/draftjs_exporter/)
|
|
39
|
+
|
|
40
|
+
> Library to convert rich text from Draft.js raw ContentState to HTML.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), and [measures performance](https://thib.me/python-memory-profiling-for-the-draft-js-exporter) and [code coverage](https://coveralls.io/github/wagtail/draftjs_exporter). Code is checked with [mypy](https://mypy.readthedocs.io/en/latest/index.html).
|
|
45
|
+
|
|
46
|
+
- Extensive configuration of the generated HTML.
|
|
47
|
+
- Default, extensible block & inline style maps for common HTML elements.
|
|
48
|
+
- Convert line breaks to `<br>` elements with decorators.
|
|
49
|
+
- Define any attribute in the block map – custom class names for elements.
|
|
50
|
+
- React-like API to create custom components.
|
|
51
|
+
- Automatic conversion of entity data to HTML attributes (int & boolean to string, style object to style string).
|
|
52
|
+
- Nested lists (`<li>` elements go inside `<ul>` or `<ol>`, with multiple levels).
|
|
53
|
+
- Output inline styles as inline elements (`<em>`, `<strong>`, pick and choose, with any attribute).
|
|
54
|
+
- Overlapping inline style and entity ranges.
|
|
55
|
+
- Static type annotations.
|
|
56
|
+
|
|
57
|
+
It is developed alongside the [Draftail](https://www.draftail.org/) rich text editor, for [Wagtail](https://github.com/wagtail/wagtail). Check out the [online demo](http://playground.draftail.org/), and our [introductory blog post](https://www.draftail.org/blog/2018/03/13/rethinking-rich-text-pipelines-with-draft-js).
|
|
58
|
+
|
|
59
|
+
## When to use the exporter
|
|
60
|
+
|
|
61
|
+
[Draft.js](https://draftjs.org/) is a rich text editor framework for [React](https://reactjs.org/). Its approach is different from most rich text editors because it does not store data as HTML, but rather in its own representation called ContentState. This exporter is useful when the ContentState to HTML conversion has to be done in a Python ecosystem.
|
|
62
|
+
|
|
63
|
+
The initial use case was to gain more control over the content managed by rich text editors in a Wagtail/Django site. If you want to read the full story, have a look at our blog post: [Rethinking rich text pipelines with Draft.js](https://www.draftail.org/blog/2018/03/13/rethinking-rich-text-pipelines-with-draft-js).
|
|
64
|
+
|
|
65
|
+
## Documentation
|
|
66
|
+
|
|
67
|
+
The full documentation is available at [on the docs website](https://wagtail.github.io/draftjs_exporter/). For LLM-assisted tooling, there is also a concise [`llms.txt`](https://wagtail.github.io/draftjs_exporter/llms.txt) and a complete [`llms-full.txt`](https://wagtail.github.io/draftjs_exporter/llms-full.txt).
|
|
68
|
+
|
|
69
|
+
## Contributing
|
|
70
|
+
|
|
71
|
+
See anything you like in here? Anything missing? We welcome all support, whether on bug reports, feature requests, code, design, reviews, tests, documentation, and more. Please have a look at our [contribution guidelines](docs/CONTRIBUTING.md).
|
|
72
|
+
|
|
73
|
+
## Credits
|
|
74
|
+
|
|
75
|
+
Draftail was created by [Springload](https://github.com/springload/), and is now maintained by Wagtail contributors. View the full list of [contributors](https://github.com/wagtail/draftjs_exporter/graphs/contributors). MIT licensed.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# [Draft.js exporter](https://pypi.org/project/draftjs_exporter/)
|
|
2
|
+
|
|
3
|
+
> Library to convert rich text from Draft.js raw ContentState to HTML.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), and [measures performance](https://thib.me/python-memory-profiling-for-the-draft-js-exporter) and [code coverage](https://coveralls.io/github/wagtail/draftjs_exporter). Code is checked with [mypy](https://mypy.readthedocs.io/en/latest/index.html).
|
|
8
|
+
|
|
9
|
+
- Extensive configuration of the generated HTML.
|
|
10
|
+
- Default, extensible block & inline style maps for common HTML elements.
|
|
11
|
+
- Convert line breaks to `<br>` elements with decorators.
|
|
12
|
+
- Define any attribute in the block map – custom class names for elements.
|
|
13
|
+
- React-like API to create custom components.
|
|
14
|
+
- Automatic conversion of entity data to HTML attributes (int & boolean to string, style object to style string).
|
|
15
|
+
- Nested lists (`<li>` elements go inside `<ul>` or `<ol>`, with multiple levels).
|
|
16
|
+
- Output inline styles as inline elements (`<em>`, `<strong>`, pick and choose, with any attribute).
|
|
17
|
+
- Overlapping inline style and entity ranges.
|
|
18
|
+
- Static type annotations.
|
|
19
|
+
|
|
20
|
+
It is developed alongside the [Draftail](https://www.draftail.org/) rich text editor, for [Wagtail](https://github.com/wagtail/wagtail). Check out the [online demo](http://playground.draftail.org/), and our [introductory blog post](https://www.draftail.org/blog/2018/03/13/rethinking-rich-text-pipelines-with-draft-js).
|
|
21
|
+
|
|
22
|
+
## When to use the exporter
|
|
23
|
+
|
|
24
|
+
[Draft.js](https://draftjs.org/) is a rich text editor framework for [React](https://reactjs.org/). Its approach is different from most rich text editors because it does not store data as HTML, but rather in its own representation called ContentState. This exporter is useful when the ContentState to HTML conversion has to be done in a Python ecosystem.
|
|
25
|
+
|
|
26
|
+
The initial use case was to gain more control over the content managed by rich text editors in a Wagtail/Django site. If you want to read the full story, have a look at our blog post: [Rethinking rich text pipelines with Draft.js](https://www.draftail.org/blog/2018/03/13/rethinking-rich-text-pipelines-with-draft-js).
|
|
27
|
+
|
|
28
|
+
## Documentation
|
|
29
|
+
|
|
30
|
+
The full documentation is available at [on the docs website](https://wagtail.github.io/draftjs_exporter/). For LLM-assisted tooling, there is also a concise [`llms.txt`](https://wagtail.github.io/draftjs_exporter/llms.txt) and a complete [`llms-full.txt`](https://wagtail.github.io/draftjs_exporter/llms-full.txt).
|
|
31
|
+
|
|
32
|
+
## Contributing
|
|
33
|
+
|
|
34
|
+
See anything you like in here? Anything missing? We welcome all support, whether on bug reports, feature requests, code, design, reviews, tests, documentation, and more. Please have a look at our [contribution guidelines](docs/CONTRIBUTING.md).
|
|
35
|
+
|
|
36
|
+
## Credits
|
|
37
|
+
|
|
38
|
+
Draftail was created by [Springload](https://github.com/springload/), and is now maintained by Wagtail contributors. View the full list of [contributors](https://github.com/wagtail/draftjs_exporter/graphs/contributors). MIT licensed.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Public API and metadata of draftjs_exporter.
|
|
2
|
+
|
|
3
|
+
This module gathers metadata, core classes, default configuration maps,
|
|
4
|
+
type aliases, and helpers so callers can import everything from
|
|
5
|
+
``draftjs_exporter`` directly.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__title__ = "draftjs_exporter"
|
|
9
|
+
__version__ = "6.0.0"
|
|
10
|
+
__uri__ = "https://wagtail.github.io/draftjs_exporter/"
|
|
11
|
+
__author__ = "Springload and Contributors"
|
|
12
|
+
__license__ = "MIT"
|
|
13
|
+
__copyright__ = "Copyright 2016-present Springload and Contributors"
|
|
14
|
+
|
|
15
|
+
from draftjs_exporter.constants import BLOCK_TYPES as BLOCK_TYPES
|
|
16
|
+
from draftjs_exporter.constants import ENTITY_TYPES as ENTITY_TYPES
|
|
17
|
+
from draftjs_exporter.constants import INLINE_STYLES as INLINE_STYLES
|
|
18
|
+
from draftjs_exporter.defaults import BLOCK_MAP as BLOCK_MAP
|
|
19
|
+
from draftjs_exporter.defaults import STYLE_MAP as STYLE_MAP
|
|
20
|
+
from draftjs_exporter.dom import DOM as DOM
|
|
21
|
+
from draftjs_exporter.html import HTML as HTML
|
|
22
|
+
from draftjs_exporter.html import ExporterConfig as ExporterConfig
|
|
23
|
+
from draftjs_exporter.markdown import CONFIG as MARKDOWN_CONFIG
|
|
24
|
+
from draftjs_exporter.markdown import MarkdownOptions as MarkdownOptions
|
|
25
|
+
from draftjs_exporter.markdown import build_markdown_config as build_markdown_config
|
|
26
|
+
from draftjs_exporter.types import Block as Block
|
|
27
|
+
from draftjs_exporter.types import Component as Component
|
|
28
|
+
from draftjs_exporter.types import CompositeDecorators as CompositeDecorators
|
|
29
|
+
from draftjs_exporter.types import ConfigMap as ConfigMap
|
|
30
|
+
from draftjs_exporter.types import ContentState as ContentState
|
|
31
|
+
from draftjs_exporter.types import Decorator as Decorator
|
|
32
|
+
from draftjs_exporter.types import Element as Element
|
|
33
|
+
from draftjs_exporter.types import Entity as Entity
|
|
34
|
+
from draftjs_exporter.types import EntityKey as EntityKey
|
|
35
|
+
from draftjs_exporter.types import EntityMap as EntityMap
|
|
36
|
+
from draftjs_exporter.types import EntityRange as EntityRange
|
|
37
|
+
from draftjs_exporter.types import InlineStyleRange as InlineStyleRange
|
|
38
|
+
from draftjs_exporter.types import Mutability as Mutability
|
|
39
|
+
from draftjs_exporter.types import Props as Props
|
|
40
|
+
from draftjs_exporter.types import RenderableConfig as RenderableConfig
|
|
41
|
+
from draftjs_exporter.types import RenderableType as RenderableType
|
|
42
|
+
from draftjs_exporter.types import Tag as Tag
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
# Metadata
|
|
46
|
+
"__title__",
|
|
47
|
+
"__version__",
|
|
48
|
+
"__uri__",
|
|
49
|
+
"__author__",
|
|
50
|
+
"__license__",
|
|
51
|
+
"__copyright__",
|
|
52
|
+
# Core
|
|
53
|
+
"Exporter",
|
|
54
|
+
"HTML",
|
|
55
|
+
"ExporterConfig",
|
|
56
|
+
"ContentState",
|
|
57
|
+
"DOM",
|
|
58
|
+
# Configs
|
|
59
|
+
"HTML_CONFIG",
|
|
60
|
+
"MARKDOWN_CONFIG",
|
|
61
|
+
"MarkdownOptions",
|
|
62
|
+
"build_markdown_config",
|
|
63
|
+
# Constants
|
|
64
|
+
"BLOCK_TYPES",
|
|
65
|
+
"ENTITY_TYPES",
|
|
66
|
+
"INLINE_STYLES",
|
|
67
|
+
# Default maps
|
|
68
|
+
"BLOCK_MAP",
|
|
69
|
+
"STYLE_MAP",
|
|
70
|
+
# Types
|
|
71
|
+
"Block",
|
|
72
|
+
"Component",
|
|
73
|
+
"CompositeDecorators",
|
|
74
|
+
"ConfigMap",
|
|
75
|
+
"ContentState",
|
|
76
|
+
"Decorator",
|
|
77
|
+
"Element",
|
|
78
|
+
"Entity",
|
|
79
|
+
"EntityKey",
|
|
80
|
+
"EntityMap",
|
|
81
|
+
"EntityRange",
|
|
82
|
+
"InlineStyleRange",
|
|
83
|
+
"Mutability",
|
|
84
|
+
"Props",
|
|
85
|
+
"RenderableConfig",
|
|
86
|
+
"RenderableType",
|
|
87
|
+
"Tag",
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
Exporter = HTML
|
|
91
|
+
"""Compatibility alias for the HTML exporter."""
|
|
92
|
+
|
|
93
|
+
HTML_CONFIG: ExporterConfig = {
|
|
94
|
+
"block_map": BLOCK_MAP,
|
|
95
|
+
"style_map": STYLE_MAP,
|
|
96
|
+
"engine": DOM.STRING,
|
|
97
|
+
}
|
|
98
|
+
"""Default exporter configuration using the built-in maps and string engine."""
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Represent and build rendering commands derived from Draft.js ranges."""
|
|
2
|
+
|
|
3
|
+
from draftjs_exporter.types import Block
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Command:
|
|
7
|
+
"""Represent an operation applied while converting a block to HTML nodes."""
|
|
8
|
+
|
|
9
|
+
__slots__ = ("name", "index", "data")
|
|
10
|
+
|
|
11
|
+
def __init__(self, name: str, index: int, data: str = "") -> None:
|
|
12
|
+
"""Initialize a command.
|
|
13
|
+
|
|
14
|
+
Parameters:
|
|
15
|
+
name: The operation name, such as ``start_entity`` or ``stop_inline_style``.
|
|
16
|
+
index: The character offset at which the command applies.
|
|
17
|
+
data: The payload for the command, such as an entity key or style name.
|
|
18
|
+
"""
|
|
19
|
+
self.name = name
|
|
20
|
+
self.index = index
|
|
21
|
+
self.data = data
|
|
22
|
+
|
|
23
|
+
def __str__(self) -> str:
|
|
24
|
+
"""Return a human-readable representation of the command."""
|
|
25
|
+
return f"<Command {self.name} {self.index} {self.data}>"
|
|
26
|
+
|
|
27
|
+
def __repr__(self) -> str:
|
|
28
|
+
"""Return the same representation as ``__str__`` for debugging."""
|
|
29
|
+
return str(self)
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def from_entity_ranges(block: Block) -> list["Command"]:
|
|
33
|
+
"""Create start and stop commands from a block's entity ranges.
|
|
34
|
+
|
|
35
|
+
Parameters:
|
|
36
|
+
block: The block containing ``entityRanges``.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
A list of start_entity and stop_entity commands.
|
|
40
|
+
"""
|
|
41
|
+
commands: list["Command"] = []
|
|
42
|
+
for r in block.get("entityRanges", []):
|
|
43
|
+
# Entity key is an integer in entity ranges, while a string in the entity map.
|
|
44
|
+
data = str(r["key"])
|
|
45
|
+
start = r["offset"]
|
|
46
|
+
stop = start + r["length"]
|
|
47
|
+
commands.append(Command("start_entity", start, data))
|
|
48
|
+
commands.append(Command("stop_entity", stop, data))
|
|
49
|
+
|
|
50
|
+
return commands
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def from_style_ranges(block: Block) -> list["Command"]:
|
|
54
|
+
"""Create start and stop commands from a block's inline style ranges.
|
|
55
|
+
|
|
56
|
+
Parameters:
|
|
57
|
+
block: The block containing ``inlineStyleRanges``.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
A list of start_inline_style and stop_inline_style commands.
|
|
61
|
+
"""
|
|
62
|
+
commands: list["Command"] = []
|
|
63
|
+
for r in block.get("inlineStyleRanges", []):
|
|
64
|
+
data = r["style"]
|
|
65
|
+
start = r["offset"]
|
|
66
|
+
stop = start + r["length"]
|
|
67
|
+
commands.append(Command("start_inline_style", start, data))
|
|
68
|
+
commands.append(Command("stop_inline_style", stop, data))
|
|
69
|
+
return commands
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Apply regex-based composite decorators to text while rendering blocks."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from collections.abc import Generator
|
|
5
|
+
from operator import itemgetter
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from draftjs_exporter.dom import DOM
|
|
9
|
+
from draftjs_exporter.engines.base import DOMEngine
|
|
10
|
+
from draftjs_exporter.types import Block, CompositeDecorators, Decorator, Element
|
|
11
|
+
|
|
12
|
+
br = "\n"
|
|
13
|
+
"""Line-break character handled by the default decorator strategy."""
|
|
14
|
+
|
|
15
|
+
br_strategy = re.compile(r"\n")
|
|
16
|
+
"""Default decorator strategy matching single line breaks."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_decorations(
|
|
20
|
+
decorators: CompositeDecorators, text: str
|
|
21
|
+
) -> list[tuple[int, int, Any, Decorator]]:
|
|
22
|
+
"""Collect non-overlapping decorator matches for the given text.
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
decorators: Composite decorator definitions.
|
|
26
|
+
text: The block text to decorate.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
A sorted list of decorations as ``(start, end, match, decorator)`` tuples.
|
|
30
|
+
"""
|
|
31
|
+
occupied: dict[int, int] = {}
|
|
32
|
+
decorations = []
|
|
33
|
+
|
|
34
|
+
for decorator in decorators:
|
|
35
|
+
for match in decorator["strategy"].finditer(text):
|
|
36
|
+
begin, end = match.span()
|
|
37
|
+
if not any(occupied.get(i) for i in range(begin, end)):
|
|
38
|
+
for i in range(begin, end):
|
|
39
|
+
occupied[i] = 1
|
|
40
|
+
decorations.append((begin, end, match, decorator))
|
|
41
|
+
|
|
42
|
+
decorations.sort(key=itemgetter(0))
|
|
43
|
+
|
|
44
|
+
return decorations
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def apply_decorators(
|
|
48
|
+
decorators: CompositeDecorators,
|
|
49
|
+
text: str,
|
|
50
|
+
block: Block,
|
|
51
|
+
blocks: list[Block],
|
|
52
|
+
) -> Generator[str, None, None]:
|
|
53
|
+
"""Yield decorated text segments and decorator elements for a block.
|
|
54
|
+
|
|
55
|
+
Parameters:
|
|
56
|
+
decorators: Composite decorator definitions.
|
|
57
|
+
text: The block text to decorate.
|
|
58
|
+
block: The block currently being rendered.
|
|
59
|
+
blocks: All blocks in the content state.
|
|
60
|
+
|
|
61
|
+
Yields:
|
|
62
|
+
Plain text or decorator element nodes covering the full block text.
|
|
63
|
+
"""
|
|
64
|
+
decorations = get_decorations(decorators, text)
|
|
65
|
+
|
|
66
|
+
pointer = 0
|
|
67
|
+
for begin, end, match, decorator in decorations:
|
|
68
|
+
if pointer < begin:
|
|
69
|
+
yield text[pointer:begin]
|
|
70
|
+
|
|
71
|
+
yield DOM.create_element(
|
|
72
|
+
decorator["component"],
|
|
73
|
+
{"match": match, "block": block, "blocks": blocks},
|
|
74
|
+
match.group(0),
|
|
75
|
+
)
|
|
76
|
+
pointer = end
|
|
77
|
+
|
|
78
|
+
if pointer < len(text):
|
|
79
|
+
yield text[pointer:]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def render_decorators(
|
|
83
|
+
decorators: CompositeDecorators,
|
|
84
|
+
text: str,
|
|
85
|
+
block: Block,
|
|
86
|
+
blocks: list[Block],
|
|
87
|
+
dom: type[DOMEngine],
|
|
88
|
+
) -> Element:
|
|
89
|
+
"""Render all decorator output for a block into a single element.
|
|
90
|
+
|
|
91
|
+
Parameters:
|
|
92
|
+
decorators: Composite decorator definitions.
|
|
93
|
+
text: The block text to decorate.
|
|
94
|
+
block: The block currently being rendered.
|
|
95
|
+
blocks: All blocks in the content state.
|
|
96
|
+
dom: The active DOM engine.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
A single element containing all decorated text, or the text itself.
|
|
100
|
+
"""
|
|
101
|
+
decorated_children = list(apply_decorators(decorators, text, block, blocks))
|
|
102
|
+
|
|
103
|
+
if len(decorated_children) == 1:
|
|
104
|
+
decorated_node = decorated_children[0]
|
|
105
|
+
else:
|
|
106
|
+
decorated_node = DOM.create_element()
|
|
107
|
+
for decorated_child in decorated_children:
|
|
108
|
+
dom.append_child(decorated_node, decorated_child)
|
|
109
|
+
|
|
110
|
+
return decorated_node
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def should_render_decorators(
|
|
114
|
+
decorators: CompositeDecorators,
|
|
115
|
+
text: str,
|
|
116
|
+
) -> bool:
|
|
117
|
+
"""Return whether decorators need to be processed for the given text.
|
|
118
|
+
|
|
119
|
+
Parameters:
|
|
120
|
+
decorators: Composite decorator definitions.
|
|
121
|
+
text: The block text to check.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
True when there are decorators and the default newline-only optimization does not apply.
|
|
125
|
+
"""
|
|
126
|
+
nb_decorators = len(decorators)
|
|
127
|
+
|
|
128
|
+
if nb_decorators == 0:
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
is_skippable_br = (
|
|
132
|
+
nb_decorators == 1
|
|
133
|
+
and decorators[0]["strategy"] == br_strategy
|
|
134
|
+
and br not in text
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return not is_skippable_br
|
|
@@ -1,11 +1,36 @@
|
|
|
1
|
+
"""Draft.js-compatible constants used by the exporter.
|
|
2
|
+
|
|
3
|
+
Defines the block types, inline styles, and entity categories supported
|
|
4
|
+
by Draft.js, plus a small enum helper.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
1
8
|
# http://stackoverflow.com/a/22723724/1798491
|
|
2
9
|
class Enum:
|
|
10
|
+
"""Minimal enum-like container exposing a fixed set of string attributes.
|
|
11
|
+
|
|
12
|
+
Accessing a registered attribute returns its name. Accessing any other
|
|
13
|
+
attribute raises ``AttributeError``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
3
16
|
__slots__ = "elements"
|
|
4
17
|
|
|
5
18
|
def __init__(self, *elements: str) -> None:
|
|
19
|
+
"""Store the allowed attribute names for the enum."""
|
|
6
20
|
self.elements = tuple(elements)
|
|
7
21
|
|
|
8
22
|
def __getattr__(self, name: str) -> str:
|
|
23
|
+
"""Return the attribute name if it is a registered enum element.
|
|
24
|
+
|
|
25
|
+
Parameters:
|
|
26
|
+
name: Attribute name being accessed.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
The requested attribute name.
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
AttributeError: If the name is not a registered element.
|
|
33
|
+
"""
|
|
9
34
|
if name not in self.elements:
|
|
10
35
|
raise AttributeError(f"'Enum' has no attribute '{name}'")
|
|
11
36
|
|
|
@@ -14,6 +39,8 @@ class Enum:
|
|
|
14
39
|
|
|
15
40
|
# https://github.com/facebook/draft-js/blob/master/src/model/constants/DraftBlockType.js
|
|
16
41
|
class BLOCK_TYPES:
|
|
42
|
+
"""Draft.js block types mapped to HTML elements."""
|
|
43
|
+
|
|
17
44
|
UNSTYLED = "unstyled"
|
|
18
45
|
HEADER_ONE = "header-one"
|
|
19
46
|
HEADER_TWO = "header-two"
|
|
@@ -40,6 +67,7 @@ ENTITY_TYPES = Enum(
|
|
|
40
67
|
# Special type to configure handling of missing components.
|
|
41
68
|
"FALLBACK",
|
|
42
69
|
)
|
|
70
|
+
"""Draft.js entity categories."""
|
|
43
71
|
|
|
44
72
|
INLINE_STYLES = Enum(
|
|
45
73
|
"BOLD",
|
|
@@ -59,3 +87,4 @@ INLINE_STYLES = Enum(
|
|
|
59
87
|
# Special type to configure handling of missing components.
|
|
60
88
|
"FALLBACK",
|
|
61
89
|
)
|
|
90
|
+
"""Draft.js inline style names."""
|
|
@@ -1,24 +1,28 @@
|
|
|
1
|
+
"""Default block and style maps for the exporter.
|
|
2
|
+
|
|
3
|
+
The mappings connect Draft.js block types and inline styles to HTML
|
|
4
|
+
tags or component functions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
1
7
|
from draftjs_exporter.constants import BLOCK_TYPES, INLINE_STYLES
|
|
2
8
|
from draftjs_exporter.dom import DOM
|
|
3
|
-
from draftjs_exporter.types import Element, Props
|
|
9
|
+
from draftjs_exporter.types import ConfigMap, Element, Props
|
|
4
10
|
|
|
5
11
|
|
|
6
12
|
def render_children(props: Props) -> Element:
|
|
7
|
-
"""
|
|
8
|
-
Renders the children of a component without any specific
|
|
9
|
-
markup for the component itself.
|
|
10
|
-
"""
|
|
13
|
+
"""Return the children of a component without wrapping markup."""
|
|
11
14
|
return props["children"]
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
def code_block(props: Props) -> Element:
|
|
18
|
+
"""Render a code block inside a ``pre`` > ``code`` element tree."""
|
|
15
19
|
return DOM.create_element(
|
|
16
20
|
"pre", {}, DOM.create_element("code", {}, props["children"])
|
|
17
21
|
)
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
# Default block map to extend.
|
|
21
|
-
BLOCK_MAP = {
|
|
25
|
+
BLOCK_MAP: ConfigMap = {
|
|
22
26
|
BLOCK_TYPES.UNSTYLED: "p",
|
|
23
27
|
BLOCK_TYPES.HEADER_ONE: "h1",
|
|
24
28
|
BLOCK_TYPES.HEADER_TWO: "h2",
|
|
@@ -33,12 +37,14 @@ BLOCK_MAP = {
|
|
|
33
37
|
BLOCK_TYPES.CODE: code_block,
|
|
34
38
|
BLOCK_TYPES.ATOMIC: render_children,
|
|
35
39
|
}
|
|
40
|
+
"""Default mapping from block types to renderable tags or components."""
|
|
41
|
+
|
|
36
42
|
|
|
37
43
|
# Default style map to extend.
|
|
38
44
|
# Tags come from https://developer.mozilla.org/en-US/docs/Web/HTML/Element.
|
|
39
45
|
# and are loosely aligned with https://github.com/jpuri/draftjs-to-html.
|
|
40
46
|
# Only styles that map to HTML elements are allowed as defaults.
|
|
41
|
-
STYLE_MAP = {
|
|
47
|
+
STYLE_MAP: ConfigMap = {
|
|
42
48
|
INLINE_STYLES.BOLD: "strong",
|
|
43
49
|
INLINE_STYLES.CODE: "code",
|
|
44
50
|
INLINE_STYLES.ITALIC: "em",
|
|
@@ -54,3 +60,4 @@ STYLE_MAP = {
|
|
|
54
60
|
INLINE_STYLES.DELETE: "del",
|
|
55
61
|
INLINE_STYLES.KEYBOARD: "kbd",
|
|
56
62
|
}
|
|
63
|
+
"""Default mapping from inline styles to HTML tags."""
|