radix_leptos_primitives/components/
separator.rs

1use crate::utils::{merge_classes, generate_id};
2use leptos::children::Children;
3use leptos::prelude::*;
4
5/// Separator component - Visual dividers with orientation support
6#[component]
7pub fn Separator(
8    #[prop(optional)] class: Option<String>,
9    #[prop(optional)] style: Option<String>,
10    #[prop(optional)] children: Option<Children>,
11    #[prop(optional)] orientation: Option<SeparatorOrientation>,
12    #[prop(optional)] decorative: Option<bool>,
13    #[prop(optional)] thickness: Option<SeparatorThickness>,
14    #[prop(optional)] color: Option<String>,
15) -> impl IntoView {
16    let orientation = orientation.unwrap_or_default();
17    let decorative = decorative.unwrap_or(true);
18    let thickness = thickness.unwrap_or_default();
19    let color = color.unwrap_or_default();
20
21    let class = merge_classes(vec!["separator", orientation.to_class(), thickness.to_class()]);
22    let aria_orientation = orientation.to_aria_orientation();
23
24    view! {
25        <div
26            class=class
27            style=style
28            role=if decorative { "none" } else { "separator" }
29            aria-orientation=aria_orientation
30            data-thickness=thickness.to_string()
31            data-color=color
32        >
33            {children.map(|c| c())}
34        </div>
35    }
36}
37
38/// Separator Line component
39#[component]
40pub fn SeparatorLine(
41    #[prop(optional)] class: Option<String>,
42    #[prop(optional)] style: Option<String>,
43    #[prop(optional)] orientation: Option<SeparatorOrientation>,
44    #[prop(optional)] thickness: Option<SeparatorThickness>,
45    #[prop(optional)] color: Option<String>,
46) -> impl IntoView {
47    let orientation = orientation.unwrap_or_default();
48    let thickness = thickness.unwrap_or_default();
49    let color = color.unwrap_or_default();
50
51    let class = merge_classes(
52        [
53            "separator-line",
54            orientation.to_class(),
55            thickness.to_class(),
56            class.as_deref().unwrap_or(""),
57        ]
58        .to_vec(),
59    );
60
61    let aria_orientation = orientation.to_aria_orientation();
62
63    view! {
64        <hr
65            class=class
66            style=style
67            role="separator"
68            aria-orientation=aria_orientation
69            data-thickness=thickness.to_string()
70            data-color=color
71        />
72    }
73}
74
75/// Separator Text component
76#[component]
77pub fn SeparatorText(
78    #[prop(optional)] class: Option<String>,
79    #[prop(optional)] style: Option<String>,
80    #[prop(optional)] children: Option<Children>,
81    #[prop(optional)] text: Option<String>,
82    #[prop(optional)] orientation: Option<SeparatorOrientation>,
83) -> impl IntoView {
84    let text = text.unwrap_or_default();
85    let orientation = orientation.unwrap_or_default();
86
87    let class = merge_classes(
88        [
89            "separator-text",
90            orientation.to_class(),
91            class.as_deref().unwrap_or(""),
92        ]
93        .to_vec(),
94    );
95
96    view! {
97        <div
98            class=class
99            style=style
100            role="separator"
101            aria-label=text
102            data-orientation=orientation.to_string()
103        >
104            {children.map(|c| c())}
105        </div>
106    }
107}
108
109/// Separator Orientation enum
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
111pub enum SeparatorOrientation {
112    #[default]
113    Horizontal,
114    Vertical,
115}
116
117impl SeparatorOrientation {
118    pub fn to_class(&self) -> &'static str {
119        match self {
120            SeparatorOrientation::Horizontal => "orientation-horizontal",
121            SeparatorOrientation::Vertical => "orientation-vertical",
122        }
123    }
124
125    pub fn to_string(&self) -> &'static str {
126        match self {
127            SeparatorOrientation::Horizontal => "horizontal",
128            SeparatorOrientation::Vertical => "vertical",
129        }
130    }
131
132    pub fn to_aria_orientation(&self) -> &'static str {
133        match self {
134            SeparatorOrientation::Horizontal => "horizontal",
135            SeparatorOrientation::Vertical => "vertical",
136        }
137    }
138}
139
140/// Separator Thickness enum
141#[derive(Debug, Clone, Copy, PartialEq, Default)]
142pub enum SeparatorThickness {
143    #[default]
144    Thin,
145    Medium,
146    Thick,
147    Custom(f64),
148}
149
150impl SeparatorThickness {
151    pub fn to_class(&self) -> &'static str {
152        match self {
153            SeparatorThickness::Thin => "thickness-thin",
154            SeparatorThickness::Medium => "thickness-medium",
155            SeparatorThickness::Thick => "thickness-thick",
156            SeparatorThickness::Custom(_) => "thickness-custom",
157        }
158    }
159
160    pub fn to_string(&self) -> String {
161        match self {
162            SeparatorThickness::Thin => "thin".to_string(),
163            SeparatorThickness::Medium => "medium".to_string(),
164            SeparatorThickness::Thick => "thick".to_string(),
165            SeparatorThickness::Custom(thickness) => format!("custom-{}", thickness),
166        }
167    }
168}
169
170/// Separator Group component
171#[component]
172pub fn SeparatorGroup(
173    #[prop(optional)] class: Option<String>,
174    #[prop(optional)] style: Option<String>,
175    #[prop(optional)] children: Option<Children>,
176    #[prop(optional)] spacing: Option<SeparatorSpacing>,
177    #[prop(optional)] orientation: Option<SeparatorOrientation>,
178) -> impl IntoView {
179    let spacing = spacing.unwrap_or_default();
180    let orientation = orientation.unwrap_or_default();
181
182    let class = merge_classes(
183        [
184            "separator-group",
185            spacing.to_class(),
186            orientation.to_class(),
187            class.as_deref().unwrap_or(""),
188        ]
189        .to_vec(),
190    );
191
192    view! {
193        <div
194            class=class
195            style=style
196            role="group"
197            aria-label="Separator group"
198            data-spacing=spacing.to_string()
199            data-orientation=orientation.to_string()
200        >
201            {children.map(|c| c())}
202        </div>
203    }
204}
205
206/// Separator Spacing enum
207#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
208pub enum SeparatorSpacing {
209    #[default]
210    Tight,
211    Normal,
212    Loose,
213}
214
215impl SeparatorSpacing {
216    pub fn to_class(&self) -> &'static str {
217        match self {
218            SeparatorSpacing::Tight => "spacing-tight",
219            SeparatorSpacing::Normal => "spacing-normal",
220            SeparatorSpacing::Loose => "spacing-loose",
221        }
222    }
223
224    pub fn to_string(&self) -> &'static str {
225        match self {
226            SeparatorSpacing::Tight => "tight",
227            SeparatorSpacing::Normal => "normal",
228            SeparatorSpacing::Loose => "loose",
229        }
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use proptest::prelude::*;
236    use wasm_bindgen_test::*;
237
238    wasm_bindgen_test_configure!(run_in_browser);
239
240    // Unit Tests
241    #[test]
242    fn test_separator_creation() {}
243    #[test]
244    fn test_separator_with_class() {}
245    #[test]
246    fn test_separator_with_style() {}
247    #[test]
248    fn test_separator_orientation() {}
249    #[test]
250    fn test_separator_decorative() {}
251    #[test]
252    fn test_separator_thickness() {}
253    #[test]
254    fn test_separator_color() {}
255
256    // Separator Line tests
257    #[test]
258    fn test_separator_line_creation() {}
259    #[test]
260    fn test_separator_line_with_class() {}
261    #[test]
262    fn test_separator_line_orientation() {}
263    #[test]
264    fn test_separator_line_thickness() {}
265    #[test]
266    fn test_separator_line_color() {}
267
268    // Separator Text tests
269    #[test]
270    fn test_separator_text_creation() {}
271    #[test]
272    fn test_separator_text_with_class() {}
273    #[test]
274    fn test_separator_text_text() {}
275    #[test]
276    fn test_separator_text_orientation() {}
277
278    // Separator Orientation tests
279    #[test]
280    fn test_separator_orientation_default() {}
281    #[test]
282    fn test_separator_orientation_horizontal() {}
283    #[test]
284    fn test_separator_orientation_vertical() {}
285
286    // Separator Thickness tests
287    #[test]
288    fn test_separator_thickness_default() {}
289    #[test]
290    fn test_separator_thickness_thin() {}
291    #[test]
292    fn test_separator_thickness_medium() {}
293    #[test]
294    fn test_separator_thickness_thick() {}
295    #[test]
296    fn test_separator_thickness_custom() {}
297
298    // Separator Group tests
299    #[test]
300    fn test_separator_group_creation() {}
301    #[test]
302    fn test_separator_group_with_class() {}
303    #[test]
304    fn test_separator_group_spacing() {}
305    #[test]
306    fn test_separator_group_orientation() {}
307
308    // Separator Spacing tests
309    #[test]
310    fn test_separator_spacing_default() {}
311    #[test]
312    fn test_separator_spacing_tight() {}
313    #[test]
314    fn test_separator_spacing_normal() {}
315    #[test]
316    fn test_separator_spacing_loose() {}
317
318    // Helper function tests
319    #[test]
320    fn test_merge_classes_empty() {}
321    #[test]
322    fn test_merge_classes_single() {}
323    #[test]
324    fn test_merge_classes_multiple() {}
325    #[test]
326    fn test_merge_classes_with_empty() {}
327
328    // Property-based Tests
329    #[test]
330    fn test_separator_property_based() {
331        proptest!(|(____class in ".*", __style in ".*")| {
332
333        });
334    }
335
336    #[test]
337    fn test_separator_thickness_validation() {
338        proptest!(|(____thickness in 1.0..20.0f64)| {
339
340        });
341    }
342
343    #[test]
344    fn test_separator_orientation_validation() {
345        proptest!(|(____orientation in ".*")| {
346
347        });
348    }
349
350    // Integration Tests
351    #[test]
352    fn test_separator_accessibility() {}
353    #[test]
354    fn test_separator_orientation_behavior() {}
355    #[test]
356    fn test_separator_thickness_rendering() {}
357    #[test]
358    fn test_separator_group_layout() {}
359    #[test]
360    fn test_separator_responsive_behavior() {}
361
362    // Performance Tests
363    #[test]
364    fn test_separator_large_groups() {}
365    #[test]
366    fn test_separator_render_performance() {}
367    #[test]
368    fn test_separator_memory_usage() {}
369    #[test]
370    fn test_separator_layout_performance() {}
371}