radix_leptos_primitives/components/
aspect_ratio.rs1use crate::utils::merge_classes;
2use leptos::children::Children;
3use leptos::prelude::*;
4
5#[component]
7pub fn AspectRatio(
8 #[prop(optional)] class: Option<String>,
9 #[prop(optional)] style: Option<String>,
10 #[prop(optional)] children: Option<Children>,
11 #[prop(optional)] ratio: Option<f64>,
12 #[prop(optional)] width: Option<f64>,
13 #[prop(optional)] height: Option<f64>,
14 #[prop(optional)] min_width: Option<f64>,
15 #[prop(optional)] max_width: Option<f64>,
16 #[prop(optional)] min_height: Option<f64>,
17 #[prop(optional)] max_height: Option<f64>,
18) -> impl IntoView {
19 let ratio = ratio.unwrap_or(16.0 / 9.0);
20 let width = width.unwrap_or(100.0);
21 let height = height.unwrap_or(width / ratio);
22
23 let class = merge_classes(vec!["aspect-ratio", class.as_deref().unwrap_or("")]);
24
25 let container_style = format!(
26 "position: relative; width: {}%; padding-bottom: {}%; {}",
27 width,
28 (height / width) * 100.0,
29 style.unwrap_or_default()
30 );
31
32 let content_style = format!(
33 "position: absolute; top: 0; left: 0; width: 100%; height: 100%; min-width: {}px; max-width: {}px; min-height: {}px; max-height: {}px;",
34 min_width.unwrap_or(0.0),
35 max_width.unwrap_or(f64::INFINITY),
36 min_height.unwrap_or(0.0),
37 max_height.unwrap_or(f64::INFINITY)
38 );
39
40 view! {
41 <div
42 class=class
43 style=container_style
44 role="img"
45 aria-label="Aspect ratio container"
46 data-ratio=ratio
47 data-width=width
48 data-height=height
49 >
50 <div
51 class="aspect-ratio-content"
52 style=content_style
53 >
54 {children.map(|c| c())}
55 </div>
56 </div>
57 }
58}
59
60#[component]
62pub fn AspectRatioContainer(
63 #[prop(optional)] class: Option<String>,
64 #[prop(optional)] style: Option<String>,
65 #[prop(optional)] children: Option<Children>,
66 #[prop(optional)] ratio: Option<f64>,
67) -> impl IntoView {
68 let ratio = ratio.unwrap_or(16.0 / 9.0);
69
70 let class = merge_classes(vec![
71 "aspect-ratio-container",
72 class.as_deref().unwrap_or(""),
73 ]);
74
75 let style = format!("aspect-ratio: {} / 1; {}", ratio, style.unwrap_or_default());
76
77 view! {
78 <div
79 class=class
80 style=style
81 role="img"
82 aria-label="Aspect ratio container"
83 data-ratio=ratio
84 >
85 {children.map(|c| c())}
86 </div>
87 }
88}
89
90#[component]
92pub fn AspectRatioWrapper(
93 #[prop(optional)] class: Option<String>,
94 #[prop(optional)] style: Option<String>,
95 #[prop(optional)] children: Option<Children>,
96 #[prop(optional)] ratio: Option<f64>,
97 #[prop(optional)] fit: Option<AspectRatioFit>,
98) -> impl IntoView {
99 let ratio = ratio.unwrap_or(16.0 / 9.0);
100 let fit = fit.unwrap_or_default();
101
102 let class = merge_classes(vec![
103 "aspect-ratio-wrapper",
104 &fit.to_class(),
105 class.as_deref().unwrap_or(""),
106 ]);
107
108 let style = format!("aspect-ratio: {} / 1; {}", ratio, style.unwrap_or_default());
109
110 view! {
111 <div
112 class=class
113 style=style
114 role="img"
115 aria-label="Aspect ratio wrapper"
116 data-ratio=ratio
117 data-fit=fit.to_string()
118 >
119 {children.map(|c| c())}
120 </div>
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
126pub enum AspectRatioFit {
127 #[default]
128 Cover,
129 Contain,
130 Fill,
131 ScaleDown,
132 None,
133}
134
135impl AspectRatioFit {
136 pub fn to_class(&self) -> &'static str {
137 match self {
138 AspectRatioFit::Cover => "fit-cover",
139 AspectRatioFit::Contain => "fit-contain",
140 AspectRatioFit::Fill => "fit-fill",
141 AspectRatioFit::ScaleDown => "fit-scale-down",
142 AspectRatioFit::None => "fit-none",
143 }
144 }
145
146 pub fn to_string(&self) -> &'static str {
147 match self {
148 AspectRatioFit::Cover => "cover",
149 AspectRatioFit::Contain => "contain",
150 AspectRatioFit::Fill => "fill",
151 AspectRatioFit::ScaleDown => "scale-down",
152 AspectRatioFit::None => "none",
153 }
154 }
155}
156
157#[cfg(test)]
160mod tests {
161 use proptest::prelude::*;
162 use wasm_bindgen_test::*;
163
164 wasm_bindgen_test_configure!(run_in_browser);
165
166 #[test]
168 fn test_aspect_ratio_creation() {}
169 #[test]
170 fn test_aspect_ratio_with_class() {}
171 #[test]
172 fn test_aspect_ratio_with_style() {}
173 #[test]
174 fn test_aspect_ratio_default_ratio() {}
175 #[test]
176 fn test_aspect_ratio_custom_ratio() {}
177 #[test]
178 fn test_aspect_ratio_width_height() {}
179 #[test]
180 fn test_aspect_ratio_min_max_constraints() {}
181
182 #[test]
184 fn test_aspect_ratio_container_creation() {}
185 #[test]
186 fn test_aspect_ratio_container_with_class() {}
187 #[test]
188 fn test_aspect_ratio_container_custom_ratio() {}
189
190 #[test]
192 fn test_aspect_ratio_wrapper_creation() {}
193 #[test]
194 fn test_aspect_ratio_wrapper_with_class() {}
195 #[test]
196 fn test_aspect_ratio_wrapper_custom_ratio() {}
197 #[test]
198 fn test_aspect_ratio_wrapper_fit_options() {}
199
200 #[test]
202 fn test_aspect_ratio_fit_default() {}
203 #[test]
204 fn test_aspect_ratio_fit_cover() {}
205 #[test]
206 fn test_aspect_ratio_fit_contain() {}
207 #[test]
208 fn test_aspect_ratio_fit_fill() {}
209 #[test]
210 fn test_aspect_ratio_fit_scale_down() {}
211 #[test]
212 fn test_aspect_ratio_fit_none() {}
213
214 #[test]
216 fn test_merge_classes_empty() {}
217 #[test]
218 fn test_merge_classes_single() {}
219 #[test]
220 fn test_merge_classes_multiple() {}
221 #[test]
222 fn test_merge_classes_with_empty() {}
223
224 #[test]
226 fn test_aspect_ratio_property_based() {
227 proptest!(|(____class in ".*", __style in ".*")| {
228
229 });
230 }
231
232 #[test]
233 fn test_aspect_ratio_validation() {
234 proptest!(|(____ratio in 0.1..10.0f64, __width in 10.0..1000.0f64)| {
235
236 });
237 }
238
239 #[test]
240 fn test_aspect_ratio_constraints_validation() {
241 proptest!(|(____min_width in 0.0..500.0f64, __max_width in 500.0..2000.0f64, __min_height in 0.0..500.0f64, __max_height in 500.0..2000.0f64)| {
242
243 });
244 }
245
246 #[test]
248 fn test_aspect_ratio_responsive_behavior() {}
249 #[test]
250 fn test_aspect_ratio_content_fitting() {}
251 #[test]
252 fn test_aspect_ratio_constraint_handling() {}
253 #[test]
254 fn test_aspect_ratio_nested_components() {}
255 #[test]
256 fn test_aspect_ratio_different_ratios() {}
257
258 #[test]
260 fn test_aspect_ratio_large_content() {}
261 #[test]
262 fn test_aspect_ratio_render_performance() {}
263 #[test]
264 fn test_aspect_ratio_memory_usage() {}
265 #[test]
266 fn test_aspect_ratio_resize_performance() {}
267}