Coverage for src/lexigram/admin/actions/row_manager/manager.py: 78%
102 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 14:56 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 14:56 +0800
1"""
2Row action manager implementation.
4Provides the main RowActionManager class for managing row actions.
5"""
7from __future__ import annotations
9from collections.abc import Callable
10from typing import Any
12from lexigram.admin.actions.row_manager.actions import (
13 BASIC_ROW_ACTIONS,
14 create_delete_action,
15 create_duplicate_action,
16 create_edit_action,
17 create_view_action,
18)
19from lexigram.admin.actions.row_manager.groups import ActionGroupManager
20from lexigram.admin.actions.row_manager.shortcuts import KeyboardShortcutManager
21from lexigram.admin.actions.row_manager.types import (
22 ActionPosition,
23 ActionStyle,
24 IRowDataSource,
25 RowAction,
26)
29class RowActionManager:
30 """Manager for row-level actions in data tables."""
32 def __init__(self, data_source: IRowDataSource[Any] | None = None):
33 """Initialize row action manager.
35 Args:
36 data_source: Optional data source for built-in actions
37 """
38 self.data_source = data_source
39 self._actions: list[RowAction] = []
40 self._group_manager = ActionGroupManager()
41 self._shortcut_manager = KeyboardShortcutManager()
43 # Initialize with default actions
44 self._initialize_default_actions()
46 def _initialize_default_actions(self) -> None:
47 """Initialize default row actions."""
48 for action in BASIC_ROW_ACTIONS:
49 self.add_action(action)
51 def add_action(self, action: RowAction) -> None:
52 """Add a row action."""
53 self._actions.append(action)
54 self._shortcut_manager.register_action(action)
56 def remove_action(self, action_name: str) -> None:
57 """Remove a row action."""
58 self._actions = list(
59 filter(lambda action: action.name != action_name, self._actions),
60 )
61 # Note: Keyboard shortcuts are managed by the shortcut manager
63 def get_action(self, name: str) -> RowAction | None:
64 """Get an action by name."""
65 # Check direct actions
66 for action in self._actions:
67 if action.name == name:
68 return action
70 # Check actions in groups
71 for group in self._group_manager.get_all_groups():
72 for action in group.actions:
73 if action.name == name:
74 return action
76 return None
78 def get_all_actions(self) -> list[RowAction]:
79 """Get all direct actions (not including grouped actions)."""
80 return self._actions.copy()
82 def get_visible_actions(
83 self,
84 record: Any,
85 position: ActionPosition | None = None,
86 ) -> list[RowAction]:
87 """Get visible actions for a record."""
88 actions = []
90 for action in self._actions:
91 # Check position filter
92 if position and action.position != position:
93 continue
95 # Check visibility
96 if action.visible(record):
97 actions.append(action)
99 return actions
101 def get_all_visible_actions(self, record: Any) -> list[RowAction]:
102 """Get all visible actions for a record (including grouped actions)."""
103 actions = self.get_visible_actions(record)
105 # Add visible actions from groups
106 for group in self._group_manager.get_visible_groups(record):
107 actions.extend(group.actions)
109 return actions
111 async def execute_action(
112 self,
113 action_name: str,
114 record_id: Any,
115 record: Any | None = None,
116 ) -> Any:
117 """Execute a row action."""
118 action = self.get_action(action_name)
119 if not action:
120 raise ValueError(f"Action not found: {action_name}")
122 if not action.handler:
123 raise ValueError(f"Action has no handler: {action_name}")
125 return await action.handler(record_id)
127 def handle_keyboard_shortcut(
128 self,
129 shortcut: str,
130 record_id: Any,
131 record: Any | None = None,
132 ) -> RowAction | None:
133 """Handle a keyboard shortcut."""
134 return self._shortcut_manager.execute_shortcut(shortcut, record_id, record)
136 def get_keyboard_shortcuts(self) -> dict[str, str]:
137 """Get all registered keyboard shortcuts."""
138 return self._shortcut_manager.get_registered_shortcuts_with_labels()
140 # Action group management
141 def create_action_group(
142 self,
143 name: str,
144 label: str,
145 actions: list[RowAction],
146 **kwargs,
147 ) -> None:
148 """Create an action group (dropdown menu)."""
149 self._group_manager.create_group(name, label, actions, **kwargs)
151 def get_visible_groups(self, record: Any) -> list:
152 """Get visible action groups for a record."""
153 return self._group_manager.get_visible_groups(record)
155 # Standard actions
156 def add_view_action(self, **kwargs) -> None:
157 """Add a view action."""
158 action = create_view_action(**kwargs)
159 self.add_action(action)
161 def add_edit_action(self, **kwargs) -> None:
162 """Add an edit action."""
163 action = create_edit_action(**kwargs)
164 self.add_action(action)
166 def add_delete_action(self, **kwargs) -> None:
167 """Add a delete action."""
168 action = create_delete_action(**kwargs)
169 if self.data_source:
170 action = RowAction(
171 name=action.name,
172 label=action.label,
173 handler=self.delete_record,
174 icon=action.icon,
175 style=action.style,
176 position=action.position,
177 confirm=action.confirm,
178 confirm_message=action.confirm_message,
179 url=action.url,
180 method=action.method,
181 open_in_modal=action.open_in_modal,
182 keyboard_shortcut=action.keyboard_shortcut,
183 visible=action.visible,
184 disabled=action.disabled,
185 tooltip=action.tooltip,
186 badge=action.badge,
187 group=action.group,
188 metadata=action.metadata,
189 )
190 self.add_action(action)
192 def add_duplicate_action(self, **kwargs) -> None:
193 """Add a duplicate action."""
194 action = create_duplicate_action(**kwargs)
195 if self.data_source:
196 action = RowAction(
197 name=action.name,
198 label=action.label,
199 handler=self.duplicate_record,
200 icon=action.icon,
201 style=action.style,
202 position=action.position,
203 confirm=action.confirm,
204 confirm_message=action.confirm_message,
205 url=action.url,
206 method=action.method,
207 open_in_modal=action.open_in_modal,
208 keyboard_shortcut=action.keyboard_shortcut,
209 visible=action.visible,
210 disabled=action.disabled,
211 tooltip=action.tooltip,
212 badge=action.badge,
213 group=action.group,
214 metadata=action.metadata,
215 )
216 self.add_action(action)
218 def add_custom_action(
219 self,
220 name: str,
221 label: str,
222 handler: Callable[[Any], Any],
223 **kwargs,
224 ) -> None:
225 """Add a custom row action."""
226 action = RowAction(
227 name=name,
228 label=label,
229 handler=handler,
230 icon=kwargs.get("icon"),
231 style=kwargs.get("style", ActionStyle.SECONDARY),
232 confirm=kwargs.get("confirm", False),
233 confirm_message=kwargs.get("confirm_message"),
234 keyboard_shortcut=kwargs.get("keyboard_shortcut"),
235 tooltip=kwargs.get("tooltip"),
236 visible=kwargs.get("visible", lambda _: True),
237 disabled=kwargs.get("disabled", lambda _: False),
238 metadata=kwargs.get("metadata", {}),
239 )
240 self.add_action(action)
242 # Data source operations
243 async def get_record(self, record_id: Any) -> Any:
244 """Get a record by ID."""
245 if self.data_source:
246 return await self.data_source.get_by_id(record_id)
247 return None
249 async def delete_record(self, record_id: Any) -> bool:
250 """Delete a record."""
251 if self.data_source:
252 return await self.data_source.delete(record_id)
253 return False
255 async def duplicate_record(self, record_id: Any) -> Any:
256 """Duplicate a record."""
257 if self.data_source:
258 return await self.data_source.duplicate(record_id)
259 return None
261 # Utility methods
262 def clear_all_actions(self) -> None:
263 """Clear all actions and shortcuts."""
264 self._actions.clear()
265 self._group_manager.clear_groups()
266 self._shortcut_manager.clear_all_shortcuts()
268 def get_action_count(self) -> int:
269 """Get total number of actions."""
270 direct_actions = len(self._actions)
271 grouped_actions = sum(
272 len(group.actions) for group in self._group_manager.get_all_groups()
273 )
274 return direct_actions + grouped_actions
276 def get_group_count(self) -> int:
277 """Get number of action groups."""
278 return len(self._group_manager.get_all_groups())