replace button link block with a separate button link tool inside draftail

This commit is contained in:
2026-07-05 00:51:30 +02:00
parent b0698247b6
commit 6f1d531d5d
25 changed files with 620 additions and 219 deletions
+92
View File
@@ -0,0 +1,92 @@
"""Inline "button" link for Draftail rich text fields.
Stored as `<a linktype="button" data-href="...">label</a>`. The linktype (not a
plain href) avoids colliding with the built-in link feature, and lets
expand_db_html rewrite it to `<a class="button" href="...">` for the front end.
Wired up in wagtail_hooks.py.
"""
from django.urls import reverse_lazy
from django.utils.html import escape
from draftjs_exporter.dom import DOM
from wagtail.admin.rich_text.converters.html_to_contentstate import (
InlineEntityElementHandler,
)
from wagtail.admin.rich_text.editors.draftail import features as draftail_features
from wagtail.rich_text import LinkHandler
FEATURE_NAME = "button-link"
ENTITY_TYPE = "BUTTON"
def button_link_entity_decorator(props):
"""ContentState to DB HTML (keeps just the chosen URL)."""
return DOM.create_element(
"a",
{"linktype": "button", "data-href": props.get("url", "")},
props["children"],
)
class ButtonLinkEntityElementHandler(InlineEntityElementHandler):
"""DB HTML to ContentState."""
mutability = "MUTABLE"
def get_attribute_data(self, attrs):
return {"url": attrs.get("data-href", "")}
class ButtonLinkRichTextHandler(LinkHandler):
"""DB HTML to front-end HTML (expand_db_html)."""
identifier = "button"
@classmethod
def expand_db_attributes(cls, attrs):
href = escape(attrs.get("data-href", ""))
return f'<a class="button" href="{href}">'
def register_button_link_feature(features):
features.register_editor_plugin(
"draftail",
FEATURE_NAME,
draftail_features.EntityFeature(
{
"type": ENTITY_TYPE,
"icon": "arrow-right-full",
"description": "Knapp",
# Reuse the link chooser modal + payload, so buttons work like links.
"attributes": ["url", "id", "parentId"],
"allowlist": {"href": "^(http:|https:|mailto:|tel:|#|/|undefined$)"},
"chooserUrls": {
"pageChooser": reverse_lazy("wagtailadmin_choose_page"),
"externalLinkChooser": reverse_lazy("wagtailadmin_choose_page_external_link"),
"emailLinkChooser": reverse_lazy("wagtailadmin_choose_page_email_link"),
"phoneLinkChooser": reverse_lazy("wagtailadmin_choose_page_phone_link"),
"anchorLinkChooser": reverse_lazy("wagtailadmin_choose_page_anchor_link"),
},
},
js=[
# Gives LinkModalWorkflowSource its PAGE_CHOOSER_MODAL_ONLOAD_HANDLERS.
"wagtailadmin/js/page-chooser-modal.js",
"js/draftail-button-link.js",
],
css={"all": ["css/draftail-button-link.css"]},
),
)
features.register_converter_rule(
"contentstate",
FEATURE_NAME,
{
"from_database_format": {
'a[linktype="button"]': ButtonLinkEntityElementHandler(ENTITY_TYPE),
},
"to_database_format": {
"entity_decorators": {ENTITY_TYPE: button_link_entity_decorator},
},
},
)
features.register_link_type(ButtonLinkRichTextHandler)
features.default_features.append(FEATURE_NAME)