@perspective-dev/viewer 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/dist/cdn/perspective-viewer.js +2 -2
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/dracula.css +1 -1
  4. package/dist/css/gruvbox-dark.css +1 -1
  5. package/dist/css/gruvbox.css +1 -1
  6. package/dist/css/icons.css +1 -1
  7. package/dist/css/monokai.css +1 -1
  8. package/dist/css/pro-dark.css +1 -1
  9. package/dist/css/pro.css +1 -1
  10. package/dist/css/solarized-dark.css +1 -1
  11. package/dist/css/solarized.css +1 -1
  12. package/dist/css/themes.css +1 -1
  13. package/dist/css/vaporwave.css +1 -1
  14. package/dist/esm/extensions.d.ts +23 -2
  15. package/dist/esm/perspective-viewer.d.ts +2 -7
  16. package/dist/esm/perspective-viewer.inline.js +2 -2
  17. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  18. package/dist/esm/perspective-viewer.js +2 -2
  19. package/dist/esm/perspective-viewer.js.map +4 -4
  20. package/dist/esm/plugin.d.ts +1 -1
  21. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +1 -0
  22. package/dist/wasm/perspective-viewer.d.ts +218 -46
  23. package/dist/wasm/perspective-viewer.js +1251 -762
  24. package/dist/wasm/perspective-viewer.wasm +0 -0
  25. package/dist/wasm/perspective-viewer.wasm.d.ts +38 -19
  26. package/package.json +1 -1
  27. package/src/less/containers/scroll-panel.less +0 -1
  28. package/src/less/plugin-selector.less +15 -5
  29. package/src/less/status-bar.less +75 -27
  30. package/src/less/viewer.less +140 -58
  31. package/src/rust/components/column_dropdown.rs +21 -21
  32. package/src/rust/components/column_selector/active_column.rs +131 -120
  33. package/src/rust/components/column_selector/add_expression_button.rs +5 -0
  34. package/src/rust/components/column_selector/aggregate_selector.rs +8 -4
  35. package/src/rust/components/column_selector/config_selector.rs +170 -161
  36. package/src/rust/components/column_selector/empty_column.rs +16 -11
  37. package/src/rust/components/column_selector/{expression_toolbar.rs → expr_edit_button.rs} +7 -0
  38. package/src/rust/components/column_selector/filter_column.rs +195 -194
  39. package/src/rust/components/column_selector/inactive_column.rs +82 -67
  40. package/src/rust/components/column_selector/pivot_column.rs +16 -11
  41. package/src/rust/components/column_selector/sort_column.rs +9 -7
  42. package/src/rust/components/column_selector.rs +42 -37
  43. package/src/rust/components/column_settings_sidebar/save_settings.rs +3 -1
  44. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +58 -0
  45. package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +6 -6
  46. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +2 -94
  47. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs_item.rs +111 -0
  48. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +3 -3
  49. package/src/rust/components/column_settings_sidebar/style_tab.rs +23 -83
  50. package/src/rust/components/{column_settings_sidebar/sidebar.rs → column_settings_sidebar.rs} +198 -171
  51. package/src/rust/components/containers/dragdrop_list.rs +20 -20
  52. package/src/rust/components/containers/dropdown_menu.rs +4 -6
  53. package/src/rust/components/containers/mod.rs +1 -4
  54. package/src/rust/components/containers/scroll_panel.rs +80 -80
  55. package/src/rust/components/containers/scroll_panel_item.rs +36 -36
  56. package/src/rust/components/containers/select.rs +46 -44
  57. package/src/rust/components/containers/sidebar.rs +3 -19
  58. package/src/rust/components/{column_settings_sidebar/style_tab/symbol/symbol_config.rs → containers/sidebar_close_button.rs} +15 -9
  59. package/src/rust/components/containers/split_panel.rs +212 -200
  60. package/src/rust/components/containers/tab_list.rs +11 -11
  61. package/src/rust/components/copy_dropdown.rs +22 -25
  62. package/src/rust/components/datetime_column_style/custom.rs +19 -19
  63. package/src/rust/components/datetime_column_style/simple.rs +13 -14
  64. package/src/rust/components/datetime_column_style.rs +75 -76
  65. package/src/rust/components/editable_header.rs +18 -14
  66. package/src/rust/components/empty_row.rs +5 -5
  67. package/src/rust/components/export_dropdown.rs +42 -42
  68. package/src/rust/components/expression_editor.rs +25 -19
  69. package/src/rust/components/filter_dropdown.rs +22 -22
  70. package/src/rust/components/font_loader.rs +11 -9
  71. package/src/rust/components/form/code_editor.rs +106 -105
  72. package/src/rust/components/form/color_range_selector.rs +14 -12
  73. package/src/rust/components/form/color_selector.rs +3 -1
  74. package/src/rust/components/form/debug.rs +95 -94
  75. package/src/rust/components/form/highlight.rs +5 -3
  76. package/src/rust/components/form/mod.rs +3 -2
  77. package/src/rust/components/form/optional_field.rs +2 -2
  78. package/src/rust/components/form/{select_field.rs → select_enum_field.rs} +1 -46
  79. package/src/rust/components/form/select_value_field.rs +64 -0
  80. package/src/rust/components/function_dropdown.rs +21 -21
  81. package/src/rust/components/main_panel.rs +219 -0
  82. package/src/rust/components/mod.rs +6 -6
  83. package/src/rust/components/modal.rs +42 -42
  84. package/src/rust/components/number_column_style.rs +34 -88
  85. package/src/rust/components/plugin_selector.rs +22 -25
  86. package/src/rust/components/render_warning.rs +9 -6
  87. package/src/rust/components/settings_panel.rs +82 -0
  88. package/src/rust/components/status_bar.rs +250 -146
  89. package/src/rust/components/status_bar_counter.rs +26 -119
  90. package/src/rust/components/status_indicator.rs +95 -79
  91. package/src/rust/components/string_column_style.rs +45 -45
  92. package/src/rust/components/style/style_provider.rs +1 -15
  93. package/src/rust/components/style_controls/number_string_format/digits_section.rs +1 -1
  94. package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
  95. package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
  96. package/src/rust/components/style_controls/number_string_format.rs +45 -46
  97. package/src/rust/components/type_icon.rs +14 -11
  98. package/src/rust/components/viewer.rs +241 -384
  99. package/src/rust/config/columns_config.rs +2 -2
  100. package/src/rust/config/datetime_column_style.rs +1 -6
  101. package/src/rust/config/mod.rs +1 -0
  102. package/src/rust/config/number_column_style.rs +0 -6
  103. package/src/rust/config/number_string_format.rs +27 -4
  104. package/src/rust/config/viewer_config.rs +27 -167
  105. package/src/rust/custom_elements/copy_dropdown.rs +14 -6
  106. package/src/rust/custom_elements/export_dropdown.rs +15 -7
  107. package/src/rust/custom_elements/filter_dropdown.rs +4 -4
  108. package/src/rust/custom_elements/mod.rs +3 -0
  109. package/src/rust/custom_elements/viewer.rs +353 -161
  110. package/src/rust/custom_events.rs +55 -32
  111. package/src/rust/dragdrop.rs +4 -24
  112. package/src/rust/exprtk/cursor.rs +10 -1
  113. package/src/rust/exprtk/mod.rs +2 -0
  114. package/src/rust/exprtk/tokenize.rs +20 -3
  115. package/src/rust/js/clipboard.rs +2 -2
  116. package/src/rust/js/mimetype.rs +2 -7
  117. package/src/rust/js/mod.rs +0 -1
  118. package/src/rust/js/plugin.rs +7 -0
  119. package/src/rust/lib.rs +18 -5
  120. package/src/rust/model/column_locator.rs +82 -0
  121. package/src/rust/model/columns_iter_set.rs +1 -0
  122. package/src/rust/model/copy_export.rs +50 -14
  123. package/src/rust/model/edit_expression.rs +2 -5
  124. package/src/rust/model/eject.rs +41 -0
  125. package/src/rust/model/export_app.rs +3 -2
  126. package/src/rust/model/get_viewer_config.rs +4 -28
  127. package/src/rust/model/intersection_observer.rs +20 -8
  128. package/src/rust/model/mod.rs +11 -4
  129. package/src/rust/model/plugin_column_styles.rs +0 -31
  130. package/src/rust/model/reset_all.rs +38 -0
  131. package/src/rust/model/resize_observer.rs +34 -7
  132. package/src/rust/model/restore_and_render.rs +12 -7
  133. package/src/rust/{utils/scope.rs → model/send_plugin_config.rs} +32 -35
  134. package/src/rust/model/structural.rs +194 -23
  135. package/src/rust/model/update_and_render.rs +14 -4
  136. package/src/rust/{model/create_col.rs → presentation/column_locator.rs} +73 -42
  137. package/src/rust/{utils/wasm_abi.rs → presentation/sheets.rs} +54 -40
  138. package/src/rust/presentation.rs +60 -119
  139. package/src/rust/renderer/activate.rs +20 -5
  140. package/src/rust/renderer/limits.rs +0 -149
  141. package/src/rust/renderer/render_timer.rs +1 -1
  142. package/src/rust/renderer.rs +34 -18
  143. package/src/rust/root.rs +50 -0
  144. package/src/rust/session/column_defaults_update.rs +4 -4
  145. package/src/rust/session/drag_drop_update.rs +1 -1
  146. package/src/rust/session/metadata.rs +3 -17
  147. package/src/rust/session/replace_expression_update.rs +1 -2
  148. package/src/rust/session.rs +162 -82
  149. package/src/rust/utils/browser/blob.rs +16 -2
  150. package/src/rust/utils/browser/download.rs +1 -0
  151. package/src/rust/{components/column_settings_sidebar/mod.rs → utils/browser/dragdrop.rs} +14 -5
  152. package/src/rust/utils/browser/mod.rs +8 -4
  153. package/src/rust/utils/browser/selection.rs +5 -0
  154. package/src/rust/utils/custom_element.rs +28 -13
  155. package/src/rust/utils/datetime.rs +5 -0
  156. package/src/rust/utils/debounce.rs +7 -1
  157. package/src/rust/utils/hooks/use_async_callback.rs +7 -17
  158. package/src/rust/utils/mod.rs +28 -40
  159. package/src/rust/utils/number_format.rs +6 -5
  160. package/src/rust/utils/pubsub.rs +15 -10
  161. package/src/rust/utils/weak_scope.rs +11 -1
  162. package/src/svg/bookmark-icon.svg +4 -0
  163. package/src/svg/drag-handle copy.svg +10 -0
  164. package/src/svg/drawer-tab-hover.svg +5 -7
  165. package/src/svg/drawer-tab-invert-hover.svg +4 -8
  166. package/src/svg/drawer-tab-invert.svg +4 -7
  167. package/src/svg/drawer-tab.svg +4 -6
  168. package/src/svg/status_ok.svg +24 -24
  169. package/src/ts/extensions.ts +51 -3
  170. package/src/ts/perspective-viewer.ts +2 -14
  171. package/src/ts/plugin.ts +1 -1
  172. package/src/ts/ts-rs/ViewerConfigUpdate.ts +1 -1
  173. package/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +0 -177
  174. package/src/rust/components/containers/tests/mod.rs +0 -11
  175. package/src/rust/components/containers/tests/split_panel.rs +0 -91
  176. package/src/rust/js/testing.rs +0 -149
  177. package/src/rust/utils/tee.rs +0 -88
  178. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline0.js +0 -0
  179. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline1.js +0 -0
  180. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline2.js +0 -0
  181. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline3.js +0 -0
  182. /package/dist/wasm/snippets/{perspective-viewer-c69283f6f62a5f14 → perspective-viewer-0d326a25c1022412}/inline4.js +0 -0
  183. /package/src/rust/components/{style_controls.rs → style_controls/mod.rs} +0 -0
  184. /package/src/rust/{components/containers → config}/kvpair.rs +0 -0
@@ -19,6 +19,18 @@ use crate::utils::WeakScope;
19
19
 
20
20
  static CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/css/column-dropdown.css"));
21
21
 
22
+ #[derive(Properties, PartialEq)]
23
+ pub struct ColumnDropDownProps {
24
+ #[prop_or_default]
25
+ pub weak_link: WeakScope<ColumnDropDown>,
26
+ }
27
+
28
+ impl ModalLink<ColumnDropDown> for ColumnDropDownProps {
29
+ fn weak_link(&self) -> &'_ WeakScope<ColumnDropDown> {
30
+ &self.weak_link
31
+ }
32
+ }
33
+
22
34
  pub enum ColumnDropDownMsg {
23
35
  SetValues(Vec<InPlaceColumn>, f64),
24
36
  SetCallback(Callback<InPlaceColumn>),
@@ -34,18 +46,6 @@ pub struct ColumnDropDown {
34
46
  on_select: Option<Callback<InPlaceColumn>>,
35
47
  }
36
48
 
37
- #[derive(Properties, PartialEq)]
38
- pub struct ColumnDropDownProps {
39
- #[prop_or_default]
40
- pub weak_link: WeakScope<ColumnDropDown>,
41
- }
42
-
43
- impl ModalLink<ColumnDropDown> for ColumnDropDownProps {
44
- fn weak_link(&self) -> &'_ WeakScope<ColumnDropDown> {
45
- &self.weak_link
46
- }
47
- }
48
-
49
49
  impl Component for ColumnDropDown {
50
50
  type Message = ColumnDropDownMsg;
51
51
  type Properties = ColumnDropDownProps;
@@ -91,19 +91,19 @@ impl Component for ColumnDropDown {
91
91
  },
92
92
  ColumnDropDownMsg::ItemDown => {
93
93
  self.selected += 1;
94
- if let Some(ref values) = self.values
95
- && self.selected >= values.len()
96
- {
97
- self.selected = 0;
94
+ if let Some(ref values) = self.values {
95
+ if self.selected >= values.len() {
96
+ self.selected = 0;
97
+ }
98
98
  }
99
99
 
100
100
  true
101
101
  },
102
102
  ColumnDropDownMsg::ItemUp => {
103
- if let Some(ref values) = self.values
104
- && self.selected < 1
105
- {
106
- self.selected = values.len();
103
+ if let Some(ref values) = self.values {
104
+ if self.selected < 1 {
105
+ self.selected = values.len();
106
+ }
107
107
  }
108
108
 
109
109
  self.selected -= 1;
@@ -157,6 +157,6 @@ impl Component for ColumnDropDown {
157
157
  self.width, self.width
158
158
  );
159
159
 
160
- html! { <><style>{ &CSS }</style><style>{ position }</style>{ body }</> }
160
+ html! { <><style>{ CSS }</style><style>{ position }</style>{ body }</> }
161
161
  }
162
162
  }
@@ -14,47 +14,60 @@ use std::collections::HashSet;
14
14
 
15
15
  use perspective_client::config::*;
16
16
  use perspective_client::utils::PerspectiveResultExt;
17
+ use perspective_js::utils::ApiFuture;
17
18
  use web_sys::*;
18
19
  use yew::prelude::*;
19
20
 
20
21
  use super::InPlaceColumn;
21
22
  use super::aggregate_selector::*;
22
- use super::expression_toolbar::*;
23
+ use super::expr_edit_button::*;
24
+ use crate::PerspectiveProperties;
23
25
  use crate::components::column_selector::{EmptyColumn, InvalidColumn};
24
26
  use crate::components::type_icon::TypeIcon;
25
- use crate::components::viewer::ColumnLocator;
26
27
  use crate::custom_elements::ColumnDropDownElement;
27
28
  use crate::dragdrop::*;
28
29
  use crate::js::plugin::*;
29
30
  use crate::model::*;
30
- use crate::presentation::Presentation;
31
+ use crate::presentation::ColumnLocator;
31
32
  use crate::renderer::*;
32
33
  use crate::session::*;
33
- use crate::*;
34
+ use crate::utils::*;
34
35
 
35
- enum ColumnState {
36
- Empty,
37
- Invalid,
38
- Named(String),
39
- }
40
-
41
- #[derive(Properties, Clone)]
36
+ #[derive(Clone, Properties, PerspectiveProperties!)]
42
37
  pub struct ActiveColumnProps {
38
+ /// The column's index in the list.
43
39
  pub idx: usize,
40
+
41
+ /// The column definition (including name and type).
44
42
  pub name: ActiveColumnState,
45
- pub dragdrop: DragDrop,
46
- pub session: Session,
47
- pub renderer: Renderer,
48
- pub presentation: Presentation,
43
+
44
+ /// The column select dropdown menu element.
49
45
  pub column_dropdown: ColumnDropDownElement,
46
+
47
+ /// `dragenter` event.
50
48
  pub ondragenter: Callback<()>,
49
+
50
+ /// `dragend` event.
51
51
  pub ondragend: Callback<()>,
52
+
53
+ /// Fires when this component's select button is clicked.
52
54
  pub onselect: Callback<()>,
55
+
56
+ /// Fires when this component's expression/config button is clicked.
53
57
  pub on_open_expr_panel: Callback<ColumnLocator>,
54
58
 
59
+ /// Is this column in a grouped context (does the aggregate selector
60
+ /// need to be visible)?
55
61
  #[prop_or_default]
56
62
  pub is_aggregated: bool,
63
+
64
+ /// Is this column's expression/config side panel open?
57
65
  pub is_editing: bool,
66
+
67
+ /// State
68
+ pub session: Session,
69
+ pub dragdrop: DragDrop,
70
+ pub renderer: Renderer,
58
71
  }
59
72
 
60
73
  impl PartialEq for ActiveColumnProps {
@@ -63,93 +76,6 @@ impl PartialEq for ActiveColumnProps {
63
76
  }
64
77
  }
65
78
 
66
- impl ActiveColumnProps {
67
- fn get_name(&self) -> Option<String> {
68
- match &self.name.state {
69
- ActiveColumnStateData::DragOver => Some(self.dragdrop.get_drag_column().unwrap()),
70
- ActiveColumnStateData::Column(name) => Some(name.to_owned()),
71
- ActiveColumnStateData::Required => None,
72
- ActiveColumnStateData::Invalid => None,
73
- }
74
- }
75
-
76
- fn get_table_type(&self) -> Option<ColumnType> {
77
- self.get_name()
78
- .as_ref()
79
- .and_then(|x| self.session.metadata().get_column_table_type(x))
80
- }
81
-
82
- fn _get_view_type(&self) -> Option<ColumnType> {
83
- self.get_name()
84
- .as_ref()
85
- .and_then(|x| self.session.metadata().get_column_view_type(x))
86
- }
87
- }
88
-
89
- derive_model!(Renderer, Session for ActiveColumnProps);
90
-
91
- impl ActiveColumnProps {
92
- /// Remove an active column from `columns`, or alternatively make this
93
- /// column the only column in `columns` if the shift key is set (via the
94
- /// `shift` flag).
95
- ///
96
- /// # Arguments
97
- /// - `name` The name of the column to de-activate, which is a unique ID
98
- /// with respect to `columns`.
99
- /// - `shift` whether to toggle or select this column.
100
- pub fn deactivate_column(&self, name: String, shift: bool) {
101
- let mut columns = self.session.get_view_config().columns.clone();
102
- let max_cols = self
103
- .renderer
104
- .metadata()
105
- .names
106
- .as_ref()
107
- .map_or(0, |x| x.len());
108
-
109
- match self.renderer.metadata().mode {
110
- ColumnSelectMode::Toggle => {
111
- let index = columns
112
- .iter()
113
- .position(|x| x.as_ref() == Some(&name))
114
- .unwrap();
115
-
116
- if max_cols > 0 && index < max_cols - 1 {
117
- columns[index] = None;
118
- } else if !shift && columns.len() > 1 {
119
- columns.retain(|x| x.as_ref() != Some(&name));
120
- } else if shift {
121
- columns.clear();
122
- columns.push(Some(name));
123
- }
124
- },
125
- ColumnSelectMode::Select => {
126
- columns.retain(|x| x.as_ref() != Some(&name));
127
- },
128
- }
129
- self.apply_columns(columns);
130
- }
131
-
132
- fn get_is_required(&self) -> bool {
133
- let min_cols = self.renderer.metadata().min.unwrap_or(0);
134
- self.idx < min_cols
135
- }
136
-
137
- fn get_aggregate(&self, name: &str) -> Option<Aggregate> {
138
- self.session.get_view_config().aggregates.get(name).cloned()
139
- }
140
-
141
- fn apply_columns(&self, columns: Vec<Option<String>>) {
142
- let config = ViewConfigUpdate {
143
- columns: Some(columns),
144
- ..ViewConfigUpdate::default()
145
- };
146
-
147
- self.update_and_render(config)
148
- .map(ApiFuture::spawn)
149
- .unwrap_or_log();
150
- }
151
- }
152
-
153
79
  pub enum ActiveColumnMsg {
154
80
  DeactivateColumn(String, bool),
155
81
  MouseEnter(bool),
@@ -159,14 +85,12 @@ pub enum ActiveColumnMsg {
159
85
 
160
86
  use ActiveColumnMsg::*;
161
87
 
162
- /// An `ActiveColumn` indicates a column which is part of the `columns` field of
163
- /// a `ViewConfig`. It shows additional column details in context (like
88
+ /// An [`ActiveColumn`] indicates a column which is part of the `columns` field
89
+ /// of a [`ViewConfig`]. It shows additional column details in context (like
164
90
  /// selected aggregate), and supports drag/drop and missing entries.
165
91
  /// TODO Break this into "Active", "Hover" and "Empty"?
166
- #[derive(Default)]
167
92
  pub struct ActiveColumn {
168
93
  add_expression_ref: NodeRef,
169
- is_required: bool,
170
94
  mouseover: bool,
171
95
  }
172
96
 
@@ -174,19 +98,13 @@ impl Component for ActiveColumn {
174
98
  type Message = ActiveColumnMsg;
175
99
  type Properties = ActiveColumnProps;
176
100
 
177
- fn create(ctx: &Context<Self>) -> Self {
178
- let is_required = ctx.props().get_is_required();
101
+ fn create(_ctx: &Context<Self>) -> Self {
179
102
  Self {
180
- is_required,
181
- ..Default::default()
103
+ add_expression_ref: NodeRef::default(),
104
+ mouseover: false,
182
105
  }
183
106
  }
184
107
 
185
- fn changed(&mut self, ctx: &Context<Self>, _old: &Self::Properties) -> bool {
186
- self.is_required = ctx.props().get_is_required();
187
- true
188
- }
189
-
190
108
  fn update(&mut self, ctx: &Context<Self>, msg: ActiveColumnMsg) -> bool {
191
109
  match msg {
192
110
  DeactivateColumn(column, shift_key) => {
@@ -248,6 +166,12 @@ impl Component for ActiveColumn {
248
166
  }
249
167
 
250
168
  fn view(&self, ctx: &Context<Self>) -> Html {
169
+ enum ColumnState {
170
+ Empty,
171
+ Invalid,
172
+ Named(String),
173
+ }
174
+
251
175
  let mut classes = classes!["column-selector-draggable"];
252
176
  if ctx.props().is_aggregated {
253
177
  classes.push("show-aggregate");
@@ -310,7 +234,7 @@ impl Component for ActiveColumn {
310
234
  })
311
235
  .collect();
312
236
 
313
- let col_type = ctx.props().get_table_type();
237
+ let col_type = ctx.props().get_table_type(&ctx.props().name);
314
238
  match (name, col_type) {
315
239
  ((label, ColumnState::Empty), _) => {
316
240
  classes.push("empty-named");
@@ -353,7 +277,8 @@ impl Component for ActiveColumn {
353
277
  }
354
278
  },
355
279
  ((label, ColumnState::Named(name)), Some(col_type)) => {
356
- let remove_column = if self.is_required {
280
+ let is_required = ctx.props().get_is_required(ctx.props().idx);
281
+ let remove_column = if is_required {
357
282
  None
358
283
  } else {
359
284
  Some(ctx.link().callback({
@@ -389,7 +314,7 @@ impl Component for ActiveColumn {
389
314
 
390
315
  let is_expression = ctx.props().session.metadata().is_column_expression(&name);
391
316
  let mut class = ctx.props().renderer.metadata().mode.css();
392
- if self.is_required {
317
+ if is_required {
393
318
  class.push("required");
394
319
  };
395
320
 
@@ -398,7 +323,6 @@ impl Component for ActiveColumn {
398
323
  .can_render_column_styles(&name)
399
324
  .unwrap_or_default();
400
325
  let show_edit_btn = is_expression || can_render_styles;
401
-
402
326
  html! {
403
327
  <div
404
328
  class={outer_classes}
@@ -427,7 +351,11 @@ impl Component for ActiveColumn {
427
351
  session={&ctx.props().session}
428
352
  />
429
353
  }
430
- <span class="column_name">{ name.clone() }</span>
354
+ <span
355
+ class="column_name"
356
+ >
357
+ { name.clone() }
358
+ </span>
431
359
  if !ctx.props().is_aggregated {
432
360
  <span class="column-selector--spacer" />
433
361
  }
@@ -460,3 +388,86 @@ impl Component for ActiveColumn {
460
388
  }
461
389
  }
462
390
  }
391
+
392
+ impl ActiveColumnProps {
393
+ fn get_name(&self, defn: &ActiveColumnState) -> Option<String> {
394
+ match &defn.state {
395
+ ActiveColumnStateData::DragOver => Some(self.dragdrop.get_drag_column().unwrap()),
396
+ ActiveColumnStateData::Column(name) => Some(name.to_owned()),
397
+ ActiveColumnStateData::Required => None,
398
+ ActiveColumnStateData::Invalid => None,
399
+ }
400
+ }
401
+
402
+ fn get_table_type(&self, defn: &ActiveColumnState) -> Option<ColumnType> {
403
+ self.get_name(defn)
404
+ .as_ref()
405
+ .and_then(|x| self.session.metadata().get_column_table_type(x))
406
+ }
407
+
408
+ fn _get_view_type(&self, defn: &ActiveColumnState) -> Option<ColumnType> {
409
+ self.get_name(defn)
410
+ .as_ref()
411
+ .and_then(|x| self.session.metadata().get_column_view_type(x))
412
+ }
413
+
414
+ /// Remove an active column from `columns`, or alternatively make this
415
+ /// column the only column in `columns` if the shift key is set (via the
416
+ /// `shift` flag).
417
+ ///
418
+ /// # Arguments
419
+ /// - `name` The name of the column to de-activate, which is a unique ID
420
+ /// with respect to `columns`.
421
+ /// - `shift` whether to toggle or select this column.
422
+ fn deactivate_column(&self, name: String, shift: bool) {
423
+ let mut columns = self.session.get_view_config().columns.clone();
424
+ let max_cols = self
425
+ .renderer
426
+ .metadata()
427
+ .names
428
+ .as_ref()
429
+ .map_or(0, |x| x.len());
430
+
431
+ match self.renderer.metadata().mode {
432
+ ColumnSelectMode::Toggle => {
433
+ let index = columns
434
+ .iter()
435
+ .position(|x| x.as_ref() == Some(&name))
436
+ .unwrap();
437
+
438
+ if max_cols > 0 && index < max_cols - 1 {
439
+ columns[index] = None;
440
+ } else if !shift && columns.len() > 1 {
441
+ columns.retain(|x| x.as_ref() != Some(&name));
442
+ } else if shift {
443
+ columns.clear();
444
+ columns.push(Some(name));
445
+ }
446
+ },
447
+ ColumnSelectMode::Select => {
448
+ columns.retain(|x| x.as_ref() != Some(&name));
449
+ },
450
+ }
451
+ self.apply_columns(columns);
452
+ }
453
+
454
+ fn get_is_required(&self, idx: usize) -> bool {
455
+ let min_cols = self.renderer.metadata().min.unwrap_or(0);
456
+ idx < min_cols
457
+ }
458
+
459
+ fn get_aggregate(&self, name: &str) -> Option<Aggregate> {
460
+ self.session.get_view_config().aggregates.get(name).cloned()
461
+ }
462
+
463
+ fn apply_columns(&self, columns: Vec<Option<String>>) {
464
+ let config = ViewConfigUpdate {
465
+ columns: Some(columns),
466
+ ..ViewConfigUpdate::default()
467
+ };
468
+
469
+ self.update_and_render(config)
470
+ .map(ApiFuture::spawn)
471
+ .unwrap_or_log();
472
+ }
473
+ }
@@ -13,9 +13,13 @@
13
13
  use yew::prelude::*;
14
14
 
15
15
  use super::ColumnLocator;
16
+
16
17
  #[derive(Clone, PartialEq, Properties)]
17
18
  pub struct AddExpressionButtonProps {
19
+ // Fires when this button is clicked.
18
20
  pub on_open_expr_panel: Callback<ColumnLocator>,
21
+
22
+ /// The column this butotn will open configuration for.
19
23
  pub selected_column: Option<ColumnLocator>,
20
24
  }
21
25
 
@@ -44,6 +48,7 @@ pub fn AddExpressionButton(p: &AddExpressionButtonProps) -> Html {
44
48
  let onmousedown = p
45
49
  .on_open_expr_panel
46
50
  .reform(|_| ColumnLocator::NewExpression);
51
+
47
52
  let class = if *is_mouseover || matches!(p.selected_column, Some(ColumnLocator::NewExpression))
48
53
  {
49
54
  classes!("dragdrop-hover")
@@ -15,6 +15,7 @@ use std::rc::Rc;
15
15
 
16
16
  use perspective_client::config::*;
17
17
  use perspective_client::utils::PerspectiveResultExt;
18
+ use perspective_js::utils::ApiFuture;
18
19
  use yew::prelude::*;
19
20
 
20
21
  use crate::components::containers::select::*;
@@ -22,18 +23,21 @@ use crate::components::style::LocalStyle;
22
23
  use crate::model::*;
23
24
  use crate::renderer::*;
24
25
  use crate::session::*;
25
- use crate::*;
26
+ use crate::{PerspectiveProperties, css};
26
27
 
27
- #[derive(Properties)]
28
+ #[derive(Properties, PerspectiveProperties!)]
28
29
  pub struct AggregateSelectorProps {
30
+ /// The name of this aggregate.
29
31
  pub column: String,
32
+
33
+ /// Which aggregate is currently selected.
30
34
  pub aggregate: Option<Aggregate>,
35
+
36
+ // State
31
37
  pub renderer: Renderer,
32
38
  pub session: Session,
33
39
  }
34
40
 
35
- derive_model!(Renderer, Session for AggregateSelectorProps);
36
-
37
41
  impl PartialEq for AggregateSelectorProps {
38
42
  fn eq(&self, rhs: &Self) -> bool {
39
43
  self.column == rhs.column && self.aggregate == rhs.aggregate