Coverage for src / lexigram / ui / atoms / skeleton.py: 100%

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 

7 

8class Skeleton(Component): 

9 """Skeleton loader placeholder for content. 

10 

11 Args: 

12 variant: Shape variant (text, circular, rectangular) 

13 width: CSS width value 

14 height: CSS height value 

15 count: Number of skeleton lines (for text variant) 

16 """ 

17 

18 def __init__( 

19 self, 

20 variant: Literal["text", "circular", "rectangular", "table"] = "text", 

21 width: str = "100%", 

22 height: str | None = None, 

23 count: int = 1, 

24 **props, 

25 ) -> None: 

26 super().__init__( 

27 variant=variant, 

28 width=width, 

29 height=height, 

30 count=count, 

31 **props, 

32 ) 

33 self.variant = variant 

34 self.width = width 

35 self.height = height 

36 self.count = count 

37 

38 def render(self) -> Any: 

39 if self.variant == "text": 

40 items = [] 

41 for i in range(self.count): 

42 # Last item is shorter 

43 item_width = "80%" if i == self.count - 1 else self.width 

44 items.append( 

45 el( 

46 "div", 

47 class_="h-4 bg-muted rounded animate-pulse mb-2", 

48 style=f"width: {item_width}", 

49 ), 

50 ) 

51 return el("div", *items, class_="space-y-2", aria_hidden="true") 

52 

53 if self.variant == "circular": 

54 size = self.height or "3rem" 

55 return el( 

56 "div", 

57 class_="bg-muted rounded-full animate-pulse", 

58 style=f"width: {size}; height: {size}", 

59 aria_hidden="true", 

60 ) 

61 

62 if self.variant == "table": 

63 items = [] 

64 for _ in range(self.count): 

65 items.append( 

66 el( 

67 "div", 

68 class_="h-8 bg-muted rounded animate-pulse mb-2", 

69 style=f"width: {self.width}", 

70 ), 

71 ) 

72 return el("div", *items, class_="space-y-1", aria_hidden="true") 

73 

74 # rectangular 

75 h = self.height or "8rem" 

76 return el( 

77 "div", 

78 class_="bg-muted rounded animate-pulse", 

79 style=f"width: {self.width}; height: {h}", 

80 aria_hidden="true", 

81 )