perspective_viewer/custom_elements/
column_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 std::collections::HashSet;
14
15use perspective_client::clone;
16use perspective_client::config::Expression;
17use perspective_js::json;
18use perspective_js::utils::global;
19use wasm_bindgen::JsCast;
20use wasm_bindgen::prelude::*;
21use web_sys::*;
22use yew::html::ImplicitClone;
23use yew::{Callback, props};
24
25use crate::components::column_dropdown::*;
26use crate::components::column_selector::InPlaceColumn;
27use crate::custom_elements::modal::*;
28use crate::session::Session;
29use crate::*;
30
31#[wasm_bindgen]
32#[derive(Clone)]
33pub struct ColumnDropDownElement {
34    modal: ModalElement<ColumnDropDown>,
35    session: Session,
36}
37
38impl ImplicitClone for ColumnDropDownElement {}
39
40impl ColumnDropDownElement {
41    pub fn new(session: Session) -> Self {
42        let dropdown = global::document()
43            .create_element("perspective-dropdown")
44            .unwrap()
45            .unchecked_into::<HtmlElement>();
46
47        let props = props!(ColumnDropDownProps {});
48        let modal = ModalElement::new(dropdown, props, false, None);
49        Self { modal, session }
50    }
51
52    pub fn autocomplete(
53        &self,
54        target: HtmlInputElement,
55        exclude: HashSet<String>,
56        callback: Callback<InPlaceColumn>,
57    ) -> Option<()> {
58        let input = target.value();
59        let metadata = self.session.metadata();
60        let mut values: Vec<InPlaceColumn> = vec![];
61        let small_input = input.to_lowercase();
62        for col in metadata.get_table_columns()? {
63            if !exclude.contains(col) && col.to_lowercase().contains(&small_input) {
64                values.push(InPlaceColumn::Column(col.to_owned()));
65            }
66        }
67
68        for col in self.session.metadata().get_expression_columns() {
69            if !exclude.contains(col) && col.to_lowercase().contains(&small_input) {
70                values.push(InPlaceColumn::Column(col.to_owned()));
71            }
72        }
73
74        clone!(self.modal, self.session);
75        ApiFuture::spawn(async move {
76            if !exclude.contains(&input) {
77                let is_expr = session.validate_expr(&input).await?.is_none();
78
79                if is_expr {
80                    values.push(InPlaceColumn::Expression(Expression::new(
81                        None,
82                        input.into(),
83                    )));
84                }
85            }
86
87            let classes = modal.custom_element.class_list();
88            let no_results = json!(["no-results"]);
89            if values.is_empty() {
90                classes.add(&no_results).unwrap();
91            } else {
92                classes.remove(&no_results).unwrap();
93            }
94
95            modal.send_message_batch(vec![
96                ColumnDropDownMsg::SetCallback(callback),
97                ColumnDropDownMsg::SetValues(values, target.get_bounding_client_rect().width()),
98            ]);
99
100            modal.open(target.unchecked_into(), None).await
101        });
102
103        Some(())
104    }
105
106    pub fn item_select(&self) {
107        self.modal.send_message(ColumnDropDownMsg::ItemSelect);
108    }
109
110    pub fn item_down(&self) {
111        self.modal.send_message(ColumnDropDownMsg::ItemDown);
112    }
113
114    pub fn item_up(&self) {
115        self.modal.send_message(ColumnDropDownMsg::ItemUp);
116    }
117
118    pub fn hide(&self) -> ApiResult<()> {
119        self.modal.hide()
120    }
121
122    pub fn connected_callback(&self) {}
123}