perspective_viewer/components/
filter_dropdown.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use web_sys::*;
14use yew::prelude::*;
15
16use super::modal::*;
17use crate::utils::WeakScope;
18
19static CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/css/filter-dropdown.css"));
20
21pub enum FilterDropDownMsg {
22    SetValues(Vec<String>),
23    SetCallback(Callback<String>),
24    ItemDown,
25    ItemUp,
26    ItemSelect,
27}
28
29pub struct FilterDropDown {
30    values: Option<Vec<String>>,
31    selected: usize,
32    on_select: Option<Callback<String>>,
33}
34
35#[derive(Properties, PartialEq)]
36pub struct FilterDropDownProps {
37    #[prop_or_default]
38    pub weak_link: WeakScope<FilterDropDown>,
39}
40
41impl ModalLink<FilterDropDown> for FilterDropDownProps {
42    fn weak_link(&self) -> &'_ WeakScope<FilterDropDown> {
43        &self.weak_link
44    }
45}
46
47impl Component for FilterDropDown {
48    type Message = FilterDropDownMsg;
49    type Properties = FilterDropDownProps;
50
51    fn create(ctx: &Context<Self>) -> Self {
52        ctx.set_modal_link();
53        Self {
54            values: Some(vec![]),
55            selected: 0,
56            on_select: None,
57        }
58    }
59
60    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
61        match msg {
62            FilterDropDownMsg::SetCallback(callback) => {
63                self.on_select = Some(callback);
64                false
65            },
66            FilterDropDownMsg::SetValues(values) => {
67                self.values = Some(values);
68                self.selected = 0;
69                true
70            },
71            FilterDropDownMsg::ItemSelect => {
72                if let Some(ref values) = self.values {
73                    match values.get(self.selected) {
74                        None => {
75                            console::error_1(&"Selected out-of-bounds".into());
76                            false
77                        },
78                        Some(x) => {
79                            self.on_select.as_ref().unwrap().emit(x.clone());
80                            false
81                        },
82                    }
83                } else {
84                    console::error_1(&"No Values".into());
85                    false
86                }
87            },
88            FilterDropDownMsg::ItemDown => {
89                self.selected += 1;
90                if let Some(ref values) = self.values
91                    && self.selected >= values.len()
92                {
93                    self.selected = 0;
94                };
95
96                true
97            },
98            FilterDropDownMsg::ItemUp => {
99                if let Some(ref values) = self.values
100                    && self.selected < 1
101                {
102                    self.selected = values.len();
103                }
104
105                self.selected -= 1;
106                true
107            },
108        }
109    }
110
111    fn changed(&mut self, _ctx: &Context<Self>, _old: &Self::Properties) -> bool {
112        false
113    }
114
115    fn view(&self, _ctx: &Context<Self>) -> Html {
116        let body = html! {
117            if let Some(ref values) = self.values {
118                if !values.is_empty() {
119                    { for values
120                            .iter()
121                            .enumerate()
122                            .map(|(idx, value)| {
123                                let click = self.on_select.as_ref().unwrap().reform({
124                                    let value = value.clone();
125                                    move |_: MouseEvent| value.clone()
126                                });
127
128                                html! {
129                                    if idx == self.selected {
130                                        <span onmousedown={ click } class="selected">{ value }</span>
131                                    } else {
132                                        <span onmousedown={ click }>{ value }</span>
133                                    }
134                                }
135                            }) }
136                } else {
137                    <span class="no-results">{ "No Completions" }</span>
138                }
139            }
140        };
141
142        html! { <><style>{ &CSS }</style>{ body }</> }
143    }
144}