Source code for litestar_htmx.response

from __future__ import annotations

from typing import Any, Generic, TypeVar
from urllib.parse import quote

from litestar import Response
from litestar.response import Template
from litestar.status_codes import HTTP_200_OK

from litestar_htmx._utils import HTMX_STOP_POLLING, get_headers
from litestar_htmx.types import (
    EventAfterType,
    HtmxHeaderType,
    LocationType,
    PushUrlType,
    ReSwapMethod,
    TriggerEventType,
)

__all__ = (
    "ClientRedirect",
    "ClientRefresh",
    "HTMXTemplate",
    "HXLocation",
    "HXStopPolling",
    "PushUrl",
    "ReplaceUrl",
    "Reswap",
    "Retarget",
    "TriggerEvent",
)


# HTMX defined HTTP status code.
# Response carrying this status code will ask client to stop Polling.
T = TypeVar("T")


[docs] class HXStopPolling(Response): """Stop HTMX client from Polling."""
[docs] def __init__(self) -> None: """Initialize""" super().__init__(content=None) self.status_code = HTMX_STOP_POLLING
[docs] class ClientRedirect(Response): """HTMX Response class to support client side redirect."""
[docs] def __init__(self, redirect_to: str) -> None: """Set status code to 200 (required by HTMX), and pass redirect url.""" super().__init__(content=None, headers=get_headers(hx_headers=HtmxHeaderType(redirect=redirect_to))) del self.headers["Location"]
[docs] class ClientRefresh(Response): """Response to support HTMX client page refresh"""
[docs] def __init__(self) -> None: """Set Status code to 200 and set headers.""" super().__init__(content=None, headers=get_headers(hx_headers=HtmxHeaderType(refresh=True)))
[docs] class PushUrl(Generic[T], Response[T]): """Response to push new url into the history stack."""
[docs] def __init__(self, content: T, push_url: PushUrlType, **kwargs: Any) -> None: """Initialize PushUrl.""" super().__init__( content=content, status_code=HTTP_200_OK, headers=get_headers(hx_headers=HtmxHeaderType(push_url=push_url)), **kwargs, )
[docs] class ReplaceUrl(Generic[T], Response[T]): """Response to replace url in the Browser Location bar."""
[docs] def __init__(self, content: T, replace_url: PushUrlType, **kwargs: Any) -> None: """Initialize ReplaceUrl.""" super().__init__( content=content, status_code=HTTP_200_OK, headers=get_headers(hx_headers=HtmxHeaderType(replace_url=replace_url)), **kwargs, )
[docs] class Reswap(Generic[T], Response[T]): """Response to specify how the response will be swapped."""
[docs] def __init__( self, content: T, method: ReSwapMethod, **kwargs: Any, ) -> None: """Initialize Reswap.""" super().__init__(content=content, headers=get_headers(hx_headers=HtmxHeaderType(re_swap=method)), **kwargs)
[docs] class Retarget(Generic[T], Response[T]): """Response to target different element on the page."""
[docs] def __init__(self, content: T, target: str, **kwargs: Any) -> None: """Initialize Retarget.""" super().__init__(content=content, headers=get_headers(hx_headers=HtmxHeaderType(re_target=target)), **kwargs)
[docs] class TriggerEvent(Generic[T], Response[T]): """Trigger Client side event."""
[docs] def __init__( self, content: T, name: str, after: EventAfterType, params: dict[str, Any] | None = None, **kwargs: Any, ) -> None: """Initialize TriggerEvent.""" event = TriggerEventType(name=name, params=params, after=after) headers = get_headers(hx_headers=HtmxHeaderType(trigger_event=event)) super().__init__(content=content, headers=headers, **kwargs)
[docs] class HXLocation(Response): """Client side redirect without full page reload."""
[docs] def __init__( self, redirect_to: str, source: str | None = None, event: str | None = None, target: str | None = None, select: str | None = None, swap: ReSwapMethod | None = None, hx_headers: dict[str, Any] | None = None, values: dict[str, str] | None = None, **kwargs: Any, ) -> None: """Initialize HXLocation, Set status code to 200 (required by HTMX), and pass redirect url. """ super().__init__( content=None, headers={"Location": quote(redirect_to, safe="/#%[]=:;$&()+,!?*@'~")}, **kwargs, ) spec: dict[str, Any] = get_headers( hx_headers=HtmxHeaderType( location=LocationType( path=str(self.headers.get("Location")), source=source, event=event, target=target, select=select, swap=swap, values=values, hx_headers=hx_headers, ) ) ) del self.headers["Location"] self.headers.update(spec)
[docs] class HTMXTemplate(Template): """HTMX template wrapper"""
[docs] def __init__( self, push_url: PushUrlType | None = None, re_swap: ReSwapMethod | None = None, re_target: str | None = None, trigger_event: str | None = None, params: dict[str, Any] | None = None, after: EventAfterType | None = None, **kwargs: Any, ) -> None: """Create HTMXTemplate response. Args: push_url: Either a string value specifying a URL to push to browser history or ``False`` to prevent HTMX client from pushing a url to browser history. re_swap: Method value to instruct HTMX which swapping method to use. re_target: Value for 'id of target element' to apply changes to. trigger_event: Event name to trigger. params: Dictionary of parameters if any required with trigger event parameter. after: Changes to apply after ``receive``, ``settle`` or ``swap`` event. **kwargs: Additional arguments to pass to ``Template``. """ super().__init__(**kwargs) event: TriggerEventType | None = None if trigger_event: event = TriggerEventType(name=str(trigger_event), params=params, after=after) self.headers.update( get_headers(HtmxHeaderType(push_url=push_url, re_swap=re_swap, re_target=re_target, trigger_event=event)) )