perspective_viewer/components/
function_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 perspective_client::config::CompletionItemSuggestion;
14use web_sys::*;
15use yew::prelude::*;
16
17use super::modal::*;
18use crate::utils::WeakScope;
19
20static CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/css/function-dropdown.css"));
21
22pub enum FunctionDropDownMsg {
23    SetValues(Vec<CompletionItemSuggestion>),
24    SetCallback(Callback<CompletionItemSuggestion>),
25    ItemDown,
26    ItemUp,
27    ItemSelect,
28}
29
30pub struct FunctionDropDown {
31    values: Option<Vec<CompletionItemSuggestion>>,
32    selected: usize,
33    on_select: Option<Callback<CompletionItemSuggestion>>,
34}
35
36#[derive(Properties, PartialEq)]
37pub struct FunctionDropDownProps {
38    #[prop_or_default]
39    pub weak_link: WeakScope<FunctionDropDown>,
40}
41
42impl ModalLink<FunctionDropDown> for FunctionDropDownProps {
43    fn weak_link(&self) -> &'_ WeakScope<FunctionDropDown> {
44        &self.weak_link
45    }
46}
47
48impl Component for FunctionDropDown {
49    type Message = FunctionDropDownMsg;
50    type Properties = FunctionDropDownProps;
51
52    fn create(ctx: &Context<Self>) -> Self {
53        ctx.set_modal_link();
54        Self {
55            values: Some(vec![]),
56            selected: 0,
57            on_select: None,
58        }
59    }
60
61    fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
62        match msg {
63            FunctionDropDownMsg::SetCallback(callback) => {
64                self.on_select = Some(callback);
65                false
66            },
67            FunctionDropDownMsg::SetValues(values) => {
68                self.values = Some(values);
69                self.selected = 0;
70                true
71            },
72            FunctionDropDownMsg::ItemSelect => {
73                if let Some(ref values) = self.values {
74                    match values.get(self.selected) {
75                        None => {
76                            console::error_1(&"Selected out-of-bounds".into());
77                            false
78                        },
79                        Some(x) => {
80                            self.on_select.as_ref().unwrap().emit(*x);
81                            false
82                        },
83                    }
84                } else {
85                    console::error_1(&"No Values".into());
86                    false
87                }
88            },
89            FunctionDropDownMsg::ItemDown => {
90                self.selected += 1;
91                if let Some(ref values) = self.values {
92                    if self.selected >= values.len() {
93                        self.selected = 0;
94                    };
95                };
96
97                true
98            },
99            FunctionDropDownMsg::ItemUp => {
100                if let Some(ref values) = self.values {
101                    if self.selected < 1 {
102                        self.selected = values.len();
103                    }
104                }
105
106                self.selected -= 1;
107                true
108            },
109        }
110    }
111
112    fn changed(&mut self, _ctx: &Context<Self>, _old: &Self::Properties) -> bool {
113        false
114    }
115
116    fn view(&self, _ctx: &Context<Self>) -> Html {
117        let body = html! {
118            if let Some(ref values) = self.values {
119                if !values.is_empty() {
120                    { for values
121                            .iter()
122                            .enumerate()
123                            .map(|(idx, value)| {
124                                let click = self.on_select.as_ref().unwrap().reform({
125                                    let value = *value;
126                                    move |_: MouseEvent| value
127                                });
128
129                                html! {
130                                    if idx == self.selected {
131                                        <div onmousedown={ click } class="selected">
132                                            <span style="font-weight:500">{ value.label }</span>
133                                            <br/>
134                                            <span style="padding-left:12px">{ value.documentation }</span>
135                                        </div>
136                                    } else {
137                                        <div onmousedown={ click }>
138                                            <span style="font-weight:500">{ value.label }</span>
139                                            <br/>
140                                            <span style="padding-left:12px">{ value.documentation }</span>
141                                        </div>
142                                    }
143                                }
144                            }) }
145                }
146            }
147        };
148
149        html! { <><style>{ &CSS }</style>{ body }</> }
150    }
151}