Source code for bootlace.nav.core

import enum
import warnings
from typing import Any

import attrs
from dominate import tags
from dominate.dom_tag import dom_tag
from marshmallow import fields

from bootlace import links
from bootlace.endpoint import Endpoint
from bootlace.nav.schema import NavSchema
from bootlace.util import as_tag
from bootlace.util import BootlaceWarning
from bootlace.util import ids as element_id
from bootlace.util import MaybeTaggable
from bootlace.util import Tag










class NavElement:
    """Base class for nav components"""

    Schema: type[NavSchema] = NavSchema
    _NAV_ELEMENT_REGISTRY: dict[str, type["NavElement"]] = {}

    def __init_subclass__(cls) -> None:
        cls._NAV_ELEMENT_REGISTRY[cls.__name__] = cls

    @property
    def active(self) -> bool:
        """Whether the element is active"""
        return False

    @property
    def enabled(self) -> bool:
        """Whether the element is enabled"""
        return True

    def __tag__(self) -> dom_tag:
        warnings.warn(BootlaceWarning(f"Unhandled element {self.__class__.__name__}"), stacklevel=2)
        return tags.comment(f"unhandled element {self.__class__.__name__}")

    def element_state(self, tag: dom_tag) -> dom_tag:
        """Apply :attr:`active` and :attr:`enabled` states to the tag."""
        if not isinstance(tag, tags.html_tag):
            return tag

        if self.active:
            tag.classes.add("active")
            tag.aria["current"] = "page"

        if not self.enabled:
            tag.classes.add("disabled")
            tag.aria["disabled"] = "true"
        return tag






[docs] @attrs.define class Separator(NavElement): """A separator in dropdown menus""" hr: Tag = Tag(tags.hr, classes={"dropdown-divider"})
[docs] def __tag__(self) -> tags.html_tag: return self.hr()
[docs] @attrs.define class Text(NavElement): """A text element in the nav bar""" text: MaybeTaggable span: Tag = Tag(tags.span, classes={"nav-link"}) @property def enabled(self) -> bool: return False
[docs] def __tag__(self) -> dom_tag: tag = self.span(self.text, cls="nav-link") return self.element_state(tag)
@attrs.define class SubGroup(NavElement): """Any grouping of items in the nav bar""" items: list[NavElement] = attrs.field(factory=list, metadata={"form": fields.List(fields.Nested(NavSchema))}) @property def active(self) -> bool: return any(item.active for item in self.items)