Coverage for src / lexigram / ui / molecules / input_group.py: 100%

16 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.core.base import Component, el 

6 

7 

8class InputGroup(Component): 

9 """ 

10 Input with prefix or suffix add-ons. 

11 """ 

12 

13 def __init__( 

14 self, 

15 label: str, 

16 name: str, 

17 input_type: str = "text", 

18 prefix: str | None = None, 

19 suffix: str | None = None, 

20 placeholder: str | None = None, 

21 value: str = "", 

22 error: str | None = None, 

23 **props, 

24 ) -> None: 

25 super().__init__( 

26 label=label, 

27 name=name, 

28 type=input_type, 

29 prefix=prefix, 

30 suffix=suffix, 

31 placeholder=placeholder, 

32 value=value, 

33 error=error, 

34 **props, 

35 ) 

36 self.label = label 

37 self.name = name 

38 self.type = type 

39 self.prefix = prefix 

40 self.suffix = suffix 

41 self.placeholder = placeholder 

42 self.value = value 

43 self.error = error 

44 

45 def render(self) -> Any: 

46 return el( 

47 "div", 

48 el( 

49 "label", 

50 self.label, 

51 for_=self.name, 

52 class_="block text-sm font-medium text-foreground mb-1", 

53 ), 

54 el( 

55 "div", 

56 ( 

57 el( 

58 "div", 

59 el( 

60 "span", 

61 self.prefix, 

62 class_="text-muted-foreground sm:text-sm px-3", 

63 ), 

64 class_="flex items-center pointer-events-none", 

65 ) 

66 if self.prefix 

67 else "" 

68 ), 

69 el( 

70 "input", 

71 type=self.type, 

72 name=self.name, 

73 id=self.name, 

74 value=self.value, 

75 placeholder=self.placeholder, 

76 **({"aria_describedby": f"{self.name}-error"} if self.error else {}), 

77 **({"aria_invalid": "true"} if self.error else {}), 

78 class_=f"block w-full min-w-0 flex-1 border-0 bg-transparent py-2 text-foreground placeholder:text-muted-foreground focus:ring-0 sm:text-sm {'pl-1' if self.prefix else 'pl-3'} {'pr-1' if self.suffix else 'pr-3'}", 

79 ), 

80 ( 

81 el( 

82 "div", 

83 el( 

84 "span", 

85 self.suffix, 

86 class_="text-muted-foreground sm:text-sm px-3", 

87 ), 

88 class_="flex items-center pointer-events-none", 

89 ) 

90 if self.suffix 

91 else "" 

92 ), 

93 class_=f"flex rounded-lg shadow-sm ring-1 ring-inset focus-within:ring-2 focus-within:ring-inset transition-all duration-200 {'ring-destructive focus-within:ring-destructive' if self.error else 'ring-[var(--input)] focus-within:ring-ring'} bg-background", 

94 ), 

95 ( 

96 el("p", self.error, id=f"{self.name}-error", class_="mt-2 text-sm text-destructive") 

97 if self.error 

98 else "" 

99 ), 

100 class_="mb-6", 

101 )