replace button link block with a separate button link tool inside draftail
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from grapple.helpers import register_streamfield_block
|
||||
from grapple.models import (
|
||||
GraphQLImage,
|
||||
@@ -8,7 +7,6 @@ from grapple.models import (
|
||||
GraphQLString,
|
||||
)
|
||||
from wagtail import blocks
|
||||
from wagtail.blocks import StructBlockValidationError
|
||||
from wagtail.embeds.blocks import EmbedBlock
|
||||
from wagtail.images.blocks import ImageChooserBlock
|
||||
|
||||
@@ -283,51 +281,6 @@ class ScheduleBlock(blocks.StructBlock):
|
||||
label = "Timeplan"
|
||||
|
||||
|
||||
@register_streamfield_block
|
||||
class ButtonLinkBlock(blocks.StructBlock):
|
||||
text = blocks.CharBlock(
|
||||
max_length=64,
|
||||
required=True,
|
||||
label="Tekst",
|
||||
help_text="Teksten som vises på knappen.",
|
||||
)
|
||||
page = blocks.PageChooserBlock(
|
||||
required=False,
|
||||
label="Intern side",
|
||||
help_text="Velg en underside på denne nettsiden.",
|
||||
)
|
||||
anchor = blocks.CharBlock(
|
||||
max_length=64,
|
||||
required=False,
|
||||
label="Anker",
|
||||
help_text="Anker på siden, uten «#» (valgfritt)",
|
||||
)
|
||||
external_url = blocks.URLBlock(
|
||||
required=False,
|
||||
label="Ekstern lenke",
|
||||
)
|
||||
|
||||
graphql_fields = [
|
||||
GraphQLString("text", required=True),
|
||||
GraphQLPage("page"),
|
||||
GraphQLString("anchor"),
|
||||
GraphQLString("external_url"),
|
||||
]
|
||||
|
||||
def clean(self, value):
|
||||
result = super().clean(value)
|
||||
if value.get("page") and value.get("external_url"):
|
||||
error = ValidationError(
|
||||
"Velg enten en intern side eller en ekstern lenke, ikke begge."
|
||||
)
|
||||
raise StructBlockValidationError(block_errors={"page": error, "external_url": error})
|
||||
return result
|
||||
|
||||
class Meta:
|
||||
icon = "link"
|
||||
label = "Knapp med lenke"
|
||||
|
||||
|
||||
BASE_BLOCKS = [
|
||||
("paragraph", blocks.RichTextBlock(label="Rik tekst")),
|
||||
("image", ImageWithTextBlock()),
|
||||
@@ -338,7 +291,6 @@ BASE_BLOCKS = [
|
||||
("accordion", AccordionBlock()),
|
||||
("fact_box", FactBoxBlock()),
|
||||
("schedule", ScheduleBlock()),
|
||||
("button_link", ButtonLinkBlock()),
|
||||
("embed", EmbedBlock()),
|
||||
("raw_html", blocks.RawHTMLBlock()),
|
||||
]
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,17 @@
|
||||
/* Styles the button-link entity as a button in the editor. Front end uses the global .button class. */
|
||||
.button-link-entity .TooltipEntity {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25em;
|
||||
padding: 0.1em 0.7em;
|
||||
border: 1.5px solid currentColor;
|
||||
border-radius: 999px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.button-link-entity .TooltipEntity__icon {
|
||||
width: 0.85em;
|
||||
height: 0.85em;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Draftail plugin for the "button-link" feature (see dnscms/rich_text.py).
|
||||
* Reuses Wagtail's link chooser (source) and edit/remove tooltip (decorator),
|
||||
* so buttons insert and edit like normal links. No build step: uses Wagtail's
|
||||
* globals and React.createElement.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var draftail = window.draftail;
|
||||
var React = window.React;
|
||||
var Icon = window.wagtail.components.Icon;
|
||||
var TooltipEntity = draftail.TooltipEntity;
|
||||
|
||||
// Wrap TooltipEntity so CSS can style it as a button
|
||||
function ButtonLink(props) {
|
||||
var url = props.contentState.getEntity(props.entityKey).getData().url || null;
|
||||
return React.createElement(
|
||||
"span",
|
||||
{ className: "button-link-entity" },
|
||||
React.createElement(
|
||||
TooltipEntity,
|
||||
Object.assign({}, props, {
|
||||
icon: React.createElement(Icon, { name: "arrow-right-full" }),
|
||||
label: url || "",
|
||||
url: url,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
draftail.registerPlugin(
|
||||
{
|
||||
type: "BUTTON",
|
||||
source: draftail.LinkModalWorkflowSource,
|
||||
decorator: ButtonLink,
|
||||
},
|
||||
"entityTypes"
|
||||
);
|
||||
})();
|
||||
@@ -5,12 +5,19 @@ from wagtail import hooks
|
||||
from wagtail.models import Page
|
||||
from wagtail.search.backends import get_search_backend
|
||||
|
||||
from dnscms.rich_text import register_button_link_feature
|
||||
|
||||
|
||||
@hooks.register("register_rich_text_features")
|
||||
def enable_additional_rich_text_features(features):
|
||||
features.default_features.extend(["h5", "h6", "blockquote"])
|
||||
|
||||
|
||||
@hooks.register("register_rich_text_features")
|
||||
def register_button_link(features):
|
||||
register_button_link_feature(features)
|
||||
|
||||
|
||||
@hooks.register("register_schema_query")
|
||||
def override_search_resolver(query_mixins):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user