Coverage for src/lexigram/admin/resources/layouts.py: 0%
40 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:31 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:31 +0800
1"""Layout configurators for Admin Resources.
3Registry-style dispatch that applies a resource's layout configuration to a
4:class:`~lexigram.admin.layout.layout_manager.LayoutManager`. One configurator
5per :class:`~lexigram.admin.layout.LayoutType`; the registry selects by
6``config.type`` and raises on unknown types so misconfiguration is explicit.
7"""
9from __future__ import annotations
11from typing import TYPE_CHECKING, Any, Protocol
13if TYPE_CHECKING:
14 from lexigram.admin.layout.layout_manager import LayoutManager
17class LayoutConfiguratorProtocol(Protocol):
18 """Protocol for layout configurators."""
20 def can_configure(self, layout_type: Any) -> bool: ...
22 def configure_layout(self, manager: LayoutManager, config: Any) -> None: ...
25class GridLayoutConfigurator:
26 def can_configure(self, layout_type: Any) -> bool:
27 from lexigram.admin.layout import LayoutType
29 return bool(layout_type == LayoutType.GRID)
31 def configure_layout(self, manager: LayoutManager, config: Any) -> None:
32 manager.add_grid_layout( # type: ignore[attr-defined]
33 columns=config.columns,
34 card_template=config.card_template,
35 enabled=config.enabled,
36 )
39class CalendarLayoutConfigurator:
40 def can_configure(self, layout_type: Any) -> bool:
41 from lexigram.admin.layout import LayoutType
43 return bool(layout_type == LayoutType.CALENDAR)
45 def configure_layout(self, manager: LayoutManager, config: Any) -> None:
46 manager.add_calendar_layout( # type: ignore[attr-defined]
47 date_field=config.date_field,
48 title_field=config.title_field,
49 enabled=config.enabled,
50 )
53class MapLayoutConfigurator:
54 def can_configure(self, layout_type: Any) -> bool:
55 from lexigram.admin.layout import LayoutType
57 return bool(layout_type == LayoutType.MAP)
59 def configure_layout(self, manager: LayoutManager, config: Any) -> None:
60 manager.add_map_layout( # type: ignore[attr-defined]
61 latitude_field=config.latitude_field,
62 longitude_field=config.longitude_field,
63 marker_template=config.marker_template,
64 enabled=config.enabled,
65 )
68class ListLayoutConfigurator:
69 def can_configure(self, layout_type: Any) -> bool:
70 from lexigram.admin.layout import LayoutType
72 return bool(layout_type == LayoutType.LIST)
74 def configure_layout(self, manager: LayoutManager, config: Any) -> None:
75 manager.add_list_layout(enabled=config.enabled) # type: ignore[attr-defined]
78class LayoutConfiguratorRegistry:
79 """Registry that selects a configurator for a given layout type."""
81 def __init__(self) -> None:
82 self._configurators: list[LayoutConfiguratorProtocol] = [
83 GridLayoutConfigurator(),
84 CalendarLayoutConfigurator(),
85 MapLayoutConfigurator(),
86 ListLayoutConfigurator(),
87 ]
89 def configure(self, manager: LayoutManager, config: Any) -> None:
90 for c in self._configurators:
91 if c.can_configure(config.type):
92 c.configure_layout(manager, config)
93 return
94 # Fallback: raise to make misconfiguration explicit
95 raise ValueError(f"No configurator for layout type: {config.type}")
98_layout_configurator_registry = LayoutConfiguratorRegistry()
101def apply_layout_config(manager: LayoutManager, config: Any) -> None:
102 """Apply layout configuration to *manager* using the configurator registry.
104 Args:
105 manager: Layout manager receiving the configured layout.
106 config: Layout configuration object with a ``type`` attribute.
107 """
108 _layout_configurator_registry.configure(manager, config)
111__all__ = [
112 "CalendarLayoutConfigurator",
113 "GridLayoutConfigurator",
114 "LayoutConfiguratorProtocol",
115 "LayoutConfiguratorRegistry",
116 "ListLayoutConfigurator",
117 "MapLayoutConfigurator",
118 "apply_layout_config",
119]