Coverage for src / lexigram / ui / atoms / link.py: 96%

27 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, Literal 

4 

5from lexigram.ui.core.base import Component, el 

6 

7LinkVariant = Literal["default", "muted", "primary"] 

8LinkSize = Literal["sm", "md", "lg"] 

9 

10 

11class Link(Component): 

12 """Create a consistent styled link element. 

13 

14 Args: 

15 label: Link text 

16 href: URL 

17 variant: Visual style variant 

18 size: Optional size (sm, md, lg) 

19 """ 

20 

21 def __init__( 

22 self, 

23 label: str, 

24 href: str, 

25 *, 

26 as_child: bool = False, 

27 variant: LinkVariant = "default", 

28 size: LinkSize | None = None, 

29 **props, 

30 ) -> None: 

31 super().__init__(as_child=as_child, **props) 

32 self.label = label 

33 self.href = href 

34 self.variant = variant 

35 self.size = size 

36 

37 def render(self) -> Any: 

38 base_classes = ["text-sm", "font-medium", "inline-flex", "items-center"] 

39 

40 if self.variant == "primary": 

41 base_classes.append("text-primary hover:text-primary/80") 

42 elif self.variant == "muted": 

43 base_classes.append( 

44 "text-muted-foreground hover:text-foreground" 

45 ) 

46 else: 

47 base_classes.append("text-foreground hover:underline") 

48 

49 if self.size == "sm": 

50 base_classes.append("text-sm") 

51 elif self.size == "lg": 

52 base_classes.append("text-lg") 

53 

54 custom_cls = self.props.get("class_") or self.props.get("class") 

55 cls = " ".join(base_classes) + (f" {custom_cls}" if custom_cls else "") 

56 

57 extra_attrs = { 

58 k: v 

59 for k, v in self.props.items() 

60 if k not in ("class_", "class", "children") 

61 } 

62 return el("a", self.label, href=self.href, class_=cls, **extra_attrs)