Source code for htmx_nav.views

from __future__ import annotations

from collections.abc import Callable
from typing import Any, Protocol

from django.http import HttpRequest
from django.template.response import TemplateResponse

from .partials import PartialSpec
from .shell import ShellRenderer
from .shortcuts import render_nav
from .swaps import Swaps, _normalize_swaps


class _ShellViewProtocol(Protocol):
    request: HttpRequest

    def get_template_names(self) -> list[str]: ...
    def get_extra_swaps(self) -> Swaps: ...
    def get_partial(self) -> PartialSpec: ...
    def get_title(self) -> str | None: ...
    def get_shell_template_name(self) -> str: ...


[docs] def make_shell_view_mixin( render: ShellRenderer | None = None, *, default_swaps: Swaps = None, default_partial: PartialSpec = "#content", ) -> type: """Build a mixin that routes a CBV's response through `render_nav` — directly by default, or through a shell renderer if one is supplied. Args: render: Optional render function, usually created by `make_shell_renderer`. When omitted, the mixin calls `render_nav` directly — no `make_shell_renderer` required for a CBV to use swaps. default_swaps: Swap(s) applied on every view using this mixin, combined with (not replaced by) each view's own `get_extra_swaps()`. default_partial: Partial spec used unless a view overrides `get_partial()`. Override points on the view: - `get_extra_swaps()`: this view's Swap(s), runs after `self.request`/`self.object` are set. - `get_title()` / `title` class attribute. - `get_partial()`: overrides `default_partial` per-view. - `get_shell_template_name()`: defaults to `get_template_names()[0]`. Example: .. code-block:: python ShellViewMixin = make_shell_view_mixin() class TicketListView(ShellViewMixin, ListView): template_name = "pages/project.html" def get_extra_swaps(self): return [sidebar_swap, breadcrumb_swap] """ defaults = _normalize_swaps(default_swaps) render_fn: Callable[..., TemplateResponse] = render or render_nav swaps_kwarg = "extra_swaps" if render is not None else "swaps" class ShellViewMixin: title: str | None = None def get_extra_swaps(self) -> Swaps: return None def get_title(self) -> str | None: return self.title def get_partial(self) -> PartialSpec: return default_partial def get_shell_template_name(self: _ShellViewProtocol) -> str: return self.get_template_names()[0] def render_to_response( self: _ShellViewProtocol, context: dict[str, Any], **response_kwargs: Any, ) -> TemplateResponse: swaps = [*defaults, *_normalize_swaps(self.get_extra_swaps())] response_kwargs.setdefault("partial", self.get_partial()) response_kwargs.setdefault("title", self.get_title()) response_kwargs.setdefault(swaps_kwarg, swaps) return render_fn( self.request, self.get_shell_template_name(), context, **response_kwargs, ) return ShellViewMixin