patternfly_yew/components/
context_selector.rs

1//! Context selector
2use crate::prelude::{GlobalClose, Icon, InputGroup, TextInput, TextInputType};
3use std::rc::Rc;
4use yew::prelude::*;
5
6/// Properties for [`ContextSelector`]
7#[derive(Properties, Debug, Clone, PartialEq)]
8pub struct ContextSelectorProperties {
9    #[prop_or_default]
10    pub selected: String,
11    #[prop_or_default]
12    pub onsearch: Callback<String>,
13    #[prop_or_default]
14    pub children: ChildrenWithProps<ContextSelectorItem>,
15}
16
17#[doc(hidden)]
18#[derive(Clone, Debug)]
19pub enum ContextSelectorMsg {
20    Toggle,
21    Close,
22    Search(String),
23}
24
25/// Context Selector component
26///
27/// > A **context selector** can be used in addition to global navigation when the data or resources you show in the interface need to change depending on the user's context.
28///
29/// See: <https://www.patternfly.org/components/menus/context-selector>
30///
31/// ## Properties
32///
33/// Defined by [`ContextSelectorProperties`].
34#[deprecated(
35    since = "5.0.0",
36    note = "The ContentSelector component has been deprecated by PatternFly. \
37    See https://pf5.patternfly.org/components/menus/context-selector for more information."
38)]
39pub struct ContextSelector {
40    expanded: bool,
41    global_close: GlobalClose,
42}
43
44#[allow(deprecated)]
45impl Component for ContextSelector {
46    type Message = ContextSelectorMsg;
47    type Properties = ContextSelectorProperties;
48
49    fn create(ctx: &Context<Self>) -> Self {
50        let global_close = GlobalClose::new(
51            NodeRef::default(),
52            ctx.link().callback(|_| ContextSelectorMsg::Close),
53        );
54        Self {
55            expanded: false,
56            global_close,
57        }
58    }
59
60    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
61        match msg {
62            Self::Message::Toggle => {
63                self.expanded = !self.expanded;
64            }
65            Self::Message::Close => {
66                self.expanded = false;
67            }
68            Self::Message::Search(value) => {
69                ctx.props().onsearch.emit(value);
70                return false;
71            }
72        }
73        true
74    }
75
76    fn view(&self, ctx: &Context<Self>) -> Html {
77        let mut classes = Classes::from("pf-v5-c-context-selector");
78
79        if self.expanded {
80            classes.push("pf-m-expanded");
81        }
82
83        html! (
84            <div
85                class={classes}
86                ref={self.global_close.clone()}
87            >
88                <button
89                    class="pf-v5-c-context-selector__toggle"
90                    aria-expanded={self.expanded.to_string()}
91                    type="button"
92                    onclick={ctx.link().callback(|_|ContextSelectorMsg::Toggle)}
93                >
94                    <span class="pf-v5-c-context-selector__toggle-text">{&ctx.props().selected}</span>
95                    <span class="pf-v5-c-context-selector__toggle-icon">{Icon::CaretDown}</span>
96                </button>
97                <div class="pf-v5-c-context-selector__menu"
98                    hidden={!self.expanded}
99                >
100                    <div class="pf-v5-c-context-selector__menu-search">
101                        <InputGroup>
102                            <TextInput
103                                onchange={ctx.link().callback(ContextSelectorMsg::Search)}
104                                icon={Icon::Search}
105                                r#type={TextInputType::Search}
106                            />
107                        </InputGroup>
108                    </div>
109                    <ul class="pf-v5-c-context-selector__menu-list">
110                        { for ctx.props().children.iter().map(|mut item|{
111                            let props = Rc::make_mut(&mut item.props);
112                            props.need_close = ctx.link().callback(|_|ContextSelectorMsg::Close);
113                            item
114                        }) }
115                    </ul>
116                </div>
117            </div>
118        )
119    }
120}
121
122// item
123
124/// Properties for [`ContextSelectorItem`]
125#[derive(Properties, Debug, Clone, PartialEq)]
126pub struct ItemProperties {
127    pub label: String,
128    #[prop_or_default]
129    pub onclick: Callback<()>,
130    #[prop_or_default]
131    pub disabled: bool,
132    #[prop_or_default]
133    pub(crate) need_close: Callback<()>,
134}
135
136#[doc(hidden)]
137#[derive(Clone, Copy, Debug)]
138pub enum ItemMsg {
139    Clicked,
140}
141
142/// An item of a [`ContextSelector`]
143pub struct ContextSelectorItem {}
144
145impl Component for ContextSelectorItem {
146    type Message = ItemMsg;
147    type Properties = ItemProperties;
148
149    fn create(_ctx: &Context<Self>) -> Self {
150        Self {}
151    }
152
153    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
154        match msg {
155            Self::Message::Clicked => {
156                ctx.props().onclick.emit(());
157                ctx.props().need_close.emit(());
158            }
159        }
160        true
161    }
162
163    fn view(&self, ctx: &Context<Self>) -> Html {
164        let classes = Classes::from("pf-v5-c-context-selector__menu-list-item");
165
166        html!(
167            <li>
168                <button
169                    class={classes}
170                    disabled={ctx.props().disabled}
171                    type="button"
172                    onclick={ctx.link().callback(|_|ItemMsg::Clicked)}
173                >
174                    { &ctx.props().label }
175                </button>
176            </li>
177        )
178    }
179}