Coverage for src / lexigram / admin / ui / molecules / page_size_selector.py: 30%
20 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-11 02:25 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-11 02:25 +0800
1from __future__ import annotations
3from typing import Any
5from lexigram.ui import Component, Select, Zones, el
8class PageSizeSelector(Component):
9 """
10 Component for selecting the number of items per page.
11 Uses Zone-based targeting for consistent HTMX behavior.
12 """
14 def __init__(
15 self,
16 per_page: int = 20,
17 base_url: str = "",
18 extra_query: str = "",
19 hx_target: str | None = None,
20 hx_swap: str = "innerHTML",
21 hx_push_url: str = "true",
22 size_options: list[int] | None = None,
23 state: Any | None = None,
24 **props,
25 ) -> None:
26 super().__init__(**props)
27 self.per_page = per_page
28 self.base_url = base_url
29 self.extra_query = extra_query
30 # Use Zones.DATA by default
31 self.hx_target = hx_target or Zones.DATA.selector
32 self.hx_swap = hx_swap
33 self.hx_push_url = hx_push_url
34 self.size_options = size_options or [10, 20, 50, 100]
35 self.state = state
37 def render(self) -> Any:
38 choices = [(str(size), str(size)) for size in self.size_options]
40 # Build consistent HTMX attrs using new API
42 if self.state:
43 # Use clean URL with full state include to avoid duplication and state loss
44 htmx_attrs = {
45 "hx-get": f"{self.base_url.rstrip('/')}/",
46 "hx-target": self.hx_target,
47 "hx-swap": self.hx_swap,
48 "hx-select": Zones.DATA.selector,
49 "hx-push_url": "true",
50 "hx-trigger": "change",
51 "hx-include": f"{Zones.TABLE.selector} [name]",
52 "hx-params": "*",
53 }
54 else:
55 # Fallback for simple usage
56 htmx_attrs = {
57 "hx-get": f"{self.base_url}?page=1{self.extra_query}",
58 "hx-target": self.hx_target,
59 "hx-swap": self.hx_swap,
60 "hx-select": Zones.DATA.selector,
61 "hx-push_url": str(self.hx_push_url).lower(),
62 "hx-trigger": "change",
63 "hx-include": "this",
64 }
66 return el(
67 "div",
68 el(
69 "label",
70 "per page",
71 class_="text-sm text-muted-foreground mr-2 hidden lg:inline",
72 ),
73 Select(
74 name="per_page",
75 label=None,
76 choices=choices,
77 value=str(self.per_page),
78 class_="w-20 text-sm",
79 **{k.replace("-", "_"): v for k, v in htmx_attrs.items()}, # type: ignore[arg-type]
80 ),
81 class_="flex items-center",
82 )