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

1from __future__ import annotations 

2 

3from typing import Any 

4 

5from lexigram.ui.atoms.inputs.base import AbstractInput 

6from lexigram.ui.atoms.inputs.selection.select import Select 

7from lexigram.ui.core.base import el 

8 

9 

10class BelongsTo(Select): 

11 """Select field for BelongsTo relationships. 

12 

13 Links to another resource and can be searchable. 

14 """ 

15 

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 

26 

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 

33 

34 

35class MorphTo(AbstractInput): 

36 """Polymorphic relationship selector. 

37 

38 Consisted of two dropdowns: one for the type and one for the ID. 

39 """ 

40 

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 

55 

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 ) 

67 

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 ) 

75 

76 id_wrapper = el( 

77 "div", 

78 id_select_placeholder, 

79 id=f"{self.name}_id_wrapper", 

80 class_="flex-1", 

81 ) 

82 

83 return el( 

84 "div", 

85 el("div", type_select, class_="flex-1"), 

86 id_wrapper, 

87 class_="flex gap-4 items-end", 

88 ) 

89 

90 def render(self) -> Any: 

91 content = self._render_input() 

92 

93 if not self.label: 

94 return content 

95 

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 )