Add anchor links to headers
This commit is contained in:
parent
1c7917cb86
commit
03e9da57a1
4 changed files with 33 additions and 5 deletions
|
@ -1,6 +1,16 @@
|
||||||
section.content {
|
section.content {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
|
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
.heading-anchor {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-details {
|
.content-details {
|
||||||
|
|
|
@ -10,7 +10,7 @@ from wagtail.images import get_image_model_string
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
|
|
||||||
from .streamfield import get_blocks, get_html, get_word_count, truncate_streamfield
|
from .streamfield import get_blocks, get_html, get_word_count, truncate_streamfield
|
||||||
from .utils import TocEntry, get_table_of_contents
|
from .utils import TocEntry, add_heading_anchors, get_table_of_contents
|
||||||
|
|
||||||
|
|
||||||
class BasePage(Page):
|
class BasePage(Page):
|
||||||
|
@ -68,6 +68,10 @@ class BaseContentMixin(models.Model):
|
||||||
def summary(self) -> str:
|
def summary(self) -> str:
|
||||||
return truncate_streamfield(self.body, 50)
|
return truncate_streamfield(self.body, 50)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def body_html(self):
|
||||||
|
return add_heading_anchors(str(self.body))
|
||||||
|
|
||||||
|
|
||||||
class ContentPage(BasePage, BaseContentMixin): # type: ignore[misc]
|
class ContentPage(BasePage, BaseContentMixin): # type: ignore[misc]
|
||||||
subpage_types: list[Any] = []
|
subpage_types: list[Any] = []
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{% extends "wagtail_base.html" %}
|
{% extends "wagtail_base.html" %}
|
||||||
|
|
||||||
{% load wagtailcore_tags static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% include "common/hero.html" %}
|
{% include "common/hero.html" %}
|
||||||
|
|
||||||
<section class="container content">
|
<section class="container content">
|
||||||
{% include_block page.body %}
|
{{ page.body_html|safe }}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -5,9 +5,12 @@ from typing import Type
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http.request import HttpRequest
|
from django.http.request import HttpRequest
|
||||||
|
from django.utils.text import slugify
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
from wagtail.models import get_page_models as get_wagtail_page_models
|
from wagtail.models import get_page_models as get_wagtail_page_models
|
||||||
|
|
||||||
|
HEADER_TAGS = ["h2", "h3", "h4", "h5", "h6"]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TocEntry:
|
class TocEntry:
|
||||||
|
@ -20,10 +23,10 @@ class TocEntry:
|
||||||
def get_table_of_contents(html: str) -> list[TocEntry]:
|
def get_table_of_contents(html: str) -> list[TocEntry]:
|
||||||
soup = BeautifulSoup(html, "lxml")
|
soup = BeautifulSoup(html, "lxml")
|
||||||
|
|
||||||
headings = soup.find_all(["h2", "h3", "h4", "h5", "h6"])
|
headings = soup.find_all(HEADER_TAGS)
|
||||||
|
|
||||||
heading_levels = [
|
heading_levels = [
|
||||||
TocEntry(tag.text, tag.text, int(tag.name[1]), []) for tag in headings
|
TocEntry(tag.text, slugify(tag.text), int(tag.name[1]), []) for tag in headings
|
||||||
]
|
]
|
||||||
|
|
||||||
# Abort if there are no headings
|
# Abort if there are no headings
|
||||||
|
@ -53,6 +56,17 @@ def get_table_of_contents(html: str) -> list[TocEntry]:
|
||||||
return root.children
|
return root.children
|
||||||
|
|
||||||
|
|
||||||
|
def add_heading_anchors(html: str) -> str:
|
||||||
|
soup = BeautifulSoup(html, "lxml")
|
||||||
|
for tag in soup.find_all(HEADER_TAGS):
|
||||||
|
slug = slugify(tag.text)
|
||||||
|
anchor = soup.new_tag("a", href="#" + slug, id=slug)
|
||||||
|
anchor.string = "#"
|
||||||
|
anchor.attrs["class"] = "heading-anchor"
|
||||||
|
tag.insert(0, anchor)
|
||||||
|
return str(soup)
|
||||||
|
|
||||||
|
|
||||||
def get_page_models() -> list[Type[Page]]:
|
def get_page_models() -> list[Type[Page]]:
|
||||||
page_models = get_wagtail_page_models().copy()
|
page_models = get_wagtail_page_models().copy()
|
||||||
page_models.remove(Page)
|
page_models.remove(Page)
|
||||||
|
|
Loading…
Reference in a new issue