perspective_viewer/components/
empty_row.rs1use std::collections::HashSet;
14use std::rc::Rc;
15
16use derivative::Derivative;
17use perspective_client::clone;
18use wasm_bindgen::JsCast;
19use web_sys::*;
20use yew::prelude::*;
21
22use crate::components::filter_dropdown::FilterDropDownElement;
23
24#[derive(Properties, Derivative)]
25#[derivative(Debug)]
26pub struct EmptyRowProps {
27 #[derivative(Debug = "ignore")]
28 pub dropdown: Rc<FilterDropDownElement>,
29 pub exclude: HashSet<String>,
30 pub on_select: Callback<String>,
31 pub focused: bool,
32 pub index: usize,
33 pub set_focused_index: Callback<Option<usize>>,
34 pub value: String,
35 pub column_name: String,
36}
37
38impl PartialEq for EmptyRowProps {
39 fn eq(&self, _other: &Self) -> bool {
40 false
41 }
42}
43
44pub enum EmptyRowMsg {
45 KeyDown(u32),
46 Blur,
47 Input(String),
48 Focus,
49}
50
51use EmptyRowMsg::*;
52
53#[derive(Default)]
54pub struct EmptyRow {
55 input_ref: NodeRef,
56}
57
58impl Component for EmptyRow {
59 type Message = EmptyRowMsg;
60 type Properties = EmptyRowProps;
61
62 fn view(&self, ctx: &Context<Self>) -> Html {
63 let onblur = ctx.link().callback(|_| Blur);
64 let oninput = ctx.link().callback(|e: InputEvent| {
65 let value = e
66 .target()
67 .unwrap()
68 .unchecked_into::<HtmlInputElement>()
69 .value();
70 Input(value)
71 });
72 let onkeydown = ctx
73 .link()
74 .callback(|event: KeyboardEvent| KeyDown(event.key_code()));
75
76 if ctx.props().focused {
77 ctx.link()
79 .send_message_batch(vec![Focus, Input(ctx.props().value.clone())]);
80 }
81 let onfocus = {
82 clone!(ctx.props().value);
83 ctx.link().callback(move |_| Input(value.clone()))
84 };
85
86 html! {
87 <div class="pivot-column column-empty">
88 <input
89 spellcheck="false"
90 ref={self.input_ref.clone()}
91 {onblur}
92 {onkeydown}
93 {oninput}
94 {onfocus}
95 class="column-empty-input"
96 />
97 </div>
98 }
99 }
100
101 fn changed(&mut self, _ctx: &Context<Self>, _old_props: &Self::Properties) -> bool {
102 if let Some(elem) = self.input_ref.cast::<HtmlInputElement>() {
103 elem.blur().unwrap();
104 }
105 false
106 }
107
108 fn destroy(&mut self, ctx: &Context<Self>) {
109 ctx.props().dropdown.hide().unwrap();
110 }
111
112 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
113 let p = ctx.props();
114 match msg {
115 Focus => {
116 if let Some(elem) = self.input_ref.cast::<HtmlInputElement>() {
117 elem.set_value(&ctx.props().value);
118 elem.focus().unwrap();
119 }
120
121 false
122 },
123 Blur => {
124 p.dropdown.hide().unwrap();
125 if let Some(elem) = self.input_ref.cast::<HtmlInputElement>() {
126 elem.set_value("");
127 }
128 p.set_focused_index.emit(Some(p.index + 1));
129
130 false
131 },
132 KeyDown(40) => {
133 p.dropdown.item_down();
134 false
135 },
136 KeyDown(38) => {
137 p.dropdown.item_up();
138 false
139 },
140 KeyDown(13) => {
141 p.dropdown.item_select();
142 p.dropdown.hide().unwrap();
143 p.set_focused_index.emit(Some(p.index + 1));
144 true
145 },
146 KeyDown(_) => false,
147 Input(value) => {
148 if let Some(elem) = self.input_ref.cast::<HtmlElement>() {
149 ctx.props().dropdown.autocomplete(
150 (ctx.props().index, ctx.props().column_name.clone()),
151 value,
152 ctx.props().exclude.clone(),
153 elem,
154 ctx.props().on_select.clone(),
155 );
156 }
157
158 false
159 },
160 }
161 }
162
163 fn create(_ctx: &Context<Self>) -> Self {
164 Self::default()
165 }
166}