Coverage for src / lexigram / ui / molecules / card.py: 100%
41 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.atoms.button import Button
6from lexigram.ui.core.base import Component, el, raw, render_to_string
9class Card(Component):
10 """Refined Card component for better aesthetics."""
12 def __init__(
13 self,
14 title: str | Any | None = None,
15 content: str | Any | None = None,
16 footer: Any = None,
17 *,
18 as_child: bool = False,
19 **props,
20 ) -> None:
21 super().__init__(
22 title=title, content=content, footer=footer, as_child=as_child, **props
23 )
24 self.title = title
25 self.content = content
26 self.footer = footer
28 def render(self) -> Any:
29 # Merge classes
30 cls = "bg-card text-card-foreground shadow rounded-xl overflow-hidden mb-6 border border-border transition-colors duration-300"
31 custom_cls = self.props.get("class_", self.props.get("class"))
32 if custom_cls:
33 cls = f"{cls} {custom_cls}"
35 # Clean attrs
36 attrs = {
37 k: v
38 for k, v in self.props.items()
39 if k not in ("title", "content", "footer", "actions", "class_", "class")
40 }
42 # Header
43 header = None
44 if self.title:
45 title_content = self.title
46 if not hasattr(title_content, "__html__"):
47 title_content = str(title_content)
48 header = el(
49 "div",
50 title_content,
51 class_="px-6 py-4 border-b border-border font-semibold text-card-foreground card-header",
52 )
54 # Body
55 body_content = self.content or ""
56 if not hasattr(body_content, "__html__"):
57 body_content = render_to_string(body_content)
59 # Add any children from Streamlit-style usage
60 if self.children:
61 body_content += "".join(render_to_string(c) for c in self.children)
63 body = el("div", raw(body_content), class_="px-6 py-4 card-body")
65 # Footer / Actions (for backward compatibility)
66 footer_el = None
67 actions = self.props.get("actions", [])
69 if self.footer or actions:
70 footer_children = []
71 if self.footer:
72 footer_children.append(self.footer)
74 for a in actions:
75 if hasattr(a, "__html__") or (
76 hasattr(a, "render") and callable(a.render)
77 ):
78 footer_children.append(a)
79 else:
80 # Render string actions as buttons
81 footer_children.append(
82 Button(str(a), color="primary", class_="mx-1"),
83 )
85 footer_content = raw("".join(render_to_string(c) for c in footer_children))
86 footer_el = el(
87 "div",
88 footer_content,
89 class_="px-6 py-4 bg-muted border-t border-border card-footer",
90 )
92 return el("div", header, body, footer_el, class_=cls, **attrs)