Coverage for src / lexigram / admin / navigation / builder.py: 38%
29 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-13 22:14 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-13 22:14 +0800
1"""Fluent builder for admin navigation configuration."""
3from __future__ import annotations
5from lexigram.admin.navigation.types import NavGroup, NavigationConfig, NavItem
8class NavigationBuilder:
9 """Fluent builder for navigation configuration.
11 Example:
12 >>> nav = (NavigationBuilder()
13 ... .prefix("/admin")
14 ... .group("main", "Main Menu", icon="home", order=0)
15 ... .item("dashboard", "Dashboard", "/admin/dashboard", icon="chart-pie")
16 ... .item("users", "Users", "/admin/users", icon="users", resource="users")
17 ... .end_group()
18 ... .group("content", "Content", icon="document", order=1)
19 ... .item("posts", "Posts", "/admin/posts", resource="posts")
20 ... .end_group()
21 ... .build())
22 """
24 def __init__(self) -> None:
25 self._prefix = "/admin"
26 self._groups: dict[str, NavGroup] = {}
27 self._current_group: str | None = None
28 self._default_icons: dict[str, str] = {}
30 def prefix(self, prefix: str) -> NavigationBuilder:
31 """Set URL prefix."""
32 self._prefix = prefix
33 return self
35 def default_icon(self, resource: str, icon: str) -> NavigationBuilder:
36 """Set default icon for a resource type."""
37 self._default_icons[resource] = icon
38 return self
40 def group(
41 self,
42 name: str,
43 label: str,
44 *,
45 icon: str | None = None,
46 order: int = 0,
47 collapsible: bool = True,
48 permission: str | None = None,
49 feature_flag: str | None = None,
50 ) -> NavigationBuilder:
51 """Start a new navigation group."""
52 self._groups[name] = NavGroup(
53 name=name,
54 label=label,
55 icon=icon,
56 order=order,
57 collapsible=collapsible,
58 permission=permission,
59 feature_flag=feature_flag,
60 items=[],
61 )
62 self._current_group = name
63 return self
65 def item(
66 self,
67 name: str,
68 label: str,
69 url: str,
70 *,
71 icon: str | None = None,
72 badge: str | None = None,
73 resource: str | None = None,
74 permission: str | None = None,
75 external: bool = False,
76 ) -> NavigationBuilder:
77 """Add an item to the current group."""
78 if not self._current_group:
79 raise ValueError("Must call group() before adding items")
81 self._groups[self._current_group].items.append(
82 NavItem(
83 name=name,
84 label=label,
85 url=url,
86 icon=icon,
87 badge=badge,
88 resource_name=resource,
89 permission=permission,
90 external=external,
91 ),
92 )
93 return self
95 def end_group(self) -> NavigationBuilder:
96 """End the current group."""
97 self._current_group = None
98 return self
100 def build(self) -> NavigationConfig:
101 """Build the navigation configuration."""
102 return NavigationConfig(
103 prefix=self._prefix,
104 groups=self._groups,
105 default_icons=self._default_icons,
106 )
109__all__ = ["NavigationBuilder"]