Skip to main content

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::AttributeOrProperty;
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-v6-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-v6-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-v6-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-v6-c-alert__icon">{ t.icon() }</div>
147            <p class={title_classes}>
148                <span class="pf-v6-screen-reader">{ t.aria_label() }{ ":" }</span>
149                { &props.title }
150            </p>
151            if let Some(onclose) = props.onclose.as_ref() {
152                <div class="pf-v6-c-alert__action">
153                    <Button
154                        variant={ButtonVariant::Plain}
155                        icon={Icon::Times}
156                        onclick={onclose.clone().reform(|_|())}
157                    />
158                </div>
159            }
160            if !props.children.is_empty() {
161                <div class="pf-v6-c-alert__description">{ props.children.clone() }</div>
162            }
163            { actions }
164        </div>
165    )
166}
167
168// alert group
169
170/// A group for [`Alert`]s
171#[derive(Clone, PartialEq, Properties)]
172pub struct GroupProperties {
173    #[prop_or_default]
174    pub children: ChildrenWithProps<Alert>,
175    #[prop_or_default]
176    pub toast: bool,
177}
178
179#[function_component(AlertGroup)]
180pub fn view(props: &GroupProperties) -> Html {
181    let mut classes = classes!("pf-v6-c-alert-group");
182
183    if props.toast {
184        classes.push(classes!("pf-m-toast"));
185    }
186
187    html! (
188        <ul class={classes} role="list">
189            { for props.children.iter().map(|child|
190                wrapper_elt_with_attributes(child.into(), "li", &[("class", AttributeOrProperty::Static("pf-v6-c-alert-group__item"))])
191            ) }
192        </ul>
193    )
194}