Skip to main content

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}