draftjs_exporter 5.2.0__tar.gz → 7.1.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.
Files changed (61) hide show
  1. draftjs_exporter-7.1.0/PKG-INFO +75 -0
  2. draftjs_exporter-7.1.0/README.md +38 -0
  3. draftjs_exporter-7.1.0/draftjs_exporter/__init__.py +178 -0
  4. draftjs_exporter-7.1.0/draftjs_exporter/command.py +69 -0
  5. draftjs_exporter-7.1.0/draftjs_exporter/composite_decorators.py +137 -0
  6. {draftjs_exporter-5.2.0 → draftjs_exporter-7.1.0}/draftjs_exporter/constants.py +29 -0
  7. draftjs_exporter-7.1.0/draftjs_exporter/contentstate_filter/__init__.py +267 -0
  8. {draftjs_exporter-5.2.0 → draftjs_exporter-7.1.0}/draftjs_exporter/defaults.py +14 -7
  9. draftjs_exporter-7.1.0/draftjs_exporter/dom.py +183 -0
  10. draftjs_exporter-7.1.0/draftjs_exporter/engines/__init__.py +1 -0
  11. draftjs_exporter-7.1.0/draftjs_exporter/engines/base.py +79 -0
  12. draftjs_exporter-7.1.0/draftjs_exporter/engines/html5lib.py +89 -0
  13. draftjs_exporter-7.1.0/draftjs_exporter/engines/lxml.py +108 -0
  14. draftjs_exporter-7.1.0/draftjs_exporter/engines/markdown.py +244 -0
  15. draftjs_exporter-7.1.0/draftjs_exporter/engines/string.py +188 -0
  16. {draftjs_exporter-5.2.0 → draftjs_exporter-7.1.0}/draftjs_exporter/engines/string_compat.py +38 -7
  17. draftjs_exporter-7.1.0/draftjs_exporter/entity_state.py +155 -0
  18. draftjs_exporter-7.1.0/draftjs_exporter/error.py +30 -0
  19. draftjs_exporter-7.1.0/draftjs_exporter/html.py +263 -0
  20. draftjs_exporter-7.1.0/draftjs_exporter/markdown/__init__.py +222 -0
  21. draftjs_exporter-7.1.0/draftjs_exporter/markdown/blocks.py +66 -0
  22. draftjs_exporter-7.1.0/draftjs_exporter/markdown/code.py +41 -0
  23. draftjs_exporter-7.1.0/draftjs_exporter/markdown/entities.py +64 -0
  24. draftjs_exporter-7.1.0/draftjs_exporter/markdown/escape.py +209 -0
  25. draftjs_exporter-7.1.0/draftjs_exporter/markdown/fallbacks.py +50 -0
  26. draftjs_exporter-7.1.0/draftjs_exporter/markdown/helpers.py +62 -0
  27. draftjs_exporter-7.1.0/draftjs_exporter/markdown/lists.py +114 -0
  28. draftjs_exporter-7.1.0/draftjs_exporter/markdown/styles.py +34 -0
  29. draftjs_exporter-7.1.0/draftjs_exporter/markdown_importer/__init__.py +63 -0
  30. draftjs_exporter-7.1.0/draftjs_exporter/markdown_parser/__init__.py +129 -0
  31. draftjs_exporter-7.1.0/draftjs_exporter/markdown_parser/blocks.py +346 -0
  32. draftjs_exporter-7.1.0/draftjs_exporter/markdown_parser/builder.py +88 -0
  33. draftjs_exporter-7.1.0/draftjs_exporter/markdown_parser/inline.py +526 -0
  34. draftjs_exporter-7.1.0/draftjs_exporter/markdown_parser/resolvers.py +129 -0
  35. draftjs_exporter-7.1.0/draftjs_exporter/options.py +185 -0
  36. draftjs_exporter-7.1.0/draftjs_exporter/style_state.py +143 -0
  37. draftjs_exporter-7.1.0/draftjs_exporter/types.py +111 -0
  38. draftjs_exporter-7.1.0/draftjs_exporter/utils/__init__.py +1 -0
  39. {draftjs_exporter-5.2.0 → draftjs_exporter-7.1.0}/draftjs_exporter/utils/module_loading.py +16 -7
  40. {draftjs_exporter-5.2.0 → draftjs_exporter-7.1.0}/draftjs_exporter/wrapper_state.py +115 -30
  41. {draftjs_exporter-5.2.0 → draftjs_exporter-7.1.0}/pyproject.toml +60 -19
  42. draftjs_exporter-5.2.0/LICENSE +0 -9
  43. draftjs_exporter-5.2.0/PKG-INFO +0 -376
  44. draftjs_exporter-5.2.0/README.md +0 -330
  45. draftjs_exporter-5.2.0/draftjs_exporter/__init__.py +0 -5
  46. draftjs_exporter-5.2.0/draftjs_exporter/command.py +0 -56
  47. draftjs_exporter-5.2.0/draftjs_exporter/composite_decorators.py +0 -93
  48. draftjs_exporter-5.2.0/draftjs_exporter/dom.py +0 -129
  49. draftjs_exporter-5.2.0/draftjs_exporter/engines/__init__.py +0 -0
  50. draftjs_exporter-5.2.0/draftjs_exporter/engines/base.py +0 -50
  51. draftjs_exporter-5.2.0/draftjs_exporter/engines/html5lib.py +0 -45
  52. draftjs_exporter-5.2.0/draftjs_exporter/engines/lxml.py +0 -49
  53. draftjs_exporter-5.2.0/draftjs_exporter/engines/string.py +0 -116
  54. draftjs_exporter-5.2.0/draftjs_exporter/entity_state.py +0 -108
  55. draftjs_exporter-5.2.0/draftjs_exporter/error.py +0 -6
  56. draftjs_exporter-5.2.0/draftjs_exporter/html.py +0 -180
  57. draftjs_exporter-5.2.0/draftjs_exporter/options.py +0 -105
  58. draftjs_exporter-5.2.0/draftjs_exporter/style_state.py +0 -49
  59. draftjs_exporter-5.2.0/draftjs_exporter/types.py +0 -35
  60. draftjs_exporter-5.2.0/draftjs_exporter/utils/__init__.py +0 -0
  61. {draftjs_exporter-5.2.0 → draftjs_exporter-7.1.0}/draftjs_exporter/py.typed +0 -0
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: draftjs_exporter
3
+ Version: 7.1.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,178 @@
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__ = "7.1.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.contentstate_filter import (
19
+ ContentStateFilter as ContentStateFilter,
20
+ )
21
+ from draftjs_exporter.contentstate_filter import FilterRule as FilterRule
22
+ from draftjs_exporter.defaults import BLOCK_MAP as BLOCK_MAP
23
+ from draftjs_exporter.defaults import STYLE_MAP as STYLE_MAP
24
+ from draftjs_exporter.defaults import code_block as code_block
25
+ from draftjs_exporter.defaults import render_children as render_children
26
+ from draftjs_exporter.dom import DOM as DOM
27
+ from draftjs_exporter.error import MarkdownParseError as MarkdownParseError
28
+ from draftjs_exporter.html import HTML as HTML
29
+ from draftjs_exporter.html import ExporterConfig as ExporterConfig
30
+ from draftjs_exporter.markdown import CONFIG as MARKDOWN_CONFIG
31
+ from draftjs_exporter.markdown import MarkdownOptions as MarkdownOptions
32
+ from draftjs_exporter.markdown import build_markdown_config as build_markdown_config
33
+ from draftjs_exporter.markdown.blocks import md_list_wrapper as md_list_wrapper
34
+ from draftjs_exporter.markdown.blocks import md_make_ol as md_make_ol
35
+ from draftjs_exporter.markdown.blocks import md_make_ul as md_make_ul
36
+ from draftjs_exporter.markdown.blocks import md_ol as md_ol
37
+ from draftjs_exporter.markdown.blocks import md_prefixed_block as md_prefixed_block
38
+ from draftjs_exporter.markdown.blocks import md_ul as md_ul
39
+ from draftjs_exporter.markdown.code import md_code_element as md_code_element
40
+ from draftjs_exporter.markdown.code import md_code_wrapper as md_code_wrapper
41
+ from draftjs_exporter.markdown.code import md_make_code_element as md_make_code_element
42
+ from draftjs_exporter.markdown.code import md_make_code_wrapper as md_make_code_wrapper
43
+ from draftjs_exporter.markdown.entities import md_horizontal_rule as md_horizontal_rule
44
+ from draftjs_exporter.markdown.entities import md_image as md_image
45
+ from draftjs_exporter.markdown.entities import md_link as md_link
46
+ from draftjs_exporter.markdown.entities import (
47
+ md_make_horizontal_rule as md_make_horizontal_rule,
48
+ )
49
+ from draftjs_exporter.markdown.fallbacks import md_block_fallback as md_block_fallback
50
+ from draftjs_exporter.markdown.fallbacks import md_entity_fallback as md_entity_fallback
51
+ from draftjs_exporter.markdown.fallbacks import md_style_fallback as md_style_fallback
52
+ from draftjs_exporter.markdown.helpers import md_block as md_block
53
+ from draftjs_exporter.markdown.helpers import md_inline as md_inline
54
+ from draftjs_exporter.markdown.helpers import md_link_destination as md_link_destination
55
+ from draftjs_exporter.markdown.helpers import md_mark_safe as md_mark_safe
56
+ from draftjs_exporter.markdown.styles import md_code_span as md_code_span
57
+ from draftjs_exporter.markdown.styles import md_inline_style as md_inline_style
58
+ from draftjs_exporter.markdown_importer import ImporterConfig as ImporterConfig
59
+ from draftjs_exporter.markdown_importer import MarkdownImporter as MarkdownImporter
60
+ from draftjs_exporter.markdown_parser import EntityResolution as EntityResolution
61
+ from draftjs_exporter.markdown_parser import EntityResolver as EntityResolver
62
+ from draftjs_exporter.markdown_parser import MarkdownParser as MarkdownParser
63
+ from draftjs_exporter.markdown_parser import ParserConfig as ParserConfig
64
+ from draftjs_exporter.markdown_parser import scheme_resolver as scheme_resolver
65
+ from draftjs_exporter.types import Block as Block
66
+ from draftjs_exporter.types import Component as Component
67
+ from draftjs_exporter.types import CompositeDecorators as CompositeDecorators
68
+ from draftjs_exporter.types import ConfigMap as ConfigMap
69
+ from draftjs_exporter.types import ContentState as ContentState
70
+ from draftjs_exporter.types import Decorator as Decorator
71
+ from draftjs_exporter.types import Element as Element
72
+ from draftjs_exporter.types import Entity as Entity
73
+ from draftjs_exporter.types import EntityKey as EntityKey
74
+ from draftjs_exporter.types import EntityMap as EntityMap
75
+ from draftjs_exporter.types import EntityRange as EntityRange
76
+ from draftjs_exporter.types import InlineStyleRange as InlineStyleRange
77
+ from draftjs_exporter.types import Mutability as Mutability
78
+ from draftjs_exporter.types import Props as Props
79
+ from draftjs_exporter.types import RenderableConfig as RenderableConfig
80
+ from draftjs_exporter.types import RenderableType as RenderableType
81
+ from draftjs_exporter.types import Tag as Tag
82
+
83
+ __all__ = [
84
+ # Metadata
85
+ "__title__",
86
+ "__version__",
87
+ "__uri__",
88
+ "__author__",
89
+ "__license__",
90
+ "__copyright__",
91
+ # Core
92
+ "Exporter",
93
+ "HTML",
94
+ "HTMLExporter",
95
+ "ExporterConfig",
96
+ "ContentState",
97
+ "DOM",
98
+ # Configs
99
+ "HTML_CONFIG",
100
+ "MARKDOWN_CONFIG",
101
+ "MarkdownOptions",
102
+ "build_markdown_config",
103
+ # Markdown helpers
104
+ "md_block",
105
+ "md_block_fallback",
106
+ "md_code_element",
107
+ "md_code_span",
108
+ "md_code_wrapper",
109
+ "md_entity_fallback",
110
+ "md_horizontal_rule",
111
+ "md_image",
112
+ "md_inline",
113
+ "md_inline_style",
114
+ "md_link",
115
+ "md_link_destination",
116
+ "md_list_wrapper",
117
+ "md_make_code_element",
118
+ "md_make_code_wrapper",
119
+ "md_make_horizontal_rule",
120
+ "md_make_ol",
121
+ "md_make_ul",
122
+ "md_mark_safe",
123
+ "md_ol",
124
+ "md_prefixed_block",
125
+ "md_style_fallback",
126
+ "md_ul",
127
+ # Importer
128
+ "MarkdownImporter",
129
+ "ImporterConfig",
130
+ "MarkdownParser",
131
+ "ParserConfig",
132
+ "ContentStateFilter",
133
+ "FilterRule",
134
+ "MarkdownParseError",
135
+ "EntityResolution",
136
+ "EntityResolver",
137
+ "scheme_resolver",
138
+ # Constants
139
+ "BLOCK_TYPES",
140
+ "ENTITY_TYPES",
141
+ "INLINE_STYLES",
142
+ # Defaults
143
+ "BLOCK_MAP",
144
+ "STYLE_MAP",
145
+ "code_block",
146
+ "render_children",
147
+ # Types
148
+ "Block",
149
+ "Component",
150
+ "CompositeDecorators",
151
+ "ConfigMap",
152
+ "ContentState",
153
+ "Decorator",
154
+ "Element",
155
+ "Entity",
156
+ "EntityKey",
157
+ "EntityMap",
158
+ "EntityRange",
159
+ "InlineStyleRange",
160
+ "Mutability",
161
+ "Props",
162
+ "RenderableConfig",
163
+ "RenderableType",
164
+ "Tag",
165
+ ]
166
+
167
+ Exporter = HTML
168
+ """Compatibility alias for the HTML exporter."""
169
+
170
+ HTMLExporter = HTML
171
+ """Compatibility alias for the HTML exporter."""
172
+
173
+ HTML_CONFIG: ExporterConfig = {
174
+ "block_map": BLOCK_MAP,
175
+ "style_map": STYLE_MAP,
176
+ "engine": DOM.STRING,
177
+ }
178
+ """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: list[tuple[int, int, Any, Decorator]] = []
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."""