""" Named layout presets for defpage. Each layout generates header rows for full-page and OOB rendering. Layouts wrap existing helper functions from ``shared.sx.helpers`` so defpage can reference them by name (e.g. ``:layout :root``). Layouts are registered in ``_LAYOUT_REGISTRY`` and looked up by ``get_layout()`` at request time. """ from __future__ import annotations from typing import Any, Callable, Awaitable from .helpers import ( root_header_sx, post_header_sx, post_admin_header_sx, oob_header_sx, header_child_sx, mobile_menu_sx, mobile_root_nav_sx, post_mobile_nav_sx, post_admin_mobile_nav_sx, ) # --------------------------------------------------------------------------- # Layout protocol # --------------------------------------------------------------------------- class Layout: """A named layout that generates header rows for full and OOB rendering.""" __slots__ = ("name", "_full_fn", "_oob_fn", "_mobile_fn") def __init__( self, name: str, full_fn: Callable[..., str | Awaitable[str]], oob_fn: Callable[..., str | Awaitable[str]], mobile_fn: Callable[..., str | Awaitable[str]] | None = None, ): self.name = name self._full_fn = full_fn self._oob_fn = oob_fn self._mobile_fn = mobile_fn async def full_headers(self, ctx: dict, **kwargs: Any) -> str: result = self._full_fn(ctx, **kwargs) if hasattr(result, "__await__"): result = await result return result async def oob_headers(self, ctx: dict, **kwargs: Any) -> str: result = self._oob_fn(ctx, **kwargs) if hasattr(result, "__await__"): result = await result return result async def mobile_menu(self, ctx: dict, **kwargs: Any) -> str: if self._mobile_fn is None: return "" result = self._mobile_fn(ctx, **kwargs) if hasattr(result, "__await__"): result = await result return result def __repr__(self) -> str: return f"" # --------------------------------------------------------------------------- # Registry # --------------------------------------------------------------------------- _LAYOUT_REGISTRY: dict[str, Layout] = {} def register_layout(layout: Layout) -> None: """Register a layout preset.""" _LAYOUT_REGISTRY[layout.name] = layout def get_layout(name: str) -> Layout | None: """Look up a layout by name.""" return _LAYOUT_REGISTRY.get(name) # --------------------------------------------------------------------------- # Built-in layouts # --------------------------------------------------------------------------- def _root_full(ctx: dict, **kw: Any) -> str: return root_header_sx(ctx) def _root_oob(ctx: dict, **kw: Any) -> str: root_hdr = root_header_sx(ctx) return oob_header_sx("root-header-child", "root-header-child", root_hdr) def _post_full(ctx: dict, **kw: Any) -> str: root_hdr = root_header_sx(ctx) post_hdr = post_header_sx(ctx) return "(<> " + root_hdr + " " + post_hdr + ")" def _post_oob(ctx: dict, **kw: Any) -> str: post_hdr = post_header_sx(ctx, oob=True) # Also replace #post-header-child (empty — clears any nested admin rows) child_oob = oob_header_sx("post-header-child", "", "") return "(<> " + post_hdr + " " + child_oob + ")" def _post_admin_full(ctx: dict, **kw: Any) -> str: slug = ctx.get("post", {}).get("slug", "") selected = kw.get("selected", "") root_hdr = root_header_sx(ctx) admin_hdr = post_admin_header_sx(ctx, slug, selected=selected) post_hdr = post_header_sx(ctx, child=admin_hdr) return "(<> " + root_hdr + " " + post_hdr + ")" def _post_admin_oob(ctx: dict, **kw: Any) -> str: slug = ctx.get("post", {}).get("slug", "") selected = kw.get("selected", "") post_hdr = post_header_sx(ctx, oob=True) admin_hdr = post_admin_header_sx(ctx, slug, selected=selected) admin_oob = oob_header_sx("post-header-child", "post-admin-header-child", admin_hdr) return "(<> " + post_hdr + " " + admin_oob + ")" def _root_mobile(ctx: dict, **kw: Any) -> str: return mobile_root_nav_sx(ctx) def _post_mobile(ctx: dict, **kw: Any) -> str: return mobile_menu_sx(post_mobile_nav_sx(ctx), mobile_root_nav_sx(ctx)) def _post_admin_mobile(ctx: dict, **kw: Any) -> str: slug = ctx.get("post", {}).get("slug", "") selected = kw.get("selected", "") return mobile_menu_sx( post_admin_mobile_nav_sx(ctx, slug, selected), post_mobile_nav_sx(ctx), mobile_root_nav_sx(ctx), ) register_layout(Layout("root", _root_full, _root_oob, _root_mobile)) register_layout(Layout("post", _post_full, _post_oob, _post_mobile)) register_layout(Layout("post-admin", _post_admin_full, _post_admin_oob, _post_admin_mobile)) # --------------------------------------------------------------------------- # Callable layout — services register custom Python layout functions # --------------------------------------------------------------------------- _CUSTOM_LAYOUTS: dict[str, tuple] = {} # name → (full_fn, oob_fn) def register_custom_layout(name: str, full_fn: Callable[..., str | Awaitable[str]], oob_fn: Callable[..., str | Awaitable[str]], mobile_fn: Callable[..., str | Awaitable[str]] | None = None) -> None: """Register a custom layout function. Used by services with non-standard header patterns:: register_custom_layout("sx-section", full_fn=my_full_headers, oob_fn=my_oob_headers, mobile_fn=my_mobile_menu) """ register_layout(Layout(name, full_fn, oob_fn, mobile_fn))