dioxus_bootstrap_css/figure.rs
1use dioxus::prelude::*;
2
3/// Bootstrap Figure component — image with optional caption.
4///
5/// # Bootstrap HTML → Dioxus
6///
7/// ```html
8/// <!-- Bootstrap HTML -->
9/// <figure class="figure">
10/// <img src="/photo.jpg" class="figure-img img-fluid rounded" alt="Photo">
11/// <figcaption class="figure-caption text-end">A caption.</figcaption>
12/// </figure>
13/// ```
14///
15/// ```rust,no_run
16/// rsx! {
17/// Figure { src: "/photo.jpg", alt: "A photo",
18/// caption: "A caption for the image.",
19/// caption_align: "end",
20/// rounded: true,
21/// }
22/// }
23/// ```
24///
25/// # Props
26///
27/// - `src` — image URL
28/// - `alt` — alt text
29/// - `caption` / `caption_align` — caption text and alignment
30/// - `fluid` — responsive image (default: true)
31/// - `rounded` — rounded corners
32/// - `thumbnail` — thumbnail border
33#[derive(Clone, PartialEq, Props)]
34pub struct FigureProps {
35 /// Image source URL.
36 pub src: String,
37 /// Alt text for the image.
38 #[props(default)]
39 pub alt: String,
40 /// Caption text.
41 #[props(default)]
42 pub caption: String,
43 /// Align caption (start, center, end).
44 #[props(default)]
45 pub caption_align: String,
46 /// Make the image fluid (responsive).
47 #[props(default = true)]
48 pub fluid: bool,
49 /// Add rounded corners to the image.
50 #[props(default)]
51 pub rounded: bool,
52 /// Add thumbnail border to the image.
53 #[props(default)]
54 pub thumbnail: bool,
55 /// Additional CSS classes for the figure.
56 #[props(default)]
57 pub class: String,
58 /// Additional CSS classes for the image.
59 #[props(default)]
60 pub img_class: String,
61}
62
63#[component]
64pub fn Figure(props: FigureProps) -> Element {
65 let fig_class = if props.class.is_empty() {
66 "figure".to_string()
67 } else {
68 format!("figure {}", props.class)
69 };
70
71 let mut img_classes = vec!["figure-img".to_string()];
72 if props.fluid {
73 img_classes.push("img-fluid".to_string());
74 }
75 if props.rounded {
76 img_classes.push("rounded".to_string());
77 }
78 if props.thumbnail {
79 img_classes.push("img-thumbnail".to_string());
80 }
81 if !props.img_class.is_empty() {
82 img_classes.push(props.img_class.clone());
83 }
84 let img_class = img_classes.join(" ");
85
86 let caption_class = if props.caption_align.is_empty() {
87 "figure-caption".to_string()
88 } else {
89 format!("figure-caption text-{}", props.caption_align)
90 };
91
92 rsx! {
93 figure { class: "{fig_class}",
94 img {
95 class: "{img_class}",
96 src: "{props.src}",
97 alt: "{props.alt}",
98 }
99 if !props.caption.is_empty() {
100 figcaption { class: "{caption_class}", "{props.caption}" }
101 }
102 }
103 }
104}
105
106/// Bootstrap responsive embed / aspect ratio wrapper.
107///
108/// # Bootstrap HTML → Dioxus
109///
110/// ```html
111/// <!-- Bootstrap HTML -->
112/// <div class="ratio ratio-16x9">
113/// <iframe src="https://www.youtube.com/embed/..."></iframe>
114/// </div>
115/// ```
116///
117/// ```rust,no_run
118/// rsx! {
119/// Ratio { aspect: "16x9",
120/// iframe { src: "https://www.youtube.com/embed/..." }
121/// }
122/// Ratio { aspect: "1x1",
123/// div { class: "bg-primary text-white d-flex align-items-center justify-content-center", "1:1" }
124/// }
125/// }
126/// ```
127///
128/// # Props
129///
130/// - `aspect` — "1x1", "4x3", "16x9", "21x9"
131#[derive(Clone, PartialEq, Props)]
132pub struct RatioProps {
133 /// Aspect ratio: "1x1", "4x3", "16x9", "21x9".
134 #[props(default = "16x9".to_string())]
135 pub aspect: String,
136 /// Additional CSS classes.
137 #[props(default)]
138 pub class: String,
139 /// Child element (iframe, video, embed, etc.).
140 pub children: Element,
141}
142
143#[component]
144pub fn Ratio(props: RatioProps) -> Element {
145 let full_class = if props.class.is_empty() {
146 format!("ratio ratio-{}", props.aspect)
147 } else {
148 format!("ratio ratio-{} {}", props.aspect, props.class)
149 };
150
151 rsx! {
152 div { class: "{full_class}",
153 {props.children}
154 }
155 }
156}