Source code for htmx_nav.shell

"""
make_shell_renderer: a render_nav wrapper that always includes a fixed
list of navigational Swaps alongside whatever extra_swaps the caller
passes per-call.

Earlier versions took a single `shell_template` + `context_builder` and
built exactly one Swap internally. That collapsed every navigational
region into one fragment, which meant giving up what Swap already does
per-region for free: independent target_id, independent include_if for
conditional inclusion, and independent debug-swap highlighting (the
debug marker in swaps.py is emitted per Swap, keyed on that Swap's own
target_id — one shell Swap means one marker for the whole shell).

This version takes a `swaps` builder instead: a callable that returns
whatever Swap(s) should always accompany this shell for a given
request. Each returned Swap is a full Swap — its own template, context,
target_id, include_if — so per-region conditional rendering and
per-region debug highlighting both fall out for free, the same way they
would for any hand-written `render_nav(..., swaps=[...])` call.
make_shell_renderer's only remaining job is merging that fixed list
with per-call extra_swaps and forwarding to render_nav.
"""

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

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

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


[docs] class ShellRenderer(Protocol): """Callable signature for renderers produced by `make_shell_renderer`.""" def __call__( self, request: HttpRequest, template_name: str, context: Mapping[str, Any] | None = None, *, extra_swaps: Swaps = None, partial: PartialSpec = "#content", **kwargs: Any, ) -> TemplateResponse: ... """ Renders a template with a fixed set of navigational Swaps always included. Args: request: The HTTP request object. template_name: Path to the main content template. context: Optional context for the main template. extra_swaps: Additional, per-call Swaps included alongside the fixed shell Swaps. partial: Specifies the partial to render for HTMX requests. **kwargs: Additional arguments passed to `render_nav`. Returns: A TemplateResponse with the shell Swaps included. """
[docs] def make_shell_renderer( swaps: Swaps | Callable[[HttpRequest], Swaps], *, partial: PartialSpec = "#content", ) -> ShellRenderer: """ Creates a renderer that always includes a fixed set of Swaps. Args: swaps: Swaps inclued in this request by default. Using a Callable allows the swap context to vary based on the request. partial: Default PartialSpec used unless overridden per-call. Returns: A `render_shell` function with the signature of `ShellRenderer`. Example: .. code-block:: python def build_shell_swaps(request): return [ Swap("nav/_sidebar.html", sidebar_context(request), target_id="sidebar"), Swap("nav/_breadcrumbs.html", crumbs_context(request), target_id="breadcrumbs"), ] render_shell = make_shell_renderer(build_shell_swaps) def project_detail(request, pk): project = get_object_or_404(Project, pk=pk) return render_shell(request, "app/project_detail.html", {"project": project}) """ default_partial: PartialSpec = partial def render_shell( request: HttpRequest, template_name: str, context: Mapping[str, Any] | None = None, *, extra_swaps: Swaps = None, partial: PartialSpec = default_partial, **kwargs: Any, ) -> TemplateResponse: resolved = swaps(request) if callable(swaps) else swaps return render_nav( request, template_name, context, partial=partial, swaps=[*_normalize_swaps(resolved), *_normalize_swaps(extra_swaps)], **kwargs, ) return render_shell