|
| 1 | +//////////////////////////////////////////////////////////////////////////////// |
| 2 | +// |
| 3 | +// Copyright (c) 2018, the Perspective Authors. |
| 4 | +// |
| 5 | +// This file is part of the Perspective library, distributed under the terms |
| 6 | +// of the Apache License 2.0. The full license can be found in the LICENSE |
| 7 | +// file. |
| 8 | + |
| 9 | +use web_sys::*; |
| 10 | +use yew::prelude::*; |
| 11 | + |
| 12 | +use super::modal::*; |
| 13 | +use super::InPlaceColumn; |
| 14 | +use crate::utils::WeakScope; |
| 15 | +use crate::*; |
| 16 | + |
| 17 | +static CSS: &str = include_str!("../../../target/css/column-dropdown.css"); |
| 18 | + |
| 19 | +pub enum ColumnDropDownMsg { |
| 20 | + SetValues(Vec<InPlaceColumn>, f64), |
| 21 | + SetCallback(Callback<InPlaceColumn>), |
| 22 | + ItemDown, |
| 23 | + ItemUp, |
| 24 | + ItemSelect, |
| 25 | +} |
| 26 | + |
| 27 | +pub struct ColumnDropDown { |
| 28 | + values: Option<Vec<InPlaceColumn>>, |
| 29 | + selected: usize, |
| 30 | + width: f64, |
| 31 | + on_select: Option<Callback<InPlaceColumn>>, |
| 32 | +} |
| 33 | + |
| 34 | +#[derive(Properties, PartialEq)] |
| 35 | +pub struct ColumnDropDownProps { |
| 36 | + #[prop_or_default] |
| 37 | + pub weak_link: WeakScope<ColumnDropDown>, |
| 38 | +} |
| 39 | + |
| 40 | +impl ModalLink<ColumnDropDown> for ColumnDropDownProps { |
| 41 | + fn weak_link(&self) -> &'_ WeakScope<ColumnDropDown> { |
| 42 | + &self.weak_link |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +impl Component for ColumnDropDown { |
| 47 | + type Message = ColumnDropDownMsg; |
| 48 | + type Properties = ColumnDropDownProps; |
| 49 | + |
| 50 | + fn create(ctx: &Context<Self>) -> Self { |
| 51 | + ctx.set_modal_link(); |
| 52 | + Self { |
| 53 | + values: Some(vec![]), |
| 54 | + selected: 0, |
| 55 | + width: 0.0, |
| 56 | + on_select: None, |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool { |
| 61 | + match msg { |
| 62 | + ColumnDropDownMsg::SetCallback(callback) => { |
| 63 | + self.on_select = Some(callback); |
| 64 | + false |
| 65 | + } |
| 66 | + ColumnDropDownMsg::SetValues(values, width) => { |
| 67 | + self.values = Some(values); |
| 68 | + self.selected = 0; |
| 69 | + self.width = width; |
| 70 | + true |
| 71 | + } |
| 72 | + ColumnDropDownMsg::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.clone()); |
| 81 | + false |
| 82 | + } |
| 83 | + } |
| 84 | + } else { |
| 85 | + console::error_1(&"No Values".into()); |
| 86 | + false |
| 87 | + } |
| 88 | + } |
| 89 | + ColumnDropDownMsg::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 | + ColumnDropDownMsg::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 | + { |
| 121 | + for values |
| 122 | + .iter() |
| 123 | + .enumerate() |
| 124 | + .map(|(idx, value)| { |
| 125 | + let click = self.on_select.as_ref().unwrap().reform({ |
| 126 | + let value = value.clone(); |
| 127 | + move |_: MouseEvent| value.clone() |
| 128 | + }); |
| 129 | + |
| 130 | + let row = match value { |
| 131 | + InPlaceColumn::Column(col) => html! { |
| 132 | + <span>{ col }</span> |
| 133 | + }, |
| 134 | + InPlaceColumn::Expression(col) => html! { |
| 135 | + <span id="add-expression">{ col }</span> |
| 136 | + }, |
| 137 | + }; |
| 138 | + |
| 139 | + html! { |
| 140 | + if idx == self.selected { |
| 141 | + <span onmousedown={ click } class="selected">{ row }</span> |
| 142 | + } else { |
| 143 | + <span onmousedown={ click }>{ row }</span> |
| 144 | + } |
| 145 | + } |
| 146 | + }) |
| 147 | + } |
| 148 | + } else { |
| 149 | + <span class="no-results">{ "Invalid Column" }</span> |
| 150 | + } |
| 151 | + } |
| 152 | + }; |
| 153 | + |
| 154 | + let position = format!( |
| 155 | + ":host{{min-width:{}px;max-width:{}px}}", |
| 156 | + self.width, self.width |
| 157 | + ); |
| 158 | + |
| 159 | + html_template! { |
| 160 | + <style> |
| 161 | + { &CSS } |
| 162 | + </style> |
| 163 | + <style> |
| 164 | + { position } |
| 165 | + </style> |
| 166 | + { body } |
| 167 | + } |
| 168 | + } |
| 169 | +} |
0 commit comments