Skip to main content

perspective_viewer/components/column_selector/
empty_column.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::config::Expression;
16use web_sys::*;
17use yew::prelude::*;
18
19use crate::components::style::LocalStyle;
20use crate::css;
21use crate::custom_elements::ColumnDropDownElement;
22
23#[derive(Properties)]
24pub struct EmptyColumnProps {
25    /// The autocomplete dropdown element.
26    pub column_dropdown: ColumnDropDownElement,
27
28    /// Values to exclude from autocomplete
29    pub exclude: HashSet<String>,
30
31    /// Fires when a value is selected.
32    pub on_select: Callback<InPlaceColumn>,
33}
34
35impl PartialEq for EmptyColumnProps {
36    fn eq(&self, _other: &Self) -> bool {
37        false
38    }
39}
40
41#[derive(Clone, Debug)]
42pub enum InPlaceColumn {
43    Column(String),
44    Expression(Expression<'static>),
45}
46
47pub enum EmptyColumnMsg {
48    KeyDown(u32),
49    Blur,
50    Input,
51}
52
53use EmptyColumnMsg::*;
54
55#[derive(Default)]
56pub struct EmptyColumn {
57    input_ref: NodeRef,
58}
59
60impl Component for EmptyColumn {
61    type Message = EmptyColumnMsg;
62    type Properties = EmptyColumnProps;
63
64    fn view(&self, ctx: &Context<Self>) -> Html {
65        let onblur = ctx.link().callback(|_| Blur);
66        let oninput = ctx.link().callback(|_| Input);
67        let onkeydown = ctx
68            .link()
69            .callback(|event: KeyboardEvent| KeyDown(event.key_code()));
70
71        html! {
72            <div class="pivot-column column-empty">
73                <LocalStyle href={css!("empty-column")} />
74                <input
75                    spellcheck="false"
76                    ref={self.input_ref.clone()}
77                    {onblur}
78                    {onkeydown}
79                    {oninput}
80                    class="column-empty-input"
81                />
82            </div>
83        }
84    }
85
86    fn changed(&mut self, _ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
87        if let Some(elem) = self.input_ref.cast::<HtmlInputElement>() {
88            elem.blur().unwrap();
89        }
90
91        false
92    }
93
94    fn destroy(&mut self, ctx: &Context<Self>) {
95        ctx.props().column_dropdown.hide().unwrap();
96    }
97
98    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
99        match msg {
100            Blur => {
101                ctx.props().column_dropdown.hide().unwrap();
102                if let Some(elem) = self.input_ref.cast::<HtmlInputElement>() {
103                    elem.set_value("");
104                }
105
106                false
107            },
108            KeyDown(40) => {
109                ctx.props().column_dropdown.item_down();
110                false
111            },
112            KeyDown(38) => {
113                ctx.props().column_dropdown.item_up();
114                false
115            },
116            KeyDown(13) => {
117                ctx.props().column_dropdown.item_select();
118                ctx.props().column_dropdown.hide().unwrap();
119                false
120            },
121            KeyDown(_) => false,
122            Input => {
123                if let Some(elem) = self.input_ref.cast::<HtmlInputElement>() {
124                    ctx.props().column_dropdown.autocomplete(
125                        elem,
126                        ctx.props().exclude.clone(),
127                        ctx.props().on_select.clone(),
128                    );
129                }
130
131                false
132            },
133        }
134    }
135
136    fn create(_ctx: &Context<Self>) -> Self {
137        Self::default()
138    }
139}