patternfly_yew/components/
alert.rs

1//! Alert popup
2
3use crate::ouia;
4use crate::prelude::wrap::wrapper_elt_with_attributes;
5use crate::prelude::{Action, Button, ButtonVariant, Icon};
6use crate::utils::{Ouia, OuiaComponentType, OuiaSafe};
7use yew::prelude::*;
8use yew::virtual_dom::ApplyAttributeAs;
9
10const OUIA: Ouia = ouia!("Alert");
11
12#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
13pub enum AlertType {
14    #[default]
15    Custom,
16    Info,
17    Success,
18    Warning,
19    Danger,
20}
21
22impl AlertType {
23    pub fn as_classes(&self) -> Vec<&'static str> {
24        match self {
25            AlertType::Custom => vec!["pf-m-custom"],
26            AlertType::Info => vec!["pf-m-info"],
27            AlertType::Success => vec!["pf-m-success"],
28            AlertType::Warning => vec!["pf-m-warning"],
29            AlertType::Danger => vec!["pf-m-danger"],
30        }
31    }
32
33    pub fn aria_label(&self) -> &'static str {
34        match self {
35            AlertType::Custom => "Custom alert",
36            AlertType::Info => "Information alert",
37            AlertType::Success => "Success alert",
38            AlertType::Warning => "Warning alert",
39            AlertType::Danger => "Danger alert",
40        }
41    }
42
43    pub fn icon(&self) -> Icon {
44        match self {
45            AlertType::Custom => Icon::Bell,
46            AlertType::Info => Icon::InfoCircle,
47            AlertType::Success => Icon::CheckCircle,
48            AlertType::Warning => Icon::ExclamationTriangle,
49            AlertType::Danger => Icon::ExclamationCircle,
50        }
51    }
52}
53
54/// Properties for [`Alert`]
55#[derive(Clone, PartialEq, Properties)]
56pub struct AlertProperties {
57    #[prop_or_default]
58    pub id: String,
59    #[prop_or_default]
60    pub r#type: AlertType,
61    pub title: String,
62    #[prop_or_default]
63    pub children: Children,
64    #[prop_or_default]
65    pub inline: bool,
66    #[prop_or_default]
67    pub plain: bool,
68    #[prop_or_default]
69    pub truncate: bool,
70    #[prop_or_default]
71    pub actions: Vec<Action>,
72    #[prop_or_default]
73    pub onclose: Option<Callback<()>>,
74
75    /// OUIA Component id
76    #[prop_or_default]
77    pub ouia_id: Option<String>,
78    /// OUIA Component Type
79    #[prop_or(OUIA.component_type())]
80    pub ouia_type: OuiaComponentType,
81    /// OUIA Component Safe
82    #[prop_or(OuiaSafe::TRUE)]
83    pub ouia_safe: OuiaSafe,
84}
85
86/// Alert component
87///
88/// > An **alert** is a notification that provides brief information to the user without blocking their workflow.
89///
90/// See: <https://www.patternfly.org/components/alert>
91///
92/// ## Properties
93///
94/// Defined by [`AlertProperties`].
95#[function_component(Alert)]
96pub fn alert(props: &AlertProperties) -> Html {
97    let ouia_id = use_memo(props.ouia_id.clone(), |id| {
98        id.clone().unwrap_or(OUIA.generated_id())
99    });
100    let mut classes = classes!("pf-v5-c-alert");
101
102    classes.extend(props.r#type.as_classes());
103
104    if props.inline {
105        classes.push("pf-m-inline");
106    }
107    if props.plain {
108        classes.push("pf-m-plain");
109    }
110
111    let mut title_classes = classes!("pf-v5-c-alert__title");
112
113    if props.truncate {
114        title_classes.push("pf-m-truncate");
115    }
116
117    let t = props.r#type;
118
119    let actions = if props.actions.is_empty() {
120        html!()
121    } else {
122        html! (
123            <div class="pf-v5-c-alert__action-group">
124                {for props.actions.iter().map(|action|{
125                    html!{
126                        <Button
127                            variant={ButtonVariant::InlineLink}
128                            label={action.label.clone()}
129                            onclick={action.callback.reform(|_|())}
130                        />
131                    }
132                })}
133            </div>
134        )
135    };
136
137    html! (
138        <div
139            id={props.id.clone()}
140            class={classes}
141            aria_label={t.aria_label()}
142            data-ouia-component-id={(*ouia_id).clone()}
143            data-ouia-component-type={props.ouia_type}
144            data-ouia-safe={props.ouia_safe}
145        >
146            <div class="pf-v5-c-alert__icon">{ t.icon() }</div>
147            <p class={title_classes}>
148                <span class="pf-v5-screen-reader">{ t.aria_label() }{":"}</span>
149                { &props.title }
150            </p>
151
152
153            if let Some(onclose) = props.onclose.as_ref() {
154                <div class="pf-v5-c-alert__action">
155                    <Button variant={ButtonVariant::Plain} icon={Icon::Times} onclick={onclose.clone().reform(|_|())} />
156                </div>
157            }
158
159
160            if !props.children.is_empty() {
161                <div class="pf-v5-c-alert__description">
162                    { props.children.clone() }
163                </div>
164            }
165
166            { actions }
167
168        </div>
169    )
170}
171
172// alert group
173
174/// A group for [`Alert`]s
175#[derive(Clone, PartialEq, Properties)]
176pub struct GroupProperties {
177    #[prop_or_default]
178    pub children: ChildrenWithProps<Alert>,
179    #[prop_or_default]
180    pub toast: bool,
181}
182
183#[function_component(AlertGroup)]
184pub fn view(props: &GroupProperties) -> Html {
185    let mut classes = classes!("pf-v5-c-alert-group");
186
187    if props.toast {
188        classes.push(classes!("pf-m-toast"));
189    }
190
191    html! (
192        <ul class={classes} role="list">
193            { for props.children.iter().map(|child|
194                wrapper_elt_with_attributes(child.to_html(), "li", &[("class", "pf-v5-c-alert-group__item", ApplyAttributeAs::Attribute)])
195            )}
196        </ul>
197    )
198}