Skip to main content

patternfly_yew/components/menu/
item.rs

1use super::use_close_menu_callback;
2use crate::prelude::Icon;
3use yew::prelude::*;
4
5#[derive(Clone, Debug, PartialEq, Properties)]
6struct MenuItemProperties {
7    pub children: Html,
8    pub icon: Option<Html>,
9    pub danger: bool,
10    pub disabled: bool,
11    pub selected: bool,
12    pub r#type: MenuItemType,
13    pub close_on_select: bool,
14    pub description: Option<String>,
15    pub style: Option<AttrValue>,
16    pub class: Classes,
17}
18
19#[derive(Clone, PartialEq, Debug)]
20enum MenuItemType {
21    Button(Callback<()>),
22    Link { href: AttrValue, target: AttrValue },
23}
24
25#[component]
26fn MenuItem(props: &MenuItemProperties) -> Html {
27    let mut class = classes!("pf-v6-c-menu__list-item");
28
29    if props.danger {
30        class.push(classes!("pf-m-danger"));
31    }
32
33    if props.disabled {
34        class.push(classes!("pf-m-disabled"));
35    }
36
37    let onclose = use_close_menu_callback();
38
39    let mut item_class = classes!("pf-v6-c-menu__item");
40    if props.selected {
41        item_class.push(classes!("pf-m-selected"));
42    }
43
44    let element = |content: Html| match &props.r#type {
45        MenuItemType::Button(callback) => {
46            let callback = callback.clone();
47            let close_on_select = props.close_on_select;
48
49            html!(
50                <button
51                    class={item_class}
52                    type="button"
53                    role="menuitem"
54                    tabindex="-1"
55                    disabled={props.disabled}
56                    onclick={callback.reform(move |_| {
57                        if close_on_select {
58                            onclose.emit(());
59                        }
60                    })}
61                >
62                    { content }
63                </button>
64            )
65        }
66        MenuItemType::Link { href, target } => {
67            let tabindex = match props.disabled {
68                true => Some("-1"),
69                false => None,
70            };
71
72            let onclick = props.close_on_select.then(|| onclose.reform(|_| ()));
73
74            html!(
75                <a
76                    class={item_class}
77                    {href}
78                    {target}
79                    {onclick}
80                    aria-disabled={props.disabled.to_string()}
81                    {tabindex}
82                    role="menuitem"
83                >
84                    { content }
85                </a>
86            )
87        }
88    };
89
90    class.extend(&props.class);
91
92    html!(
93        <li {class} style={&props.style}>
94            { element(html!(
95                <>
96                    <span class="pf-v6-c-menu__item-main">
97                        if let Some(icon) = &props.icon {
98                            <span class="pf-v6-c-menu__item-icon"> {icon.clone()} </span>
99                        }
100                        if props.danger {
101                            <span class="pf-v6-screen-reader">{ "Danger Item:" }</span>
102                        }
103
104                        <span class="pf-v6-c-menu__item-text">{ props.children.clone() }</span>
105
106                        if props.selected {
107                            <span class="pf-v6-c-menu__item-select-icon">{ Icon::Check }</span>
108                        }
109                    </span>
110                    if let Some(description) = &props.description {
111                        <span class="pf-v6-c-menu__item-description"> {description} </span>
112                    }
113                </>
114            )) }
115        </li>
116    )
117}
118
119#[derive(Clone, Debug, PartialEq, Properties)]
120pub struct MenuActionProperties {
121    #[prop_or_default]
122    pub children: Html,
123
124    #[prop_or_default]
125    pub description: Option<String>,
126
127    #[prop_or_default]
128    pub icon: Option<Html>,
129
130    #[prop_or_default]
131    pub danger: bool,
132
133    #[prop_or_default]
134    pub disabled: bool,
135
136    #[prop_or_default]
137    pub onclick: Callback<()>,
138
139    #[prop_or_default]
140    pub selected: bool,
141
142    /// Close the surrounding menu (e.g. a dropdown) when the item is selected.
143    #[prop_or(true)]
144    pub close_on_select: bool,
145
146    #[prop_or_default]
147    pub style: Option<AttrValue>,
148
149    #[prop_or_default]
150    pub class: Classes,
151}
152
153#[component]
154pub fn MenuAction(props: &MenuActionProperties) -> Html {
155    // we use destructing and struct initialization here to ensure we're not missing any new field
156
157    let MenuActionProperties {
158        children,
159        icon,
160        danger,
161        disabled,
162        onclick,
163        description,
164        selected,
165        close_on_select,
166        style,
167        class,
168    } = props.clone();
169
170    let props = MenuItemProperties {
171        children,
172        icon,
173        danger,
174        disabled,
175        r#type: MenuItemType::Button(onclick),
176        close_on_select,
177        description,
178        selected,
179        style,
180        class,
181    };
182
183    html!(<MenuItem ..props />)
184}
185
186#[derive(Clone, Debug, PartialEq, Properties)]
187pub struct MenuLinkProperties {
188    #[prop_or_default]
189    pub children: Html,
190
191    #[prop_or_default]
192    pub description: Option<String>,
193
194    #[prop_or_default]
195    pub icon: Option<Html>,
196
197    #[prop_or_default]
198    pub danger: bool,
199
200    #[prop_or_default]
201    pub disabled: bool,
202
203    pub href: AttrValue,
204
205    #[prop_or_default]
206    pub target: AttrValue,
207
208    #[prop_or_default]
209    pub selected: bool,
210
211    /// Close the surrounding menu (e.g. a dropdown) when the item is selected.
212    #[prop_or(true)]
213    pub close_on_select: bool,
214
215    #[prop_or_default]
216    pub style: Option<AttrValue>,
217
218    #[prop_or_default]
219    pub class: Classes,
220}
221
222#[component()]
223pub fn MenuLink(props: &MenuLinkProperties) -> Html {
224    // we use destructing and struct initialization here to ensure we're not missing any new field
225
226    let MenuLinkProperties {
227        children,
228        icon,
229        danger,
230        disabled,
231        href,
232        target,
233        description,
234        selected,
235        close_on_select,
236        style,
237        class,
238    } = props.clone();
239
240    let props = MenuItemProperties {
241        children,
242        icon,
243        danger,
244        disabled,
245        r#type: MenuItemType::Link { href, target },
246        close_on_select,
247        description,
248        selected,
249        style,
250        class,
251    };
252
253    html!(<MenuItem ..props />)
254}