draftail-text-utils 0.3.0__tar.gz → 3.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.
Files changed (51) hide show
  1. draftail_text_utils-3.0.0/LICENSE +11 -0
  2. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/PKG-INFO +20 -22
  3. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/README.md +5 -5
  4. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/pyproject.toml +35 -28
  5. draftail_text_utils-3.0.0/pyproject.toml.orig +71 -0
  6. draftail_text_utils-3.0.0/src/draftail_text_utils/__init__.py +1 -0
  7. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/conf.py +25 -5
  8. draftail_text_utils-3.0.0/src/draftail_text_utils/rich_text/dynamic_link.py +42 -0
  9. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/rich_text/font_family.py +6 -5
  10. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/rich_text/text_style.py +129 -6
  11. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/css/color.css +15 -6
  12. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/css/common.css +2 -1
  13. draftail_text_utils-3.0.0/src/draftail_text_utils/static/draftail_text_utils/css/dynamic_link.css +79 -0
  14. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/css/font_size.css +6 -8
  15. draftail_text_utils-3.0.0/src/draftail_text_utils/static/draftail_text_utils/css/page.css +17 -0
  16. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/css/text_alignment.css +1 -1
  17. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/js/common.js +26 -4
  18. draftail_text_utils-3.0.0/src/draftail_text_utils/static/draftail_text_utils/js/dynamic_link.js +249 -0
  19. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/js/font_size.js +3 -3
  20. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/js/highlight_color.js +1 -0
  21. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/js/styled_link_source.js +6 -0
  22. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/js/text_color.js +1 -0
  23. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/js/text_style_entity.js +10 -1
  24. draftail_text_utils-3.0.0/src/draftail_text_utils/templatetags/draftail_text_utils_tags.py +28 -0
  25. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/wagtail_hooks.py +38 -28
  26. draftail_text_utils-0.3.0/src/draftail_text_utils/__init__.py +0 -4
  27. draftail_text_utils-0.3.0/src/draftail_text_utils/static/draftail_text_utils/css/page.css +0 -5
  28. draftail_text_utils-0.3.0/src/draftail_text_utils/templatetags/draftail_text_utils_tags.py +0 -27
  29. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/apps.py +0 -0
  30. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/migrations/__init__.py +0 -0
  31. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/rich_text/__init__.py +0 -0
  32. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/rich_text/base.py +0 -0
  33. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/rich_text/font_size.py +0 -0
  34. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/rich_text/highlight_color.py +0 -0
  35. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/rich_text/text_alignment.py +0 -0
  36. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/rich_text/text_color.py +0 -0
  37. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/css/font_family.css +0 -0
  38. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/css/highlight_color.css +0 -0
  39. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/css/text_color.css +0 -0
  40. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/js/font_family.js +0 -0
  41. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/static/draftail_text_utils/js/text_alignment.js +0 -0
  42. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templates/draftail_text_utils/icons/align-center.svg +0 -0
  43. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templates/draftail_text_utils/icons/align-justify.svg +0 -0
  44. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templates/draftail_text_utils/icons/align-left.svg +0 -0
  45. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templates/draftail_text_utils/icons/align-right.svg +0 -0
  46. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templates/draftail_text_utils/icons/anchor.svg +0 -0
  47. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templates/draftail_text_utils/icons/font.svg +0 -0
  48. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templates/draftail_text_utils/icons/highlighter.svg +0 -0
  49. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templates/draftail_text_utils/icons/palette.svg +0 -0
  50. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templates/draftail_text_utils/icons/text-height.svg +0 -0
  51. {draftail_text_utils-0.3.0 → draftail_text_utils-3.0.0}/src/draftail_text_utils/templatetags/__init__.py +0 -0
@@ -0,0 +1,11 @@
1
+ Copyright 2026 baldwinboy
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
+
5
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
+
7
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
+
9
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10
+
11
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -1,34 +1,32 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: draftail-text-utils
3
- Version: 0.3.0
3
+ Version: 3.0.0
4
4
  Summary: Extends Draftail's text customization
5
- Keywords: wagtail,draftail,rich text,text color,highlight,font size,font family,text alignment
5
+ Keywords: draftail,font family,font size,highlight,rich text,text alignment,text color,wagtail
6
6
  Author: baldwinboy
7
7
  License-Expression: BSD-3-Clause
8
+ License-File: LICENSE
8
9
  Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Framework :: Django
11
+ Classifier: Framework :: Django :: 6.0
12
+ Classifier: Framework :: Wagtail
13
+ Classifier: Framework :: Wagtail :: 8
9
14
  Classifier: Intended Audience :: Developers
10
15
  Classifier: Operating System :: OS Independent
11
16
  Classifier: Programming Language :: Python
12
17
  Classifier: Programming Language :: Python :: 3
13
18
  Classifier: Programming Language :: Python :: 3.13
14
19
  Classifier: Programming Language :: Python :: 3.14
15
- Classifier: Framework :: Django
16
- Classifier: Framework :: Django :: 5.0
17
- Classifier: Framework :: Django :: 5.1
18
- Classifier: Framework :: Django :: 6.0
19
- Classifier: Framework :: Wagtail
20
- Classifier: Framework :: Wagtail :: 6
21
- Classifier: Framework :: Wagtail :: 7
22
- Requires-Dist: cssutils>=2.15.0
23
- Requires-Dist: django>=5.0
24
- Requires-Dist: wagtail>=7.3.2
20
+ Requires-Dist: django>=6.0,<6.1
21
+ Requires-Dist: wagtail>=8.0,<8.1
22
+ Requires-Dist: cssutils>=2.15.0,<3
25
23
  Requires-Dist: wagtail-traverse>=1.0.1
26
24
  Requires-Python: >=3.13
27
- Project-URL: Homepage, https://github.com/baldwinboy/draftail-text-utils
28
- Project-URL: Repository, https://github.com/baldwinboy/draftail-text-utils
29
- Project-URL: Changelog, https://github.com/baldwinboy/draftail-text-utils/blob/main/CHANGELOG.md
30
- Project-URL: Documentation, https://github.com/baldwinboy/draftail-text-utils/tree/main/docs
31
- Project-URL: Issues, https://github.com/baldwinboy/draftail-text-utils/issues
25
+ Project-URL: Changelog, https://github.com/baldwinboy/draftail_text_utils/blob/main/CHANGELOG.md
26
+ Project-URL: Documentation, https://github.com/baldwinboy/draftail_text_utils/tree/main/docs
27
+ Project-URL: Homepage, https://github.com/baldwinboy/draftail_text_utils
28
+ Project-URL: Issues, https://github.com/baldwinboy/draftail_text_utils/issues
29
+ Project-URL: Repository, https://github.com/baldwinboy/draftail_text_utils
32
30
  Description-Content-Type: text/markdown
33
31
 
34
32
  # Draftail Text Utils
@@ -43,11 +41,11 @@ Extends Draftail's text customization. Includes:
43
41
 
44
42
  ## Links
45
43
 
46
- - [Documentation](https://github.com/baldwinboy/draftail-text-utils/blob/main/README.md)
47
- - [Changelog](https://github.com/baldwinboy/draftail-text-utils/blob/main/CHANGELOG.md)
48
- - [Contributing](https://github.com/baldwinboy/draftail-text-utils/blob/main/CONTRIBUTING.md)
49
- - [Discussions](https://github.com/baldwinboy/draftail-text-utils/discussions)
50
- - [Security](https://github.com/baldwinboy/draftail-text-utils/security)
44
+ - [Documentation](https://github.com/baldwinboy/draftail_text_utils/blob/main/docs/README.md)
45
+ - [Changelog](https://github.com/baldwinboy/draftail_text_utils/blob/main/CHANGELOG.md)
46
+ - [Contributing](https://github.com/baldwinboy/draftail_text_utils/blob/main/CONTRIBUTING.md)
47
+ - [Discussions](https://github.com/baldwinboy/draftail_text_utils/discussions)
48
+ - [Security](https://github.com/baldwinboy/draftail_text_utils/security)
51
49
 
52
50
  ## Supported versions
53
51
 
@@ -10,11 +10,11 @@ Extends Draftail's text customization. Includes:
10
10
 
11
11
  ## Links
12
12
 
13
- - [Documentation](https://github.com/baldwinboy/draftail-text-utils/blob/main/README.md)
14
- - [Changelog](https://github.com/baldwinboy/draftail-text-utils/blob/main/CHANGELOG.md)
15
- - [Contributing](https://github.com/baldwinboy/draftail-text-utils/blob/main/CONTRIBUTING.md)
16
- - [Discussions](https://github.com/baldwinboy/draftail-text-utils/discussions)
17
- - [Security](https://github.com/baldwinboy/draftail-text-utils/security)
13
+ - [Documentation](https://github.com/baldwinboy/draftail_text_utils/blob/main/docs/README.md)
14
+ - [Changelog](https://github.com/baldwinboy/draftail_text_utils/blob/main/CHANGELOG.md)
15
+ - [Contributing](https://github.com/baldwinboy/draftail_text_utils/blob/main/CONTRIBUTING.md)
16
+ - [Discussions](https://github.com/baldwinboy/draftail_text_utils/discussions)
17
+ - [Security](https://github.com/baldwinboy/draftail_text_utils/security)
18
18
 
19
19
  ## Supported versions
20
20
 
@@ -1,57 +1,64 @@
1
1
  [project]
2
2
  name = "draftail-text-utils"
3
- version = "0.3.0"
3
+ version = "3.0.0"
4
4
  description = "Extends Draftail's text customization"
5
5
  readme = "README.md"
6
- license = "BSD-3-Clause"
7
6
  requires-python = ">=3.13"
8
- authors = [
9
- { name = "baldwinboy" },
7
+ license = "BSD-3-Clause"
8
+ license-files = ["LICENSE"]
9
+ keywords = [
10
+ "draftail",
11
+ "font family",
12
+ "font size",
13
+ "highlight",
14
+ "rich text",
15
+ "text alignment",
16
+ "text color",
17
+ "wagtail",
10
18
  ]
11
- keywords = ["wagtail", "draftail", "rich text", "text color", "highlight", "font size", "font family", "text alignment"]
12
19
  classifiers = [
13
20
  "Development Status :: 3 - Alpha",
21
+ "Framework :: Django",
22
+ "Framework :: Django :: 6.0",
23
+ "Framework :: Wagtail",
24
+ "Framework :: Wagtail :: 8",
14
25
  "Intended Audience :: Developers",
15
26
  "Operating System :: OS Independent",
16
27
  "Programming Language :: Python",
17
28
  "Programming Language :: Python :: 3",
18
29
  "Programming Language :: Python :: 3.13",
19
30
  "Programming Language :: Python :: 3.14",
20
- "Framework :: Django",
21
- "Framework :: Django :: 5.0",
22
- "Framework :: Django :: 5.1",
23
- "Framework :: Django :: 6.0",
24
- "Framework :: Wagtail",
25
- "Framework :: Wagtail :: 6",
26
- "Framework :: Wagtail :: 7",
27
31
  ]
28
32
  dependencies = [
29
- "cssutils>=2.15.0",
30
- "Django>=5.0",
31
- "Wagtail>=7.3.2",
33
+ "Django>=6.0,<6.1",
34
+ "Wagtail>=8.0,<8.1",
35
+ "cssutils>=2.15.0,<3",
32
36
  "wagtail-traverse>=1.0.1",
33
37
  ]
34
38
 
39
+ [[project.authors]]
40
+ name = "baldwinboy"
41
+
35
42
  [project.urls]
36
- Homepage = "https://github.com/baldwinboy/draftail-text-utils"
37
- Repository = "https://github.com/baldwinboy/draftail-text-utils"
38
- Changelog = "https://github.com/baldwinboy/draftail-text-utils/blob/main/CHANGELOG.md"
39
- Documentation = "https://github.com/baldwinboy/draftail-text-utils/tree/main/docs"
40
- Issues = "https://github.com/baldwinboy/draftail-text-utils/issues"
43
+ Changelog = "https://github.com/baldwinboy/draftail_text_utils/blob/main/CHANGELOG.md"
44
+ Documentation = "https://github.com/baldwinboy/draftail_text_utils/tree/main/docs"
45
+ Homepage = "https://github.com/baldwinboy/draftail_text_utils"
46
+ Issues = "https://github.com/baldwinboy/draftail_text_utils/issues"
47
+ Repository = "https://github.com/baldwinboy/draftail_text_utils"
41
48
 
42
49
  [dependency-groups]
43
50
  dev = [
44
- "dj-database-url==2.1.0",
45
- "pre-commit==3.4.0",
46
- "prek>=0.4.1",
47
- "pytest==8.1.1",
48
- "pytest-cov==5.0.0",
49
- "pytest-django==4.8.0",
50
- "ruff>=0.15.9",
51
+ "dj-database-url==3.1.2",
52
+ "pre-commit==4.6.2",
53
+ "prek==0.5.5",
54
+ "pytest==9.1.1",
55
+ "pytest-cov==7.1.0",
56
+ "pytest-django==4.14.0",
57
+ "ruff==0.16.10",
51
58
  ]
52
59
 
53
60
  [build-system]
54
- requires = ["uv_build>=0.11.1,<0.12"]
61
+ requires = ["uv_build>=0.12.23,<0.13"]
55
62
  build-backend = "uv_build"
56
63
 
57
64
  [tool.uv.build-backend]
@@ -0,0 +1,71 @@
1
+ [project]
2
+ name = "draftail-text-utils"
3
+ version = "3.0.0"
4
+ description = "Extends Draftail's text customization"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ license = "BSD-3-Clause"
8
+ license-files = ["LICENSE"]
9
+ authors = [
10
+ { name = "baldwinboy" },
11
+ ]
12
+ keywords = ["draftail", "font family", "font size", "highlight", "rich text", "text alignment", "text color", "wagtail"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Framework :: Django",
16
+ "Framework :: Django :: 6.0",
17
+ "Framework :: Wagtail",
18
+ "Framework :: Wagtail :: 8",
19
+ "Intended Audience :: Developers",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ ]
26
+ dependencies = [
27
+ "Django>=6.0,<6.1",
28
+ "Wagtail>=8.0,<8.1",
29
+ "cssutils>=2.15.0,<3",
30
+ "wagtail-traverse>=1.0.1",
31
+ ]
32
+
33
+ [project.urls]
34
+ Changelog = "https://github.com/baldwinboy/draftail_text_utils/blob/main/CHANGELOG.md"
35
+ Documentation = "https://github.com/baldwinboy/draftail_text_utils/tree/main/docs"
36
+ Homepage = "https://github.com/baldwinboy/draftail_text_utils"
37
+ Issues = "https://github.com/baldwinboy/draftail_text_utils/issues"
38
+ Repository = "https://github.com/baldwinboy/draftail_text_utils"
39
+
40
+ [dependency-groups]
41
+ dev = [
42
+ "dj-database-url==3.1.2",
43
+ "pre-commit==4.6.2",
44
+ "prek==0.5.5",
45
+ "pytest==9.1.1",
46
+ "pytest-cov==7.1.0",
47
+ "pytest-django==4.14.0",
48
+ "ruff==0.16.10",
49
+ ]
50
+
51
+ [build-system]
52
+ requires = ["uv_build>=0.12.23,<0.13"]
53
+ build-backend = "uv_build"
54
+
55
+ [tool.uv.build-backend]
56
+ module-name = "draftail_text_utils"
57
+ source-exclude = [
58
+ "draftail_text_utils/static_src",
59
+ "draftail_text_utils/test",
60
+ "testmanage.py",
61
+ ".*",
62
+ "*.ini",
63
+ "*.yml",
64
+ ]
65
+
66
+ [tool.uv.build-backend.package-data]
67
+ "draftail_text_utils/static/**" = ["*"]
68
+ "draftail_text_utils/templates/**" = ["*"]
69
+
70
+ [tool.pytest.ini_options]
71
+ DJANGO_SETTINGS_MODULE = "draftail_text_utils.test.settings"
@@ -0,0 +1 @@
1
+ __version__ = "3.0.0"
@@ -42,12 +42,14 @@ Usage in your Django settings:
42
42
  "FONT_FAMILY": True,
43
43
  "FONT_SIZE": True,
44
44
  "TEXT_ALIGNMENT": True,
45
+ "DYNAMIC_LINK": False,
45
46
  },
46
47
  }
47
48
  """
48
49
 
49
50
  import importlib
50
51
  import logging
52
+ import re
51
53
 
52
54
  from django.conf import settings
53
55
  from wagtailtraverse import traverse_value
@@ -56,6 +58,19 @@ from wagtailtraverse import traverse_value
56
58
  logger = logging.getLogger(__name__)
57
59
 
58
60
 
61
+ def font_family_type_id(label):
62
+ """
63
+ Return the stable Draftail type identifier for a font-family ``label``.
64
+
65
+ Shared by feature registration and admin data injection so the toolbar
66
+ option always matches the registered inline style, regardless of the
67
+ characters in the label (spaces, hyphens, parentheses, etc.). Labels may be
68
+ lazy translation proxies, so they are coerced with ``str`` first.
69
+ """
70
+ slug = re.sub(r"[^0-9A-Za-z]+", "_", str(label)).strip("_").upper()
71
+ return f"FONT_FAMILY_{slug}"
72
+
73
+
59
74
  DEFAULT_FONT_SIZES = {
60
75
  "MIN": 8,
61
76
  "MAX": 100,
@@ -85,6 +100,9 @@ DEFAULT_FEATURES = {
85
100
  "FONT_FAMILY": True,
86
101
  "FONT_SIZE": True,
87
102
  "TEXT_ALIGNMENT": True,
103
+ # Opt-in: the host project must resolve the stored expression at render
104
+ # time (DaisIE does this in ``daisie_richtext``).
105
+ "DYNAMIC_LINK": False,
88
106
  }
89
107
 
90
108
 
@@ -173,8 +191,9 @@ def _extract_struct_from_field(field_value):
173
191
 
174
192
  def _find_design_settings_model(module):
175
193
  """Find a SiteDesignSettings-style model class in a module."""
176
- for attr_name in dir(module):
177
- attr = getattr(module, attr_name)
194
+ # Use vars() rather than getattr() so module descriptors/properties are not
195
+ # triggered during discovery.
196
+ for attr in vars(module).values():
178
197
  if isinstance(attr, type) and hasattr(attr, "_meta"):
179
198
  try:
180
199
  if attr._meta.label and "SiteDesignSettings" in attr.__name__:
@@ -186,8 +205,7 @@ def _find_design_settings_model(module):
186
205
 
187
206
  def _find_typography_block(module):
188
207
  """Find a TypographyBlock / TypographyStreamBlock class in a module."""
189
- for attr_name in dir(module):
190
- attr = getattr(module, attr_name)
208
+ for attr in vars(module).values():
191
209
  if isinstance(attr, type) and "Typography" in getattr(attr, "__name__", ""):
192
210
  return attr
193
211
  return None
@@ -208,7 +226,9 @@ def _normalise_font_urls(font_urls):
208
226
 
209
227
 
210
228
  def load_color_palette():
211
- colors_config = get_setting("COLORS", {})
229
+ colors_config = get_setting("COLORS", {}) or {}
230
+ if not isinstance(colors_config, dict):
231
+ colors_config = {}
212
232
 
213
233
  callable_path = colors_config.get("CALLABLE")
214
234
  if callable_path:
@@ -0,0 +1,42 @@
1
+ """
2
+ Draftail rich-text feature: Context-bound (dynamic) link control.
3
+
4
+ Registers an opt-in ``"dynamic-link"`` ControlFeature that lets an author
5
+ attach a context expression (e.g. ``{{ user.url }}``) to the selected text.
6
+ The expression is stored on the unified ``TEXT_STYLE`` entity; the host
7
+ project resolves it at render time.
8
+ """
9
+
10
+ from draftail_text_utils.conf import feature_enabled
11
+
12
+ from .base import (
13
+ control_json_script,
14
+ register_control_feature,
15
+ )
16
+
17
+
18
+ def register(features):
19
+ if not feature_enabled("DYNAMIC_LINK"):
20
+ return
21
+
22
+ register_control_feature(
23
+ features,
24
+ feature_name="dynamic-link",
25
+ icon="anchor",
26
+ label="Dynamic Link",
27
+ description="Link to a context value, e.g. {{ user.url }}",
28
+ js=["draftail_text_utils/js/dynamic_link.js"],
29
+ css={"all": ("draftail_text_utils/css/dynamic_link.css",)},
30
+ )
31
+
32
+
33
+ def control():
34
+ if not feature_enabled("DYNAMIC_LINK"):
35
+ return
36
+
37
+ return control_json_script(
38
+ type_="dynamic-link",
39
+ icon="anchor",
40
+ label="Dynamic Link",
41
+ description="Link to a context value, e.g. {{ user.url }}",
42
+ )
@@ -6,9 +6,11 @@ Registers:
6
6
  - A ``"font-family"`` ControlFeature for the toolbar dropdown.
7
7
  """
8
8
 
9
- import re
10
-
11
- from draftail_text_utils.conf import feature_enabled, load_font_families
9
+ from draftail_text_utils.conf import (
10
+ feature_enabled,
11
+ font_family_type_id,
12
+ load_font_families,
13
+ )
12
14
 
13
15
  from .base import (
14
16
  control_json_script,
@@ -28,8 +30,7 @@ def register(features):
28
30
 
29
31
  for font in font_families:
30
32
  font_family = font["value"]
31
- label = re.sub("[^0-9a-zA-Z]+", "_", font["label"]).upper()
32
- type_ = font.get("type", f"FONT_FAMILY_{label}")
33
+ type_ = font.get("type") or font_family_type_id(font["label"])
33
34
 
34
35
  register_inline_style_feature(
35
36
  features,
@@ -1,5 +1,6 @@
1
1
  import cssutils
2
2
 
3
+ from django.core.exceptions import ObjectDoesNotExist
3
4
  from django.urls import reverse_lazy
4
5
  from django.utils.html import escape
5
6
  from draftjs_exporter.dom import DOM
@@ -18,10 +19,6 @@ from wagtail.rich_text import LinkHandler
18
19
  from wagtail.whitelist import check_url
19
20
 
20
21
 
21
- STYLE_KEYS = {"color", "backgroundColor", "size"}
22
- LINK_KEYS = {"url", "id", "parentId"}
23
-
24
-
25
22
  class TextStyleEntityHandler(InlineEntityElementHandler):
26
23
  """
27
24
  Database HTML to Draft.js ContentState.
@@ -130,14 +127,39 @@ class StyledLinkElementHandler(InlineEntityElementHandler):
130
127
  data["id"] = int(page_id)
131
128
  data["url"] = None
132
129
  data["parentId"] = None
130
+ elif linktype == "styled-document":
131
+ data["document_id"] = self._parse_id(attrs.get("id"))
132
+ self._add_document_url(data, data["document_id"])
133
+ elif linktype == "dynamic":
134
+ expression = attrs.get("data-dynamic") or attrs.get("dynamic") or ""
135
+ if expression:
136
+ data["dynamic"] = expression
133
137
  elif href:
134
138
  data["url"] = href
135
139
 
136
140
  return data
137
141
 
142
+ @staticmethod
143
+ def _parse_id(value):
144
+ try:
145
+ return int(value)
146
+ except (TypeError, ValueError):
147
+ return None
148
+
149
+ @staticmethod
150
+ def _add_document_url(data, document_id):
151
+ """Best-effort lookup of a document's public URL and filename."""
152
+ if document_id is None:
153
+ return
154
+ try:
155
+ from wagtail.documents import get_document_model
138
156
 
139
- def _has_styles(props):
140
- return any(props.get(k) for k in STYLE_KEYS)
157
+ document = get_document_model().objects.get(id=document_id)
158
+ except (ImportError, LookupError, ObjectDoesNotExist):
159
+ data.setdefault("url", None)
160
+ return
161
+ data["url"] = document.url
162
+ data["filename"] = document.filename
141
163
 
142
164
 
143
165
  def text_style_entity_decorator(props):
@@ -157,6 +179,8 @@ def text_style_entity_decorator(props):
157
179
  size = props.get("size")
158
180
  link_id = props.get("id")
159
181
  link_url = props.get("url")
182
+ document_id = props.get("document_id")
183
+ dynamic = props.get("dynamic")
160
184
  children = props.get("children")
161
185
 
162
186
  styles = []
@@ -175,6 +199,19 @@ def text_style_entity_decorator(props):
175
199
  if has_styles:
176
200
  attrs["style"] = "; ".join(styles) + ";"
177
201
 
202
+ if dynamic:
203
+ attrs["linktype"] = "dynamic"
204
+ attrs["data-dynamic"] = dynamic
205
+ attrs["href"] = "#"
206
+ attrs["data-entity-type"] = "TEXT_STYLE"
207
+ return DOM.create_element("a", attrs, children)
208
+
209
+ if document_id is not None:
210
+ attrs["linktype"] = "styled-document"
211
+ attrs["id"] = document_id
212
+ attrs["data-entity-type"] = "TEXT_STYLE"
213
+ return DOM.create_element("a", attrs, children)
214
+
178
215
  if link_id is not None:
179
216
  attrs["linktype"] = "page"
180
217
  attrs["id"] = link_id
@@ -231,6 +268,85 @@ class StyledPageLinkHandler(LinkHandler):
231
268
  return results
232
269
 
233
270
 
271
+ class StyledDocumentLinkHandler(LinkHandler):
272
+ """Front-end rewrite for styled document links.
273
+
274
+ Mirrors Wagtail's ``DocumentLinkHandler`` but preserves the inline
275
+ ``style`` so colour/highlight/size survive on the public site, and keeps
276
+ reference extraction intact for the Wagtail reference index.
277
+ """
278
+
279
+ identifier = "styled-document"
280
+
281
+ @classmethod
282
+ def get_model(cls):
283
+ from wagtail.documents import get_document_model
284
+
285
+ return get_document_model()
286
+
287
+ @classmethod
288
+ def expand_db_attributes(cls, attrs):
289
+ style = attrs.get("style", "")
290
+ try:
291
+ document = cls.get_instance(attrs)
292
+ tag = f'<a href="{escape(document.url)}"'
293
+ except (ObjectDoesNotExist, KeyError, TypeError, ValueError):
294
+ tag = "<a"
295
+ if style:
296
+ tag += f' style="{escape(style)}"'
297
+ return tag + ">"
298
+
299
+ @classmethod
300
+ def expand_db_attributes_many(cls, attrs_list):
301
+ instances = cls.get_many(attrs_list)
302
+ results = []
303
+ for attrs, document in zip(attrs_list, instances, strict=True):
304
+ style = attrs.get("style", "")
305
+ if document:
306
+ tag = f'<a href="{escape(document.url)}"'
307
+ else:
308
+ tag = "<a"
309
+ if style:
310
+ tag += f' style="{escape(style)}"'
311
+ tag += ">"
312
+ results.append(tag)
313
+ return results
314
+
315
+ @classmethod
316
+ def extract_references(cls, attrs):
317
+ yield cls.get_model(), attrs["id"], "", ""
318
+
319
+
320
+ class StyledDynamicLinkHandler(LinkHandler):
321
+ """Front-end rewrite for context-bound (dynamic) styled links.
322
+
323
+ The stored expression (``data-dynamic``) is intentionally *not* resolved
324
+ here: link handlers have no request/context, so resolution happens later in
325
+ DaisIE's context-aware rich-text renderer via ``resolve_dynamic_url``. This
326
+ handler only emits the marker plus the preserved inline style.
327
+ """
328
+
329
+ identifier = "dynamic"
330
+
331
+ @classmethod
332
+ def get_model(cls):
333
+ return None
334
+
335
+ @classmethod
336
+ def expand_db_attributes(cls, attrs):
337
+ style = attrs.get("style", "")
338
+ expression = escape(attrs.get("data-dynamic", ""))
339
+ tag = f'<a href="#" linktype="dynamic" data-dynamic="{expression}"'
340
+ tag += ' data-entity-type="TEXT_STYLE"'
341
+ if style:
342
+ tag += f' style="{escape(style)}"'
343
+ return tag + ">"
344
+
345
+ @classmethod
346
+ def expand_db_attributes_many(cls, attrs_list):
347
+ return [cls.expand_db_attributes(attrs) for attrs in attrs_list]
348
+
349
+
234
350
  def register(features):
235
351
  features.register_editor_plugin(
236
352
  "draftail",
@@ -245,6 +361,9 @@ def register(features):
245
361
  "url",
246
362
  "id",
247
363
  "parentId",
364
+ "document_id",
365
+ "filename",
366
+ "dynamic",
248
367
  ],
249
368
  "chooserUrls": {
250
369
  "pageChooser": reverse_lazy("wagtailadmin_choose_page"),
@@ -260,10 +379,12 @@ def register(features):
260
379
  "anchorLinkChooser": reverse_lazy(
261
380
  "wagtailadmin_choose_page_anchor_link"
262
381
  ),
382
+ "documentChooser": reverse_lazy("wagtaildocs_chooser:choose"),
263
383
  },
264
384
  },
265
385
  js=[
266
386
  "wagtailadmin/js/page-chooser-modal.js",
387
+ "wagtaildocs/js/document-chooser-modal.js",
267
388
  "draftail_text_utils/js/styled_link_source.js",
268
389
  "draftail_text_utils/js/text_style_entity.js",
269
390
  ],
@@ -291,4 +412,6 @@ def register(features):
291
412
  )
292
413
 
293
414
  features.register_link_type(StyledPageLinkHandler)
415
+ features.register_link_type(StyledDocumentLinkHandler)
416
+ features.register_link_type(StyledDynamicLinkHandler)
294
417
  features.default_features.append("text-style-entity")
@@ -1,4 +1,4 @@
1
- /* stylelint-disable selector-class-pattern */
1
+ /* stylelint-disable scss/selector-class-pattern */
2
2
  /* stylelint-disable declaration-property-value-allowed-list */
3
3
  /* stylelint-disable declaration-no-important */
4
4
  /* stylelint-disable scale-unlimited/declaration-strict-value */
@@ -21,17 +21,26 @@
21
21
 
22
22
  .Draftail--dtu-color-option-wrapper {
23
23
  flex: 0 0 calc(25% - 12px);
24
+ border: 0.3125rem solid var(--w-color-surface-menu-hover, #e8e8e8);
25
+ border-radius: 2px;
26
+ overflow: hidden;
27
+ }
28
+
29
+ .Draftail--dtu-color-option-wrapper:hover,
30
+ .Draftail--dtu-color-option-wrapper:has(.Draftail-ToolbarButton--active) {
31
+ border-color: var(--w-color-text-button-outline-default, #007d7e);
24
32
  }
25
33
 
26
34
  .Draftail--dtu-color-option {
27
35
  position: relative;
28
- background: attr(name type(<color>), black) !important;
29
- border: 0.3125rem solid var(--w-color-surface-menu-hover, #e8e8e8);
36
+ background: transparent !important;
37
+ border: 0 !important;
38
+ width: 100%;
39
+ min-height: 1.5rem;
30
40
  }
31
41
 
32
- .Draftail--dtu-color-option:hover,
33
- .Draftail--dtu-color-option.Draftail-ToolbarButton--active {
34
- border: 0.3125rem solid var(--w-color-text-button-outline-default, #007d7e) !important;
42
+ .Draftail--dtu-color-option:hover {
43
+ background: transparent !important;
35
44
  }
36
45
 
37
46
  .Draftail--dtu-color-hr {
@@ -1,8 +1,9 @@
1
- /* stylelint-disable selector-class-pattern */
1
+ /* stylelint-disable scss/selector-class-pattern */
2
2
  /* stylelint-disable declaration-property-value-allowed-list */
3
3
  /* stylelint-disable declaration-no-important */
4
4
  /* stylelint-disable scale-unlimited/declaration-strict-value */
5
5
  /* stylelint-disable property-disallowed-list */
6
+ /* stylelint-disable property-no-deprecated */
6
7
  .Draftail--dtu-control {
7
8
  cursor: default !important;
8
9
  position: relative;