radix_leptos_primitives/components/
label.rs

1use crate::utils::merge_classes;
2use leptos::callback::Callback;
3use leptos::children::Children;
4use leptos::prelude::*;
5
6/// Label component - Form labels with accessibility features
7#[component]
8pub fn Label(
9    #[prop(optional)] class: Option<String>,
10    #[prop(optional)] style: Option<String>,
11    #[prop(optional)] children: Option<Children>,
12    #[prop(optional)] for_id: Option<String>,
13    #[prop(optional)] required: Option<bool>,
14    #[prop(optional)] disabled: Option<bool>,
15    #[prop(optional)] size: Option<LabelSize>,
16    #[prop(optional)] variant: Option<LabelVariant>,
17    #[prop(optional)] on_click: Option<Callback<()>>,
18) -> impl IntoView {
19    let for_id = for_id.unwrap_or_default();
20    let required = required.unwrap_or(false);
21    let disabled = disabled.unwrap_or(false);
22    let size = size.unwrap_or_default();
23    let variant = variant.unwrap_or_default();
24
25    let class = merge_classes(vec![
26        "label",
27        &size.to_class(),
28        &variant.to_class(),
29        class.as_deref().unwrap_or(""),
30    ]);
31
32    let handle_click = move |_| {
33        if !disabled {
34            if let Some(callback) = on_click {
35                callback.run(());
36            }
37        }
38    };
39
40    view! {
41        <label
42            class=class
43            style=style
44            for=for_id
45            aria-required=required
46            aria-disabled=disabled
47            on:click=handle_click
48        >
49            {children.map(|c| c())}
50        </label>
51    }
52}
53
54/// Label Text component
55#[component]
56pub fn LabelText(
57    #[prop(optional)] class: Option<String>,
58    #[prop(optional)] style: Option<String>,
59    #[prop(optional)] children: Option<Children>,
60    #[prop(optional)] text: Option<String>,
61    #[prop(optional)] required: Option<bool>,
62) -> impl IntoView {
63    let text = text.unwrap_or_default();
64    let required = required.unwrap_or(false);
65
66    let class = merge_classes(vec!["label-text"]);
67}
68
69/// Label Description component
70#[component]
71pub fn LabelDescription(
72    #[prop(optional)] class: Option<String>,
73    #[prop(optional)] style: Option<String>,
74    #[prop(optional)] children: Option<Children>,
75    #[prop(optional)] description: Option<String>,
76) -> impl IntoView {
77    let description = description.unwrap_or_default();
78
79    let class = merge_classes(vec!["label-description", class.as_deref().unwrap_or("")]);
80
81    view! {
82        <div
83            class=class
84            style=style
85            role="text"
86            aria-label="Label description"
87        >
88            {children.map(|c| c())}
89        </div>
90    }
91}
92
93/// Label Error component
94#[component]
95pub fn LabelError(
96    #[prop(optional)] class: Option<String>,
97    #[prop(optional)] style: Option<String>,
98    #[prop(optional)] children: Option<Children>,
99    #[prop(optional)] error: Option<String>,
100    #[prop(optional)] visible: Option<bool>,
101) -> impl IntoView {
102    let error = error.unwrap_or_default();
103    let visible = visible.unwrap_or(false);
104
105    let class = merge_classes(vec!["label-error"]);
106}
107
108/// Label Group component
109#[component]
110pub fn LabelGroup(
111    #[prop(optional)] class: Option<String>,
112    #[prop(optional)] style: Option<String>,
113    #[prop(optional)] children: Option<Children>,
114    #[prop(optional)] orientation: Option<LabelOrientation>,
115    #[prop(optional)] spacing: Option<LabelSpacing>,
116) -> impl IntoView {
117    let orientation = orientation.unwrap_or_default();
118    let spacing = spacing.unwrap_or_default();
119
120    let class = merge_classes(vec![
121        "label-group",
122        &orientation.to_class(),
123        &spacing.to_class(),
124        class.as_deref().unwrap_or(""),
125    ]);
126
127    view! {
128        <div
129            class=class
130            style=style
131            role="group"
132            aria-label="Label group"
133            data-orientation=orientation.to_string()
134            data-spacing=spacing.to_string()
135        >
136            {children.map(|c| c())}
137        </div>
138    }
139}
140
141/// Label Size enum
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
143pub enum LabelSize {
144    #[default]
145    Small,
146    Medium,
147    Large,
148}
149
150impl LabelSize {
151    pub fn to_class(&self) -> &'static str {
152        match self {
153            LabelSize::Small => "size-small",
154            LabelSize::Medium => "size-medium",
155            LabelSize::Large => "size-large",
156        }
157    }
158
159    pub fn to_string(&self) -> &'static str {
160        match self {
161            LabelSize::Small => "small",
162            LabelSize::Medium => "medium",
163            LabelSize::Large => "large",
164        }
165    }
166}
167
168/// Label Variant enum
169#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
170pub enum LabelVariant {
171    #[default]
172    Default,
173    Primary,
174    Secondary,
175    Success,
176    Warning,
177    Error,
178}
179
180impl LabelVariant {
181    pub fn to_class(&self) -> &'static str {
182        match self {
183            LabelVariant::Default => "variant-default",
184            LabelVariant::Primary => "variant-primary",
185            LabelVariant::Secondary => "variant-secondary",
186            LabelVariant::Success => "variant-success",
187            LabelVariant::Warning => "variant-warning",
188            LabelVariant::Error => "variant-error",
189        }
190    }
191
192    pub fn to_string(&self) -> &'static str {
193        match self {
194            LabelVariant::Default => "default",
195            LabelVariant::Primary => "primary",
196            LabelVariant::Secondary => "secondary",
197            LabelVariant::Success => "success",
198            LabelVariant::Warning => "warning",
199            LabelVariant::Error => "error",
200        }
201    }
202}
203
204/// Label Orientation enum
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
206pub enum LabelOrientation {
207    #[default]
208    Horizontal,
209    Vertical,
210}
211
212impl LabelOrientation {
213    pub fn to_class(&self) -> &'static str {
214        match self {
215            LabelOrientation::Horizontal => "orientation-horizontal",
216            LabelOrientation::Vertical => "orientation-vertical",
217        }
218    }
219
220    pub fn to_string(&self) -> &'static str {
221        match self {
222            LabelOrientation::Horizontal => "horizontal",
223            LabelOrientation::Vertical => "vertical",
224        }
225    }
226}
227
228/// Label Spacing enum
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
230pub enum LabelSpacing {
231    #[default]
232    Tight,
233    Normal,
234    Loose,
235}
236
237impl LabelSpacing {
238    pub fn to_class(&self) -> &'static str {
239        match self {
240            LabelSpacing::Tight => "spacing-tight",
241            LabelSpacing::Normal => "spacing-normal",
242            LabelSpacing::Loose => "spacing-loose",
243        }
244    }
245
246    pub fn to_string(&self) -> &'static str {
247        match self {
248            LabelSpacing::Tight => "tight",
249            LabelSpacing::Normal => "normal",
250            LabelSpacing::Loose => "loose",
251        }
252    }
253}
254
255#[cfg(test)]
256mod tests {
257    use proptest::prelude::*;
258    use wasm_bindgen_test::*;
259
260    wasm_bindgen_test_configure!(run_in_browser);
261
262    // Unit Tests
263    #[test]
264    fn test_label_creation() {}
265    #[test]
266    fn test_label_with_class() {}
267    #[test]
268    fn test_label_with_style() {}
269    #[test]
270    fn test_label_for_id() {}
271    #[test]
272    fn test_labelrequired() {}
273    #[test]
274    fn test_labeldisabled() {}
275    #[test]
276    fn test_label_size() {}
277    #[test]
278    fn test_label_variant() {}
279    #[test]
280    fn test_label_on_click() {}
281
282    // Label Text tests
283    #[test]
284    fn test_label_text_creation() {}
285    #[test]
286    fn test_label_text_with_class() {}
287    #[test]
288    fn test_label_text_text() {}
289    #[test]
290    fn test_label_textrequired() {}
291
292    // Label Description tests
293    #[test]
294    fn test_label_description_creation() {}
295    #[test]
296    fn test_label_description_with_class() {}
297    #[test]
298    fn test_label_description_description() {}
299
300    // Label Error tests
301    #[test]
302    fn test_label_error_creation() {}
303    #[test]
304    fn test_label_error_with_class() {}
305    #[test]
306    fn test_label_error_error() {}
307    #[test]
308    fn test_label_errorvisible() {}
309
310    // Label Group tests
311    #[test]
312    fn test_label_group_creation() {}
313    #[test]
314    fn test_label_group_with_class() {}
315    #[test]
316    fn test_label_group_orientation() {}
317    #[test]
318    fn test_label_group_spacing() {}
319
320    // Label Size tests
321    #[test]
322    fn test_label_size_default() {}
323    #[test]
324    fn test_label_size_small() {}
325    #[test]
326    fn test_label_size_medium() {}
327    #[test]
328    fn test_label_size_large() {}
329
330    // Label Variant tests
331    #[test]
332    fn test_label_variant_default() {}
333    #[test]
334    fn test_label_variant_primary() {}
335    #[test]
336    fn test_label_variant_secondary() {}
337    #[test]
338    fn test_label_variant_success() {}
339    #[test]
340    fn test_label_variant_warning() {}
341    #[test]
342    fn test_label_variant_error() {}
343
344    // Label Orientation tests
345    #[test]
346    fn test_label_orientation_default() {}
347    #[test]
348    fn test_label_orientation_horizontal() {}
349    #[test]
350    fn test_label_orientation_vertical() {}
351
352    // Label Spacing tests
353    #[test]
354    fn test_label_spacing_default() {}
355    #[test]
356    fn test_label_spacing_tight() {}
357    #[test]
358    fn test_label_spacing_normal() {}
359    #[test]
360    fn test_label_spacing_loose() {}
361
362    // Helper function tests
363    #[test]
364    fn test_merge_classes_empty() {}
365    #[test]
366    fn test_merge_classes_single() {}
367    #[test]
368    fn test_merge_classes_multiple() {}
369    #[test]
370    fn test_merge_classes_with_empty() {}
371
372    // Property-based Tests
373    #[test]
374    fn test_label_property_based() {
375        proptest!(|(____class in ".*", __style in ".*")| {
376
377        });
378    }
379
380    #[test]
381    fn test_label_accessibility_validation() {
382        proptest!(|(__for_id in ".*", _required: bool, _disabled: bool)| {
383
384        });
385    }
386
387    #[test]
388    fn test_label_variant_validation() {
389        proptest!(|(____variant in ".*")| {
390
391        });
392    }
393
394    // Integration Tests
395    #[test]
396    fn test_label_accessibility() {}
397    #[test]
398    fn test_label_form_integration() {}
399    #[test]
400    fn test_label_validation_workflow() {}
401    #[test]
402    fn test_label_error_display() {}
403    #[test]
404    fn test_label_responsive_behavior() {}
405
406    // Performance Tests
407    #[test]
408    fn test_label_large_forms() {}
409    #[test]
410    fn test_label_render_performance() {}
411    #[test]
412    fn test_label_memory_usage() {}
413    #[test]
414    fn test_label_validation_performance() {}
415}