Source code for duck.html.components.table_of_contents

"""
Table of Contents HTML Component.

This module defines two components:
1. `TableOfContentsSection` - Represents a content section with a heading and body.
2. `TableOfContents` - Generates a navigable table of contents.

Each `TableOfContentsSection` is added to the `TableOfContents` component, which 
automatically creates a clickable list of links for navigation.
"""

from duck.utils.slug import slugify
from duck.html.components import to_component
from duck.html.components.container import FlexContainer
from duck.html.components.heading import Heading
from duck.html.components.paragraph import Paragraph
from duck.html.components.link import Link


[docs] class TableOfContentsSection(FlexContainer): """ Represents a section in the Table of Contents. Each section consists of a heading (anchor link target) and body content. Args: - `heading` (str): The heading of the section, which will be linked in the Table of Contents. - `body` (str): The main content of the section, which can be plain text or HTML. """
[docs] def on_create(self): super().on_create() self.style["flex-direction"] = "column" self.style["margin-top"] = "10px" self.klass = "toc-section" self.heading = None self.body = None # Add heading if provided if "heading" in self.kwargs: heading_text = self.kwargs.get("heading") or "" self.heading = Heading("h3", inner_html=heading_text, klass="toc-heading", id=slugify(heading_text)) self.add_child(self.heading) # Add body content if provided if "body" in self.kwargs: body_content = self.kwargs.get("body") or "" self.body = Paragraph(inner_html=body_content, klass="toc-body") # Allow html rendering self.add_child(self.body)
[docs] class TableOfContents(FlexContainer): """ Table of Contents Component. This component generates a navigable list of links to `TableOfContentsSection` instances. Args: title (str): The title displayed at the top of the Table of Contents. Defaults to "Table of Contents". """
[docs] def on_create(self): super().on_create() self.style["flex-direction"] = "column" self.style["gap"] = "3px" self.klass = "table-of-contents" # Set title title_text = self.kwargs.get("title", "Table of Contents") self.title_heading = Heading("h1", text=title_text, klass="toc-title") # Create list container for quick navigation links self.list_container = to_component("", tag="ul", klass="toc-list") # Add title and list container to the component super().add_child(self.title_heading) super().add_child(self.list_container)
[docs] def add_child(self, child: TableOfContentsSection, list_style: str = "circle"): """ Adds a `TableOfContentsSection` to the Table of Contents. This method also creates a clickable link for the section heading. Args: section (TableOfContentsSection): The section to add. list_style (str): The CSS list-style type for the navigation items. Defaults to "circle". """ self.add_section(child, list_style)
[docs] def add_section(self, section: TableOfContentsSection, list_style: str = "circle"): """ Adds a `TableOfContentsSection` to the Table of Contents. This method also creates a clickable link for the section heading. Args: section (TableOfContentsSection): The section to add. list_style (str): The CSS list-style type for the navigation items. Defaults to "circle". """ assert isinstance(section, TableOfContentsSection), "Only a TableOfContentsSection component is allowed" if section.heading: heading_text = section.heading.inner_html heading_link = Link(text=heading_text) heading_link.style["text-decoration"] = "none" heading_link.props["href"] = f"#{section.heading.props.get('id', '')}" list_item = to_component("", tag="li") list_item.style["list-style"] = list_style list_item.add_child(heading_link) self.list_container.add_child(list_item) # Add the section to the Table of Contents super().add_child(section)