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
« 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.core.base import Component, el
8class InputGroup(Component):
9 """
10 Input with prefix or suffix add-ons.
11 """
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
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 )