Coverage for src / lexigram / ui / atoms / progress_bar.py: 100%
18 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, Literal
5from lexigram.ui.core.base import Component, el
8class ProgressBar(Component):
9 """Linear progress bar with percentage display.
11 Args:
12 value: Current progress value
13 max: Maximum value (default 100)
14 label: Optional label text
15 show_percentage: Whether to show percentage text
16 size: Height variant (sm, md, lg)
17 """
19 def __init__(
20 self,
21 value: int,
22 max_value: int = 100,
23 label: str | None = None,
24 show_percentage: bool = True,
25 size: Literal["sm", "md", "lg"] = "md",
26 **props,
27 ) -> None:
28 super().__init__(
29 value=value,
30 max=max_value,
31 label=label,
32 show_percentage=show_percentage,
33 size=size,
34 **props,
35 )
36 self.value = value
37 self.max = max_value
38 self.label = label
39 self.show_percentage = show_percentage
40 self.size = size
42 def render(self) -> Any:
43 percentage = int((self.value / self.max) * 100) if self.max > 0 else 0
45 size_map = {"sm": "h-1", "md": "h-2", "lg": "h-3"}
47 header = ""
48 if self.label or self.show_percentage:
49 header = el(
50 "div",
51 el(
52 "span",
53 self.label or "",
54 class_="text-sm font-medium text-foreground opacity-70",
55 )
56 if self.label
57 else "",
58 el(
59 "span",
60 f"{percentage}%",
61 class_="text-sm font-medium text-foreground opacity-70",
62 )
63 if self.show_percentage
64 else "",
65 class_="flex justify-between mb-1",
66 )
68 return el(
69 "div",
70 header,
71 el(
72 "div",
73 el(
74 "div",
75 role="progressbar",
76 aria_valuenow=str(self.value),
77 aria_valuemin="0",
78 aria_valuemax=str(self.max),
79 aria_label=self.label or f"{percentage}%",
80 class_=f"bg-primary {size_map[self.size]} rounded-full transition-all duration-300",
81 style=f"width: {percentage}%",
82 ),
83 class_=f"w-full bg-muted rounded-full {size_map[self.size]} overflow-hidden",
84 ),
85 class_="w-full",
86 )