radix_leptos_primitives/components/
alert.rs

1use leptos::*;
2use leptos::prelude::*;
3
4/// Alert variant for different message types
5#[derive(Clone, Copy, PartialEq, Eq, Hash)]
6pub enum AlertVariant {
7    Default,
8    Success,
9    Error,
10    Warning,
11    Info,
12}
13
14/// Alert size variant
15#[derive(Clone, Copy, PartialEq, Eq, Hash)]
16pub enum AlertSize {
17    Small,
18    Medium,
19    Large,
20}
21
22/// Merge CSS classes with proper spacing
23fn merge_classes(classes: &[&str]) -> String {
24    classes.iter().filter(|&&c| !c.is_empty()).map(|&s| s).collect::<Vec<&str>>().join(" ")
25}
26
27/// Root Alert component
28#[component]
29pub fn Alert(
30    /// Alert variant
31    #[prop(optional, default = AlertVariant::Default)]
32    variant: AlertVariant,
33    /// Alert size
34    #[prop(optional, default = AlertSize::Medium)]
35    size: AlertSize,
36    /// Whether the alert is dismissible
37    #[prop(optional, default = false)]
38    dismissible: bool,
39    /// CSS classes
40    #[prop(optional)]
41    class: Option<String>,
42    /// Dismiss handler
43    #[prop(optional)]
44    on_dismiss: Option<Callback<web_sys::MouseEvent>>,
45    /// Child content
46    children: Children,
47) -> impl IntoView {
48    let variant_class = move || {
49        match variant {
50            AlertVariant::Default => "radix-alert--variant-default",
51            AlertVariant::Success => "radix-alert--variant-success",
52            AlertVariant::Error => "radix-alert--variant-error",
53            AlertVariant::Warning => "radix-alert--variant-warning",
54            AlertVariant::Info => "radix-alert--variant-info",
55        }
56    };
57    
58    let size_class = move || {
59        match size {
60            AlertSize::Small => "radix-alert--size-small",
61            AlertSize::Medium => "radix-alert--size-medium",
62            AlertSize::Large => "radix-alert--size-large",
63        }
64    };
65    
66    let variant_icon = move || {
67        match variant {
68            AlertVariant::Default => "ℹ️",
69            AlertVariant::Success => "✅",
70            AlertVariant::Error => "❌",
71            AlertVariant::Warning => "⚠️",
72            AlertVariant::Info => "ℹ️",
73        }
74    };
75    
76    let handle_dismiss = move |e: web_sys::MouseEvent| {
77        if let Some(callback) = on_dismiss {
78            callback.run(e);
79        }
80    };
81    
82    let class_value = class.unwrap_or_default();
83    let children_view = children();
84    let icon_clone = variant_icon();
85    
86    view! {
87        <div
88            class=merge_classes(&["radix-alert", &variant_class(), &size_class(), &class_value])
89            role="alert"
90            aria-live="polite"
91        >
92            <div class="radix-alert-content">
93                <div class="radix-alert-icon">
94                    {icon_clone}
95                </div>
96                <div class="radix-alert-body">
97                    {children_view}
98                </div>
99                <button
100                    class=move || {
101                        if dismissible {
102                            "radix-alert-close"
103                        } else {
104                            "radix-alert-close radix-alert-close--hidden"
105                        }
106                    }
107                    disabled=move || !dismissible
108                    on:click=handle_dismiss
109                    aria-label="Close alert"
110                >
111                    "×"
112                </button>
113            </div>
114        </div>
115    }
116}
117
118/// Alert title component
119#[component]
120pub fn AlertTitle(
121    /// CSS classes
122    #[prop(optional)]
123    class: Option<String>,
124    /// Child content
125    children: Children,
126) -> impl IntoView {
127    let class_value = class.unwrap_or_default();
128    let children_view = children();
129    
130    view! {
131        <h4 class=merge_classes(&["radix-alert-title", &class_value])>
132            {children_view}
133        </h4>
134    }
135}
136
137/// Alert description component
138#[component]
139pub fn AlertDescription(
140    /// CSS classes
141    #[prop(optional)]
142    class: Option<String>,
143    /// Child content
144    children: Children,
145) -> impl IntoView {
146    let class_value = class.unwrap_or_default();
147    let children_view = children();
148    
149    view! {
150        <div class=merge_classes(&["radix-alert-description", &class_value])>
151            {children_view}
152        </div>
153    }
154}
155
156/// Alert action component
157#[component]
158pub fn AlertAction(
159    /// Whether the action is disabled
160    #[prop(optional, default = false)]
161    disabled: bool,
162    /// CSS classes
163    #[prop(optional)]
164    class: Option<String>,
165    /// Click handler
166    #[prop(optional)]
167    on_click: Option<Callback<web_sys::MouseEvent>>,
168    /// Child content
169    children: Children,
170) -> impl IntoView {
171    let handle_click = move |e: web_sys::MouseEvent| {
172        if !disabled {
173            if let Some(callback) = on_click {
174                callback.run(e);
175            }
176        }
177    };
178    
179    let class_value = class.unwrap_or_default();
180    let children_view = children();
181    
182    view! {
183        <button
184            class=merge_classes(&["radix-alert-action", &class_value])
185            disabled=disabled
186            on:click=handle_click
187        >
188            {children_view}
189        </button>
190    }
191}