@perspective-dev/viewer 4.2.0 → 4.4.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 (240) 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/botanical.css +1 -0
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/pro-dark.css +1 -1
  17. package/dist/css/pro.css +1 -1
  18. package/dist/css/solarized-dark.css +1 -1
  19. package/dist/css/solarized.css +1 -1
  20. package/dist/css/themes.css +1 -1
  21. package/dist/css/vaporwave.css +1 -1
  22. package/dist/esm/extensions.d.ts +32 -1
  23. package/dist/esm/perspective-viewer.d.ts +1 -0
  24. package/dist/esm/perspective-viewer.inline.js +2 -2
  25. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  26. package/dist/esm/perspective-viewer.js +2 -2
  27. package/dist/esm/perspective-viewer.js.map +4 -4
  28. package/dist/esm/ts-rs/GroupRollupMode.d.ts +1 -0
  29. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +2 -0
  30. package/dist/wasm/perspective-viewer.d.ts +57 -53
  31. package/dist/wasm/perspective-viewer.js +197 -164
  32. package/dist/wasm/perspective-viewer.wasm +0 -0
  33. package/dist/wasm/perspective-viewer.wasm.d.ts +17 -18
  34. package/package.json +9 -6
  35. package/src/{less/aggregate-selector.less → css/aggregate-selector.css} +23 -20
  36. package/src/css/column-dropdown.css +109 -0
  37. package/src/{less/column-selector.less → css/column-selector.css} +161 -159
  38. package/src/{less/column-settings-panel.less → css/column-settings-panel.css} +69 -59
  39. package/src/{less/column-style.less → css/column-style.css} +52 -66
  40. package/src/{less/column-symbol-attributes.less → css/column-symbol-attributes.css} +15 -14
  41. package/src/css/config-selector.css +441 -0
  42. package/src/{less/containers/dropdown-menu.less → css/containers/dropdown-menu.css} +20 -19
  43. package/src/{less/containers/pairs-list.less → css/containers/pairs-list.css} +13 -12
  44. package/src/{themes/variables.less → css/containers/scroll-panel.css} +25 -22
  45. package/src/{less/containers/split-panel.less → css/containers/split-panel.css} +15 -14
  46. package/src/{less/containers/tabs.less → css/containers/tabs.css} +17 -19
  47. package/src/css/dom/checkbox.css +102 -0
  48. package/src/css/dom/scrollbar.css +35 -0
  49. package/src/{less/dom/select.less → css/dom/select.css} +17 -18
  50. package/src/{less/empty-column.less → css/empty-column.css} +19 -18
  51. package/src/{less/expression-editor.less → css/expression-editor.css} +19 -18
  52. package/src/{less/filter-dropdown.less → css/filter-dropdown.css} +12 -11
  53. package/src/{less/filter-item.less → css/filter-item.css} +16 -15
  54. package/src/{less/form/code-editor.less → css/form/code-editor.css} +26 -30
  55. package/src/{less/form/debug.less → css/form/debug.css} +19 -18
  56. package/src/{less/function-dropdown.less → css/function-dropdown.css} +12 -11
  57. package/src/css/plugin-selector.css +261 -0
  58. package/src/{less/render-warning.less → css/render-warning.css} +18 -17
  59. package/src/{less/status-bar.less → css/status-bar.css} +156 -144
  60. package/src/css/type-icon.css +116 -0
  61. package/src/{less/viewer.less → css/viewer.css} +112 -146
  62. package/src/rust/components/column_dropdown.rs +229 -119
  63. package/src/rust/components/column_selector/active_column.rs +81 -62
  64. package/src/rust/components/column_selector/add_expression_button.rs +1 -0
  65. package/src/rust/components/column_selector/aggregate_selector.rs +25 -15
  66. package/src/rust/components/column_selector/config_selector.rs +374 -185
  67. package/src/rust/components/column_selector/empty_column.rs +2 -2
  68. package/src/rust/components/column_selector/expr_edit_button.rs +8 -2
  69. package/src/rust/components/column_selector/filter_column.rs +37 -26
  70. package/src/rust/components/column_selector/inactive_column.rs +41 -29
  71. package/src/rust/components/column_selector/invalid_column.rs +7 -18
  72. package/src/rust/components/column_selector/pivot_column.rs +21 -10
  73. package/src/rust/components/column_selector/sort_column.rs +23 -13
  74. package/src/rust/components/column_selector.rs +189 -100
  75. package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +1 -1
  76. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +3 -2
  77. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs_item.rs +3 -2
  78. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +2 -3
  79. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +7 -1
  80. package/src/rust/components/column_settings_sidebar/style_tab.rs +153 -112
  81. package/src/rust/components/column_settings_sidebar.rs +91 -53
  82. package/src/rust/components/containers/dragdrop_list.rs +29 -7
  83. package/src/rust/components/containers/scroll_panel.rs +8 -1
  84. package/src/rust/components/containers/select.rs +3 -3
  85. package/src/rust/components/containers/sidebar_close_button.rs +1 -1
  86. package/src/rust/components/containers/split_panel.rs +3 -2
  87. package/src/rust/components/containers/tab_list.rs +1 -1
  88. package/src/rust/components/copy_dropdown.rs +7 -28
  89. package/src/rust/components/datetime_column_style/custom.rs +2 -2
  90. package/src/rust/components/datetime_column_style/simple.rs +2 -2
  91. package/src/rust/components/datetime_column_style.rs +4 -2
  92. package/src/rust/components/editable_header.rs +7 -4
  93. package/src/rust/components/empty_row.rs +1 -1
  94. package/src/rust/components/export_dropdown.rs +4 -30
  95. package/src/rust/components/expression_editor.rs +19 -10
  96. package/src/rust/components/filter_dropdown.rs +246 -102
  97. package/src/rust/components/font_loader.rs +11 -28
  98. package/src/rust/components/form/code_editor.rs +17 -2
  99. package/src/rust/components/form/color_range_selector.rs +19 -6
  100. package/src/rust/components/form/debug.rs +30 -13
  101. package/src/rust/components/function_dropdown.rs +186 -113
  102. package/src/rust/components/main_panel.rs +71 -89
  103. package/src/rust/components/mod.rs +1 -1
  104. package/src/rust/components/modal.rs +7 -1
  105. package/src/rust/components/number_column_style.rs +22 -7
  106. package/src/rust/components/plugin_selector.rs +34 -92
  107. package/src/rust/components/portal.rs +274 -0
  108. package/src/rust/components/render_warning.rs +72 -123
  109. package/src/rust/components/settings_panel.rs +115 -11
  110. package/src/rust/components/status_bar.rs +222 -98
  111. package/src/rust/components/status_bar_counter.rs +8 -20
  112. package/src/rust/components/status_indicator.rs +64 -111
  113. package/src/rust/components/string_column_style.rs +2 -2
  114. package/src/rust/components/style/style_cache.rs +5 -1
  115. package/src/rust/components/viewer.rs +391 -39
  116. package/src/rust/custom_elements/copy_dropdown.rs +102 -21
  117. package/src/rust/custom_elements/export_dropdown.rs +102 -20
  118. package/src/rust/custom_elements/mod.rs +0 -7
  119. package/src/rust/custom_elements/modal.rs +7 -103
  120. package/src/rust/custom_elements/viewer.rs +99 -35
  121. package/src/rust/custom_events.rs +23 -2
  122. package/src/rust/dragdrop.rs +149 -10
  123. package/src/{less/containers/scroll-panel.less → rust/engines.rs} +15 -13
  124. package/src/rust/js/plugin.rs +20 -1
  125. package/src/rust/lib.rs +5 -4
  126. package/src/rust/presentation/props.rs +39 -0
  127. package/src/rust/presentation/sheets.rs +3 -3
  128. package/src/rust/presentation.rs +44 -8
  129. package/src/rust/renderer/limits.rs +32 -3
  130. package/src/{less/dom/scrollbar.less → rust/renderer/props.rs} +18 -19
  131. package/src/rust/renderer/registry.rs +8 -1
  132. package/src/rust/renderer.rs +83 -9
  133. package/src/rust/session/column_defaults_update.rs +18 -0
  134. package/src/rust/session/metadata.rs +23 -2
  135. package/src/rust/session/props.rs +178 -0
  136. package/src/rust/session/replace_expression_update.rs +1 -0
  137. package/src/rust/session.rs +124 -117
  138. package/src/rust/tasks/column_locator.rs +133 -0
  139. package/src/rust/{model → tasks}/columns_iter_set.rs +14 -23
  140. package/src/rust/{model → tasks}/edit_expression.rs +34 -10
  141. package/src/rust/{model → tasks}/eject.rs +2 -2
  142. package/src/rust/{model → tasks}/get_viewer_config.rs +0 -11
  143. package/src/rust/{model → tasks}/intersection_observer.rs +22 -4
  144. package/src/{less/containers/radio-list.less → rust/tasks/is_invalid_drop.rs} +21 -14
  145. package/src/rust/tasks/mod.rs +52 -0
  146. package/src/rust/{model → tasks}/plugin_column_styles.rs +69 -46
  147. package/src/rust/{model → tasks}/resize_observer.rs +39 -6
  148. package/src/rust/{model → tasks}/send_plugin_config.rs +1 -1
  149. package/src/rust/tasks/structural.rs +53 -0
  150. package/src/rust/utils/mod.rs +4 -0
  151. package/src/rust/utils/modal_position.rs +110 -0
  152. package/src/rust/utils/ptr_eq_rc.rs +74 -0
  153. package/src/rust/utils/pubsub.rs +11 -1
  154. package/src/svg/bg-pattern.png +0 -0
  155. package/src/svg/close-icon.svg +1 -1
  156. package/src/svg/expression.svg +1 -1
  157. package/src/svg/mega-menu-icons-candlestick.svg +1 -1
  158. package/src/svg/mega-menu-icons-datagrid.svg +1 -2
  159. package/src/svg/mega-menu-icons-heatmap.svg +1 -1
  160. package/src/svg/mega-menu-icons-map-scatter.svg +1 -1
  161. package/src/svg/mega-menu-icons-ohlc.svg +1 -1
  162. package/src/svg/mega-menu-icons-sunburst.svg +1 -1
  163. package/src/svg/mega-menu-icons-treemap.svg +1 -1
  164. package/src/svg/mega-menu-icons-x-bar.svg +1 -1
  165. package/src/svg/mega-menu-icons-x-y-line.svg +1 -1
  166. package/src/svg/mega-menu-icons-x-y-scatter.svg +1 -1
  167. package/src/svg/mega-menu-icons-y-area.svg +1 -1
  168. package/src/svg/mega-menu-icons-y-bar.svg +1 -1
  169. package/src/svg/mega-menu-icons-y-line.svg +1 -1
  170. package/src/svg/mega-menu-icons-y-scatter.svg +1 -1
  171. package/src/svg/radio-hover.svg +1 -1
  172. package/src/svg/radio-off.svg +1 -1
  173. package/src/svg/radio-on.svg +1 -1
  174. package/src/themes/botanical.css +157 -0
  175. package/src/themes/defaults.css +139 -0
  176. package/src/themes/dracula.css +233 -0
  177. package/src/themes/gruvbox-dark.css +255 -0
  178. package/src/themes/gruvbox.css +134 -0
  179. package/src/themes/icons.css +124 -0
  180. package/src/themes/intl/de.css +102 -0
  181. package/src/themes/intl/es.css +102 -0
  182. package/src/themes/intl/fr.css +102 -0
  183. package/src/themes/intl/ja.css +102 -0
  184. package/src/themes/intl/pt.css +102 -0
  185. package/src/themes/intl/zh.css +102 -0
  186. package/src/themes/intl.css +102 -0
  187. package/src/themes/monokai.css +233 -0
  188. package/src/themes/pro-dark.css +158 -0
  189. package/src/themes/{themes.less → pro.css} +17 -20
  190. package/src/themes/solarized-dark.css +135 -0
  191. package/src/themes/solarized.css +95 -0
  192. package/src/themes/themes.css +22 -0
  193. package/src/themes/vaporwave.css +256 -0
  194. package/src/ts/extensions.ts +73 -2
  195. package/src/ts/perspective-viewer.ts +1 -0
  196. package/src/ts/ts-rs/GroupRollupMode.ts +3 -0
  197. package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -1
  198. package/tsconfig.json +1 -0
  199. package/dist/css/variables.css +0 -0
  200. package/src/less/column-dropdown.less +0 -95
  201. package/src/less/config-selector.less +0 -363
  202. package/src/less/dom/checkbox.less +0 -100
  203. package/src/less/plugin-selector.less +0 -183
  204. package/src/less/type-icon.less +0 -68
  205. package/src/rust/components/error_message.rs +0 -56
  206. package/src/rust/custom_elements/column_dropdown.rs +0 -123
  207. package/src/rust/custom_elements/filter_dropdown.rs +0 -179
  208. package/src/rust/custom_elements/function_dropdown.rs +0 -115
  209. package/src/rust/model/column_locator.rs +0 -82
  210. package/src/rust/model/is_invalid_drop.rs +0 -36
  211. package/src/rust/model/mod.rs +0 -100
  212. package/src/rust/model/reset_all.rs +0 -38
  213. package/src/rust/model/structural.rs +0 -244
  214. package/src/themes/dracula.less +0 -101
  215. package/src/themes/gruvbox-dark.less +0 -116
  216. package/src/themes/gruvbox.less +0 -152
  217. package/src/themes/icons.less +0 -130
  218. package/src/themes/intl/de.less +0 -102
  219. package/src/themes/intl/es.less +0 -102
  220. package/src/themes/intl/fr.less +0 -102
  221. package/src/themes/intl/ja.less +0 -102
  222. package/src/themes/intl/pt.less +0 -102
  223. package/src/themes/intl/zh.less +0 -102
  224. package/src/themes/intl.less +0 -102
  225. package/src/themes/monokai.less +0 -107
  226. package/src/themes/pro-dark.less +0 -147
  227. package/src/themes/pro.less +0 -186
  228. package/src/themes/solarized-dark.less +0 -78
  229. package/src/themes/solarized.less +0 -102
  230. package/src/themes/vaporwave.less +0 -145
  231. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline0.js +0 -0
  232. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline1.js +0 -0
  233. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline2.js +0 -0
  234. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline3.js +0 -0
  235. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-68fef752754ffbc6}/inline4.js +0 -0
  236. /package/src/rust/{model → tasks}/copy_export.rs +0 -0
  237. /package/src/rust/{model → tasks}/export_app.rs +0 -0
  238. /package/src/rust/{model → tasks}/export_method.rs +0 -0
  239. /package/src/rust/{model → tasks}/restore_and_render.rs +0 -0
  240. /package/src/rust/{model → tasks}/update_and_render.rs +0 -0
@@ -10,142 +10,215 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- use perspective_client::config::CompletionItemSuggestion;
13
+ use std::cell::RefCell;
14
+ use std::rc::Rc;
15
+
16
+ use perspective_client::config::{COMPLETIONS, CompletionItemSuggestion};
17
+ use perspective_js::utils::ApiResult;
14
18
  use web_sys::*;
19
+ use yew::html::ImplicitClone;
15
20
  use yew::prelude::*;
16
21
 
17
- use super::modal::*;
18
- use crate::utils::WeakScope;
22
+ use super::portal::PortalModal;
23
+ use crate::utils::*;
19
24
 
20
25
  static CSS: &str = include_str!(concat!(env!("OUT_DIR"), "/css/function-dropdown.css"));
21
26
 
22
- #[derive(Properties, PartialEq)]
23
- pub struct FunctionDropDownProps {
24
- #[prop_or_default]
25
- pub weak_link: WeakScope<FunctionDropDown>,
27
+ #[derive(Default)]
28
+ struct FunctionDropDownState {
29
+ values: Vec<CompletionItemSuggestion>,
30
+ selected: usize,
31
+ on_select: Option<Callback<CompletionItemSuggestion>>,
32
+ target: Option<HtmlElement>,
26
33
  }
27
34
 
28
- impl ModalLink<FunctionDropDown> for FunctionDropDownProps {
29
- fn weak_link(&self) -> &'_ WeakScope<FunctionDropDown> {
30
- &self.weak_link
31
- }
35
+ #[derive(Clone, Default)]
36
+ pub struct FunctionDropDownElement {
37
+ state: Rc<RefCell<FunctionDropDownState>>,
38
+ notify: Rc<PubSub<()>>,
32
39
  }
33
40
 
34
- pub enum FunctionDropDownMsg {
35
- SetValues(Vec<CompletionItemSuggestion>),
36
- SetCallback(Callback<CompletionItemSuggestion>),
37
- ItemDown,
38
- ItemUp,
39
- ItemSelect,
41
+ impl PartialEq for FunctionDropDownElement {
42
+ fn eq(&self, other: &Self) -> bool {
43
+ Rc::ptr_eq(&self.state, &other.state)
44
+ }
40
45
  }
41
46
 
42
- pub struct FunctionDropDown {
43
- values: Option<Vec<CompletionItemSuggestion>>,
44
- selected: usize,
45
- on_select: Option<Callback<CompletionItemSuggestion>>,
46
- }
47
+ impl ImplicitClone for FunctionDropDownElement {}
47
48
 
48
- impl Component for FunctionDropDown {
49
- type Message = FunctionDropDownMsg;
50
- type Properties = FunctionDropDownProps;
49
+ impl FunctionDropDownElement {
50
+ pub fn reautocomplete(&self) {
51
+ self.notify.emit(());
52
+ }
51
53
 
52
- fn create(ctx: &Context<Self>) -> Self {
53
- ctx.set_modal_link();
54
- Self {
55
- values: Some(vec![]),
56
- selected: 0,
57
- on_select: None,
54
+ pub fn autocomplete(
55
+ &self,
56
+ input: String,
57
+ target: HtmlElement,
58
+ callback: Callback<CompletionItemSuggestion>,
59
+ ) -> ApiResult<()> {
60
+ let values = filter_values(&input);
61
+ if values.is_empty() {
62
+ self.hide()?;
63
+ } else {
64
+ let mut s = self.state.borrow_mut();
65
+ s.values = values;
66
+ s.selected = 0;
67
+ s.on_select = Some(callback);
68
+ s.target = Some(target);
69
+ drop(s);
70
+ self.notify.emit(());
58
71
  }
72
+
73
+ Ok(())
59
74
  }
60
75
 
61
- fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
62
- match msg {
63
- FunctionDropDownMsg::SetCallback(callback) => {
64
- self.on_select = Some(callback);
65
- false
66
- },
67
- FunctionDropDownMsg::SetValues(values) => {
68
- self.values = Some(values);
69
- self.selected = 0;
70
- true
71
- },
72
- FunctionDropDownMsg::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);
81
- false
82
- },
83
- }
84
- } else {
85
- console::error_1(&"No Values".into());
86
- false
87
- }
88
- },
89
- FunctionDropDownMsg::ItemDown => {
90
- self.selected += 1;
91
- if let Some(ref values) = self.values
92
- && self.selected >= values.len()
93
- {
94
- self.selected = 0;
95
- };
96
-
97
- true
98
- },
99
- FunctionDropDownMsg::ItemUp => {
100
- if let Some(ref values) = self.values
101
- && self.selected < 1
102
- {
103
- self.selected = values.len();
104
- }
105
-
106
- self.selected -= 1;
107
- true
108
- },
76
+ pub fn item_select(&self) {
77
+ let state = self.state.borrow();
78
+ if let Some(value) = state.values.get(state.selected)
79
+ && let Some(ref cb) = state.on_select
80
+ {
81
+ cb.emit(*value);
109
82
  }
110
83
  }
111
84
 
112
- fn changed(&mut self, _ctx: &Context<Self>, _old: &Self::Properties) -> bool {
113
- false
85
+ pub fn item_down(&self) {
86
+ let mut state = self.state.borrow_mut();
87
+ state.selected += 1;
88
+ if state.selected >= state.values.len() {
89
+ state.selected = 0;
90
+ }
91
+
92
+ drop(state);
93
+ self.notify.emit(());
114
94
  }
115
95
 
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
- { for values
121
- .iter()
122
- .enumerate()
123
- .map(|(idx, value)| {
124
- let click = self.on_select.as_ref().unwrap().reform({
125
- let value = *value;
126
- move |_: MouseEvent| value
127
- });
128
-
129
- html! {
130
- if idx == self.selected {
131
- <div onmousedown={ click } class="selected">
132
- <span style="font-weight:500">{ value.label }</span>
133
- <br/>
134
- <span style="padding-left:12px">{ value.documentation }</span>
135
- </div>
136
- } else {
137
- <div onmousedown={ click }>
138
- <span style="font-weight:500">{ value.label }</span>
139
- <br/>
140
- <span style="padding-left:12px">{ value.documentation }</span>
141
- </div>
142
- }
143
- }
144
- }) }
145
- }
146
- }
96
+ pub fn item_up(&self) {
97
+ let mut state = self.state.borrow_mut();
98
+ if state.selected < 1 {
99
+ state.selected = state.values.len();
100
+ }
101
+
102
+ state.selected -= 1;
103
+ drop(state);
104
+ self.notify.emit(());
105
+ }
106
+
107
+ pub fn hide(&self) -> ApiResult<()> {
108
+ self.state.borrow_mut().target = None;
109
+ self.notify.emit(());
110
+ Ok(())
111
+ }
112
+ }
113
+
114
+ #[derive(Properties, PartialEq)]
115
+ pub struct FunctionDropDownPortalProps {
116
+ pub element: FunctionDropDownElement,
117
+ pub theme: String,
118
+ }
119
+
120
+ pub struct FunctionDropDownPortal {
121
+ _sub: Subscription,
122
+ }
123
+
124
+ impl Component for FunctionDropDownPortal {
125
+ type Message = ();
126
+ type Properties = FunctionDropDownPortalProps;
127
+
128
+ fn create(ctx: &Context<Self>) -> Self {
129
+ let link = ctx.link().clone();
130
+ let sub = ctx
131
+ .props()
132
+ .element
133
+ .notify
134
+ .add_listener(move |()| link.send_message(()));
135
+ Self { _sub: sub }
136
+ }
137
+
138
+ fn update(&mut self, _ctx: &Context<Self>, _msg: ()) -> bool {
139
+ true
140
+ }
141
+
142
+ fn view(&self, ctx: &Context<Self>) -> Html {
143
+ let state = ctx.props().element.state.borrow();
144
+ let target = state.target.clone();
145
+ let on_close = {
146
+ let element = ctx.props().element.clone();
147
+ Callback::from(move |()| {
148
+ let _ = element.hide();
149
+ })
147
150
  };
148
151
 
149
- html! { <><style>{ CSS }</style>{ body }</> }
152
+ if target.is_some() {
153
+ let values = state.values.clone();
154
+ let selected = state.selected;
155
+ let on_select = state.on_select.clone();
156
+ drop(state);
157
+
158
+ html! {
159
+ <PortalModal
160
+ tag_name="perspective-dropdown"
161
+ {target}
162
+ own_focus=false
163
+ {on_close}
164
+ theme={ctx.props().theme.clone()}
165
+ >
166
+ <FunctionDropDownView {values} {selected} {on_select} />
167
+ </PortalModal>
168
+ }
169
+ } else {
170
+ html! {}
171
+ }
150
172
  }
151
173
  }
174
+
175
+ #[derive(Properties, PartialEq)]
176
+ struct FunctionDropDownViewProps {
177
+ values: Vec<CompletionItemSuggestion>,
178
+ selected: usize,
179
+ on_select: Option<Callback<CompletionItemSuggestion>>,
180
+ }
181
+
182
+ #[function_component]
183
+ fn FunctionDropDownView(props: &FunctionDropDownViewProps) -> Html {
184
+ let body = html! {
185
+ if !props.values.is_empty() {
186
+ { for props.values
187
+ .iter()
188
+ .enumerate()
189
+ .map(|(idx, value)| {
190
+ let click = props.on_select.as_ref().unwrap().reform({
191
+ let value = *value;
192
+ move |_: MouseEvent| value
193
+ });
194
+
195
+ html! {
196
+ if idx == props.selected {
197
+ <div onmousedown={click} class="selected">
198
+ <span style="font-weight:500">{ value.label }</span>
199
+ <br/>
200
+ <span style="padding-left:12px">{ value.documentation }</span>
201
+ </div>
202
+ } else {
203
+ <div onmousedown={click}>
204
+ <span style="font-weight:500">{ value.label }</span>
205
+ <br/>
206
+ <span style="padding-left:12px">{ value.documentation }</span>
207
+ </div>
208
+ }
209
+ }
210
+ }) }
211
+ }
212
+ };
213
+
214
+ html! { <><style>{ CSS }</style>{ body }</> }
215
+ }
216
+
217
+ fn filter_values(input: &str) -> Vec<CompletionItemSuggestion> {
218
+ let input = input.to_lowercase();
219
+ COMPLETIONS
220
+ .iter()
221
+ .filter(|x| x.label.to_lowercase().starts_with(&input))
222
+ .cloned()
223
+ .collect::<Vec<_>>()
224
+ }
@@ -10,24 +10,48 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- use futures::channel::oneshot::*;
14
13
  use perspective_js::utils::*;
15
14
  use wasm_bindgen::prelude::*;
16
15
  use yew::prelude::*;
17
16
 
18
17
  use super::render_warning::RenderWarning;
19
18
  use super::status_bar::StatusBar;
20
- use crate::PerspectiveProperties;
21
19
  use crate::custom_events::CustomEvents;
22
20
  use crate::presentation::Presentation;
21
+ use crate::renderer::limits::RenderLimits;
23
22
  use crate::renderer::*;
24
- use crate::session::*;
23
+ use crate::session::{Session, TableErrorState, TableLoadState, ViewStats};
25
24
  use crate::utils::*;
26
25
 
27
- #[derive(Clone, Properties, PerspectiveProperties!)]
26
+ #[derive(Clone, Properties)]
28
27
  pub struct MainPanelProps {
29
28
  pub on_settings: Callback<()>,
30
29
 
30
+ /// Reset callback forwarded from the root component. Fired when the user
31
+ /// clicks the reset button; `bool` is `true` for a full reset (expressions
32
+ /// + column configs), `false` for config-only.
33
+ pub on_reset: Callback<bool>,
34
+
35
+ /// Render-limit dimensions forwarded from the root's `RendererProps`.
36
+ /// `Some` when the active plugin is capping the rendered row/column count;
37
+ /// `None` when no limits are active (e.g. after a plugin change).
38
+ pub render_limits: Option<RenderLimits>,
39
+
40
+ /// Value props from root's `SessionProps`, threaded to `StatusBar` /
41
+ /// `StatusIndicator`.
42
+ pub has_table: Option<TableLoadState>,
43
+ pub is_errored: bool,
44
+ pub stats: Option<ViewStats>,
45
+ pub update_count: u32,
46
+ pub error: Option<TableErrorState>,
47
+ pub title: Option<String>,
48
+
49
+ /// Value props from root's `PresentationProps`, threaded to `StatusBar`.
50
+ pub is_settings_open: bool,
51
+ pub selected_theme: Option<String>,
52
+ pub available_themes: PtrEqRc<Vec<String>>,
53
+ pub is_workspace: bool,
54
+
31
55
  /// State
32
56
  pub custom_events: CustomEvents,
33
57
  pub session: Session,
@@ -36,28 +60,33 @@ pub struct MainPanelProps {
36
60
  }
37
61
 
38
62
  impl PartialEq for MainPanelProps {
39
- fn eq(&self, _rhs: &Self) -> bool {
40
- false
63
+ fn eq(&self, rhs: &Self) -> bool {
64
+ self.has_table == rhs.has_table
65
+ && self.is_errored == rhs.is_errored
66
+ && self.stats == rhs.stats
67
+ && self.update_count == rhs.update_count
68
+ && self.error == rhs.error
69
+ && self.title == rhs.title
70
+ && self.is_settings_open == rhs.is_settings_open
71
+ && self.selected_theme == rhs.selected_theme
72
+ && self.available_themes == rhs.available_themes
73
+ && self.is_workspace == rhs.is_workspace
74
+ && self.render_limits == rhs.render_limits
41
75
  }
42
76
  }
43
77
 
44
78
  impl MainPanelProps {
45
79
  fn is_title(&self) -> bool {
46
- self.session.get_title().is_some()
80
+ self.title.is_some()
47
81
  }
48
82
  }
49
83
 
50
84
  #[derive(Debug)]
51
85
  pub enum MainPanelMsg {
52
- Reset(bool, Option<Sender<()>>),
53
- RenderLimits(Option<(usize, usize, Option<usize>, Option<usize>)>),
54
86
  PointerEvent(web_sys::PointerEvent),
55
- Error,
56
87
  }
57
88
 
58
89
  pub struct MainPanel {
59
- _subscriptions: [Subscription; 2],
60
- dimensions: Option<(usize, usize, Option<usize>, Option<usize>)>,
61
90
  main_panel_ref: NodeRef,
62
91
  }
63
92
 
@@ -65,85 +94,14 @@ impl Component for MainPanel {
65
94
  type Message = MainPanelMsg;
66
95
  type Properties = MainPanelProps;
67
96
 
68
- fn create(ctx: &Context<Self>) -> Self {
69
- let session_sub = {
70
- let callback = ctx.link().callback(move |(_, render_limits)| {
71
- MainPanelMsg::RenderLimits(Some(render_limits))
72
- });
73
-
74
- ctx.props()
75
- .renderer
76
- .render_limits_changed
77
- .add_listener(callback)
78
- };
79
-
80
- let error_sub = ctx
81
- .props()
82
- .session
83
- .table_errored
84
- .add_listener(ctx.link().callback(|_| MainPanelMsg::Error));
85
-
97
+ fn create(_ctx: &Context<Self>) -> Self {
86
98
  Self {
87
- _subscriptions: [session_sub, error_sub],
88
- dimensions: None,
89
99
  main_panel_ref: NodeRef::default(),
90
100
  }
91
101
  }
92
102
 
93
103
  fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
94
104
  match msg {
95
- MainPanelMsg::Error => true,
96
- MainPanelMsg::Reset(all, sender) => {
97
- ctx.props().presentation.set_open_column_settings(None);
98
-
99
- clone!(
100
- ctx.props().renderer,
101
- ctx.props().session,
102
- ctx.props().presentation
103
- );
104
-
105
- ApiFuture::spawn(async move {
106
- session
107
- .reset(ResetOptions {
108
- config: true,
109
- expressions: all,
110
- ..ResetOptions::default()
111
- })
112
- .await?;
113
- let columns_config = if all {
114
- presentation.reset_columns_configs();
115
- None
116
- } else {
117
- Some(presentation.all_columns_configs())
118
- };
119
-
120
- renderer.reset(columns_config.as_ref()).await?;
121
- presentation.reset_available_themes(None).await;
122
- if all {
123
- presentation.reset_theme().await?;
124
- }
125
-
126
- let result = renderer.draw(session.validate().await?.create_view()).await;
127
- if let Some(sender) = sender {
128
- sender.send(()).unwrap();
129
- }
130
-
131
- renderer.reset_changed.emit(());
132
- result
133
- });
134
-
135
- false
136
- },
137
-
138
- MainPanelMsg::RenderLimits(dimensions) => {
139
- if self.dimensions != dimensions {
140
- self.dimensions = dimensions;
141
- true
142
- } else {
143
- false
144
- }
145
- },
146
-
147
105
  MainPanelMsg::PointerEvent(event) => {
148
106
  if event.target().map(JsValue::from)
149
107
  == self
@@ -175,8 +133,8 @@ impl Component for MainPanel {
175
133
  ..
176
134
  } = ctx.props();
177
135
 
178
- let is_settings_open =
179
- ctx.props().presentation.is_settings_open() && ctx.props().session.has_table();
136
+ let is_settings_open = ctx.props().is_settings_open
137
+ && matches!(ctx.props().has_table, Some(TableLoadState::Loaded));
180
138
 
181
139
  let on_settings = (!is_settings_open).then(|| ctx.props().on_settings.clone());
182
140
 
@@ -189,14 +147,35 @@ impl Component for MainPanel {
189
147
  class.push("titled");
190
148
  }
191
149
 
192
- let on_reset = ctx.link().callback(|all| MainPanelMsg::Reset(all, None));
193
150
  let pointerdown = ctx.link().callback(MainPanelMsg::PointerEvent);
151
+ let on_dismiss_warning = {
152
+ clone!(renderer, session);
153
+ Callback::from(move |_: ()| {
154
+ clone!(renderer, session);
155
+ ApiFuture::spawn(async move {
156
+ renderer.disable_active_plugin_render_warning();
157
+ let view_task = session.get_view();
158
+ renderer.update(view_task).await
159
+ });
160
+ })
161
+ };
162
+
194
163
  html! {
195
164
  <div id="main_column">
196
165
  <StatusBar
197
166
  id="status_bar"
198
167
  {on_settings}
199
- on_reset={on_reset.clone()}
168
+ on_reset={ctx.props().on_reset.clone()}
169
+ has_table={ctx.props().has_table.clone()}
170
+ is_errored={ctx.props().is_errored}
171
+ stats={ctx.props().stats.clone()}
172
+ update_count={ctx.props().update_count}
173
+ error={ctx.props().error.clone()}
174
+ title={ctx.props().title.clone()}
175
+ is_settings_open={ctx.props().is_settings_open}
176
+ selected_theme={ctx.props().selected_theme.clone()}
177
+ available_themes={ctx.props().available_themes.clone()}
178
+ is_workspace={ctx.props().is_workspace}
200
179
  {custom_events}
201
180
  {presentation}
202
181
  {renderer}
@@ -208,7 +187,10 @@ impl Component for MainPanel {
208
187
  {class}
209
188
  onpointerdown={pointerdown}
210
189
  >
211
- <RenderWarning {renderer} {session} dimensions={self.dimensions} />
190
+ <RenderWarning
191
+ on_dismiss={on_dismiss_warning}
192
+ dimensions={ctx.props().render_limits}
193
+ />
212
194
  <slot />
213
195
  </div>
214
196
  </div>
@@ -22,7 +22,6 @@ pub mod copy_dropdown;
22
22
  pub mod datetime_column_style;
23
23
  pub mod editable_header;
24
24
  pub mod empty_row;
25
- pub mod error_message;
26
25
  pub mod export_dropdown;
27
26
  pub mod expression_editor;
28
27
  pub mod filter_dropdown;
@@ -33,6 +32,7 @@ pub mod main_panel;
33
32
  pub mod modal;
34
33
  pub mod number_column_style;
35
34
  pub mod plugin_selector;
35
+ pub mod portal;
36
36
  pub mod render_warning;
37
37
  pub mod settings_panel;
38
38
  pub mod status_bar;
@@ -78,7 +78,7 @@ where
78
78
  } => {
79
79
  let opacity = if visible { "" } else { ";opacity:0" };
80
80
  self.css = format!(":host{{top:{top}px;left:{left}px{opacity}}}");
81
- self.rev_vert.0.set(rev_vert);
81
+ self.rev_vert.set(rev_vert);
82
82
  true
83
83
  },
84
84
  ModalMsg::SubMsg(msg) => {
@@ -117,6 +117,12 @@ pub struct ModalOrientation(Rc<Cell<bool>>);
117
117
 
118
118
  impl ImplicitClone for ModalOrientation {}
119
119
 
120
+ impl ModalOrientation {
121
+ pub fn set(&self, value: bool) {
122
+ self.0.set(value);
123
+ }
124
+ }
125
+
120
126
  impl From<ModalOrientation> for bool {
121
127
  fn from(x: ModalOrientation) -> Self {
122
128
  x.0.get()
@@ -10,6 +10,7 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ use perspective_client::config::Scalar;
13
14
  use yew::prelude::*;
14
15
  use yew::*;
15
16
 
@@ -65,8 +66,25 @@ impl ModalLink<NumberColumnStyle> for NumberColumnStyleProps {
65
66
  }
66
67
 
67
68
  impl PartialEq for NumberColumnStyleProps {
68
- fn eq(&self, _other: &Self) -> bool {
69
- false
69
+ fn eq(&self, other: &Self) -> bool {
70
+ self.config == other.config
71
+ && self.default_config == other.default_config
72
+ && self.column_name == other.column_name
73
+ }
74
+ }
75
+
76
+ fn scalar_to_f64(scalar: &Scalar) -> f64 {
77
+ match scalar {
78
+ Scalar::Float(x) => *x,
79
+ Scalar::String(x) => x.parse::<f64>().unwrap_or_default(),
80
+ Scalar::Bool(x) => {
81
+ if *x {
82
+ 1.0
83
+ } else {
84
+ 0.0
85
+ }
86
+ },
87
+ Scalar::Null => 0.0,
70
88
  }
71
89
  }
72
90
 
@@ -76,12 +94,9 @@ fn set_default_gradient(session: &Session, ctx: &Context<NumberColumnStyle>) {
76
94
  ctx.link().send_future(async move {
77
95
  let view = session.get_view().unwrap();
78
96
  let min_max = view.get_min_max(column_name).await.unwrap();
79
- let abs_max = min_max
80
- .0
81
- .parse::<f64>()
82
- .unwrap_or_default()
97
+ let abs_max = scalar_to_f64(&min_max.0)
83
98
  .abs()
84
- .max(min_max.1.parse::<f64>().unwrap_or_default().abs());
99
+ .max(scalar_to_f64(&min_max.1).abs());
85
100
 
86
101
  let gradient = (abs_max * 100.).round() / 100.;
87
102
  NumberColumnStyleMsg::DefaultGradientChanged(gradient)