|
1 | 1 | import enum
|
2 | 2 | import warnings
|
3 | 3 | from typing import Any
|
| 4 | +from typing import Self |
4 | 5 |
|
5 | 6 | import attrs
|
6 | 7 | from dominate import tags
|
@@ -33,6 +34,26 @@ class NavAlignment(enum.Enum):
|
33 | 34 | class NavElement:
|
34 | 35 | """Base class for nav components"""
|
35 | 36 |
|
| 37 | + _NAV_ELEMENT_REGISTRY: dict[str, type["NavElement"]] = {} |
| 38 | + |
| 39 | + def __init_subclass__(cls) -> None: |
| 40 | + cls._NAV_ELEMENT_REGISTRY[cls.__name__] = cls |
| 41 | + |
| 42 | + def serialize(self) -> dict[str, Any]: |
| 43 | + """Serialize the element to a dictionary""" |
| 44 | + data = attrs.asdict(self) # type: ignore |
| 45 | + data["__type__"] = self.__class__.__name__ |
| 46 | + return data |
| 47 | + |
| 48 | + @classmethod |
| 49 | + def deserialize(cls, data: dict[str, Any]) -> "NavElement": |
| 50 | + """Deserialize an element from a dictionary""" |
| 51 | + if cls is NavElement: |
| 52 | + element_cls = cls._NAV_ELEMENT_REGISTRY.get(data["__type__"], NavElement) |
| 53 | + del data["__type__"] |
| 54 | + return element_cls.deserialize(data) |
| 55 | + return cls(**data) |
| 56 | + |
36 | 57 | @property
|
37 | 58 | def active(self) -> bool:
|
38 | 59 | """Whether the element is active"""
|
@@ -72,6 +93,18 @@ class Link(NavElement):
|
72 | 93 | #: The ID of the element
|
73 | 94 | id: str = attrs.field(factory=element_id.factory("nav-link"))
|
74 | 95 |
|
| 96 | + def serialize(self) -> dict[str, Any]: |
| 97 | + data = super().serialize() |
| 98 | + data["link"] = attrs.asdict(self.link) |
| 99 | + data["link"]["__type__"] = self.link.__class__.__name__ |
| 100 | + return data |
| 101 | + |
| 102 | + @classmethod |
| 103 | + def deserialize(cls, data: dict[str, Any]) -> Self: |
| 104 | + link_cls = getattr(links, data["link"].pop("__type__")) |
| 105 | + data["link"] = link_cls(**data["link"]) |
| 106 | + return cls(**data) |
| 107 | + |
75 | 108 | @classmethod
|
76 | 109 | def with_url(cls, url: str, text: str | Image, **kwargs: Any) -> "Link":
|
77 | 110 | """Create a link with a URL."""
|
@@ -135,6 +168,16 @@ class SubGroup(NavElement):
|
135 | 168 |
|
136 | 169 | items: list[NavElement] = attrs.field(factory=list)
|
137 | 170 |
|
| 171 | + def serialize(self) -> dict[str, Any]: |
| 172 | + data = super().serialize() |
| 173 | + data["items"] = [item.serialize() for item in self.items] |
| 174 | + return data |
| 175 | + |
| 176 | + @classmethod |
| 177 | + def deserialize(cls, data: dict[str, Any]) -> Self: |
| 178 | + data["items"] = [NavElement.deserialize(item) for item in data["items"]] |
| 179 | + return cls(**data) |
| 180 | + |
138 | 181 | @property
|
139 | 182 | def active(self) -> bool:
|
140 | 183 | return any(item.active for item in self.items)
|
0 commit comments