Coverage for src / lexigram / ui / atoms / inputs / selection / relational.py: 36%
33 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-10 04:11 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-10 04:11 +0800
1from __future__ import annotations
3from typing import Any
5from lexigram.ui.atoms.inputs.base import AbstractInput
6from lexigram.ui.atoms.inputs.selection.select import Select
7from lexigram.ui.core.base import el
10class BelongsTo(Select):
11 """Select field for BelongsTo relationships.
13 Links to another resource and can be searchable.
14 """
16 def __init__(
17 self,
18 name: str,
19 resource: str,
20 searchable: bool = False,
21 **kwargs,
22 ) -> None:
23 super().__init__(name=name, **kwargs)
24 self.resource = resource
25 self.searchable = searchable
27 def _render_input(self) -> Any:
28 select_el = super()._render_input()
29 if self.searchable:
30 select_el.props["data-searchable"] = "true"
31 select_el.props["data-resource"] = self.resource
32 return select_el
35class MorphTo(AbstractInput):
36 """Polymorphic relationship selector.
38 Consisted of two dropdowns: one for the type and one for the ID.
39 """
41 def __init__(
42 self,
43 name: str,
44 type_name: str,
45 types: list[tuple[str, str]],
46 options_url: str,
47 type_value: Any = None,
48 **kwargs,
49 ) -> None:
50 super().__init__(name=name, **kwargs)
51 self.type_name = type_name
52 self.types = types
53 self.options_url = options_url
54 self.type_value = type_value
56 def _render_input(self) -> Any:
57 type_select = Select(
58 name=self.type_name,
59 choices=self.types,
60 value=self.type_value,
61 label="Type",
62 hx_get=self.options_url,
63 hx_target=f"#{self.name}_id_wrapper",
64 hx_trigger="change",
65 hx_swap="innerHTML",
66 )
68 id_select_placeholder = Select(
69 name=self.name,
70 choices=[],
71 value=self.value,
72 label="Record",
73 disabled=(not self.type_value and not self.value),
74 )
76 id_wrapper = el(
77 "div",
78 id_select_placeholder,
79 id=f"{self.name}_id_wrapper",
80 class_="flex-1",
81 )
83 return el(
84 "div",
85 el("div", type_select, class_="flex-1"),
86 id_wrapper,
87 class_="flex gap-4 items-end",
88 )
90 def render(self) -> Any:
91 content = self._render_input()
93 if not self.label:
94 return content
96 return el(
97 "div",
98 el(
99 "label",
100 self.label,
101 class_="block text-sm font-medium text-foreground mb-2",
102 ),
103 content,
104 self._render_error(),
105 class_="mb-6",
106 )