Coverage for src/lexigram/admin/navigation/builder.py: 0%

29 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-24 23:18 +0800

1"""Fluent builder for admin navigation configuration.""" 

2 

3from __future__ import annotations 

4 

5from lexigram.admin.navigation.types import NavGroup, NavigationConfig, NavItem 

6 

7 

8class NavigationBuilder: 

9 """Fluent builder for navigation configuration. 

10 

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 """ 

23 

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] = {} 

29 

30 def prefix(self, prefix: str) -> NavigationBuilder: 

31 """Set URL prefix.""" 

32 self._prefix = prefix 

33 return self 

34 

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 

39 

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 

64 

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") 

80 

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 

94 

95 def end_group(self) -> NavigationBuilder: 

96 """End the current group.""" 

97 self._current_group = None 

98 return self 

99 

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 ) 

107 

108 

109__all__ = ["NavigationBuilder"]