dioxus_bootstrap_css/card.rs
1use dioxus::prelude::*;
2
3/// Bootstrap Card component with optional header, body, and footer slots.
4///
5/// # Bootstrap HTML → Dioxus
6///
7/// ```html
8/// <!-- Bootstrap HTML -->
9/// <div class="card">
10/// <div class="card-header">Title</div>
11/// <div class="card-body"><p>Content</p></div>
12/// <div class="card-footer">Footer</div>
13/// </div>
14/// ```
15///
16/// ```rust,no_run
17/// // Dioxus equivalent
18/// rsx! {
19/// Card {
20/// header: rsx! { "Card Title" },
21/// body: rsx! { p { "Card content goes here." } },
22/// footer: rsx! { "Last updated 3 mins ago" },
23/// }
24/// // Body-only card
25/// Card { body: rsx! { "Simple card" } }
26/// // Card with custom header styling (e.g., flex layout with action buttons)
27/// Card {
28/// class: "mb-3",
29/// header_class: "d-flex justify-content-between align-items-center py-2",
30/// body_class: "py-2",
31/// header: rsx! {
32/// span { class: "small", "Server" }
33/// button { class: "btn btn-sm btn-outline-secondary py-0 px-1",
34/// i { class: "bi bi-arrow-clockwise small" }
35/// }
36/// },
37/// body: rsx! { p { "Stats here" } },
38/// }
39/// // Custom layout (children go inside card, outside body)
40/// Card { class: "text-center",
41/// img { class: "card-img-top", src: "/photo.jpg" }
42/// div { class: "card-body", h5 { "Title" } p { "Text" } }
43/// }
44/// }
45/// ```
46#[derive(Clone, PartialEq, Props)]
47pub struct CardProps {
48 /// Card header content.
49 #[props(default)]
50 pub header: Option<Element>,
51 /// Card body content.
52 #[props(default)]
53 pub body: Option<Element>,
54 /// Card footer content.
55 #[props(default)]
56 pub footer: Option<Element>,
57 /// Additional CSS classes for the card container.
58 #[props(default)]
59 pub class: String,
60 /// Additional CSS classes for the card-header div.
61 #[props(default)]
62 pub header_class: String,
63 /// Additional CSS classes for the card body.
64 #[props(default)]
65 pub body_class: String,
66 /// Additional CSS classes for the card-footer div.
67 #[props(default)]
68 pub footer_class: String,
69 /// Child elements (rendered inside card, outside body — for custom layouts).
70 #[props(default)]
71 pub children: Element,
72}
73
74#[component]
75pub fn Card(props: CardProps) -> Element {
76 let full_class = if props.class.is_empty() {
77 "card".to_string()
78 } else {
79 format!("card {}", props.class)
80 };
81
82 let header_class = if props.header_class.is_empty() {
83 "card-header".to_string()
84 } else {
85 format!("card-header {}", props.header_class)
86 };
87
88 let body_class = if props.body_class.is_empty() {
89 "card-body".to_string()
90 } else {
91 format!("card-body {}", props.body_class)
92 };
93
94 let footer_class = if props.footer_class.is_empty() {
95 "card-footer".to_string()
96 } else {
97 format!("card-footer {}", props.footer_class)
98 };
99
100 rsx! {
101 div { class: "{full_class}",
102 if let Some(header) = props.header {
103 div { class: "{header_class}", {header} }
104 }
105 if let Some(body) = props.body {
106 div { class: "{body_class}", {body} }
107 }
108 {props.children}
109 if let Some(footer) = props.footer {
110 div { class: "{footer_class}", {footer} }
111 }
112 }
113 }
114}