@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
@@ -10,26 +10,54 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ mod column_locator;
14
+ mod sheets;
15
+
13
16
  use std::cell::RefCell;
14
17
  use std::collections::{HashMap, HashSet};
15
18
  use std::ops::Deref;
16
19
  use std::rc::Rc;
17
20
 
18
21
  use async_lock::Mutex;
19
- use perspective_js::utils::{ApiFuture, ApiResult, ToApiError, global};
22
+ use perspective_js::utils::{ApiFuture, ApiResult};
20
23
  use wasm_bindgen::prelude::*;
21
24
  use web_sys::*;
22
25
  use yew::html::ImplicitClone;
23
26
 
24
- use crate::components::column_settings_sidebar::ColumnSettingsTab;
25
- use crate::components::viewer::ColumnLocator;
27
+ pub use self::column_locator::{ColumnLocator, ColumnSettingsTab, ColumnTab, OpenColumnSettings};
26
28
  use crate::config::{ColumnConfigUpdate, ColumnConfigValueUpdate, ColumnConfigValues};
27
29
  use crate::utils::*;
28
30
 
31
+ pub type ColumnConfigMap = HashMap<String, ColumnConfigValues>;
32
+
29
33
  /// The available themes as detected in the browser environment or set
30
34
  /// explicitly when CORS prevents detection. Detection is expensive and
31
35
  /// typically must be performed only once, when `document.styleSheets` is
32
36
  /// up-to-date.
37
+ #[derive(Default)]
38
+ struct ThemeData {
39
+ themes: Option<Vec<String>>,
40
+ }
41
+
42
+ /// Actual presentations tate struct with some fields hidden.
43
+ pub struct PresentationHandle {
44
+ viewer_elem: HtmlElement,
45
+ theme_data: Mutex<ThemeData>,
46
+ is_settings_open: RefCell<bool>,
47
+ open_column_settings: RefCell<OpenColumnSettings>,
48
+ columns_config: RefCell<ColumnConfigMap>,
49
+ is_workspace: RefCell<Option<bool>>,
50
+ pub settings_open_changed: PubSub<bool>,
51
+ pub settings_before_open_changed: PubSub<bool>,
52
+ pub column_settings_open_changed: PubSub<(bool, Option<String>)>,
53
+ pub column_settings_updated: PubSub<JsValue>,
54
+ pub theme_config_updated: PubSub<(Rc<Vec<String>>, Option<usize>)>,
55
+ pub visibility_changed: PubSub<bool>,
56
+ pub on_eject: PubSub<()>,
57
+ }
58
+
59
+ /// State object responsible for the non-persistable/gui element state,
60
+ /// including Themes, panel open state and realtive size, title, etc.
33
61
  #[derive(Clone)]
34
62
  pub struct Presentation(Rc<PresentationHandle>);
35
63
 
@@ -49,74 +77,45 @@ impl Deref for Presentation {
49
77
 
50
78
  impl ImplicitClone for Presentation {}
51
79
 
52
- #[derive(Clone, Default, Debug, PartialEq)]
53
- pub struct OpenColumnSettings {
54
- pub locator: Option<ColumnLocator>,
55
- pub tab: Option<ColumnSettingsTab>,
56
- }
57
- impl OpenColumnSettings {
58
- pub fn name(&self) -> Option<String> {
59
- self.locator
60
- .as_ref()
61
- .and_then(|l| l.name())
62
- .map(|s| s.to_owned())
63
- }
64
- }
65
-
66
- pub type ColumnConfigMap = HashMap<String, ColumnConfigValues>;
67
-
68
- pub struct PresentationHandle {
69
- viewer_elem: HtmlElement,
70
- theme_data: Mutex<ThemeData>,
71
- name: RefCell<Option<String>>,
72
- is_settings_open: RefCell<bool>,
73
- open_column_settings: RefCell<OpenColumnSettings>,
74
- is_workspace: RefCell<Option<bool>>,
75
- columns_config: RefCell<ColumnConfigMap>,
76
- pub settings_open_changed: PubSub<bool>,
77
- pub column_settings_open_changed: PubSub<(bool, Option<String>)>,
78
- pub column_settings_updated: PubSub<JsValue>,
79
- pub theme_config_updated: PubSub<(Rc<Vec<String>>, Option<usize>)>,
80
- pub title_changed: PubSub<Option<String>>,
81
- }
82
-
83
- #[derive(Default)]
84
- pub struct ThemeData {
85
- themes: Option<Vec<String>>,
86
- }
87
-
88
80
  impl Presentation {
89
81
  pub fn new(elem: &HtmlElement) -> Self {
90
82
  let theme = Self(Rc::new(PresentationHandle {
91
83
  viewer_elem: elem.clone(),
92
- name: Default::default(),
93
84
  theme_data: Default::default(),
85
+ is_workspace: Default::default(),
94
86
  settings_open_changed: Default::default(),
87
+ settings_before_open_changed: Default::default(),
95
88
  column_settings_open_changed: Default::default(),
96
89
  column_settings_updated: Default::default(),
97
90
  columns_config: Default::default(),
98
91
  is_settings_open: Default::default(),
99
- is_workspace: Default::default(),
100
92
  open_column_settings: Default::default(),
101
93
  theme_config_updated: PubSub::default(),
102
- title_changed: PubSub::default(),
94
+ on_eject: PubSub::default(),
95
+ visibility_changed: PubSub::default(),
103
96
  }));
104
97
 
105
98
  ApiFuture::spawn(theme.clone().init());
106
99
  theme
107
100
  }
108
101
 
109
- pub fn get_title(&self) -> Option<String> {
110
- self.name.borrow().clone()
102
+ pub fn is_visible(&self) -> bool {
103
+ self.viewer_elem
104
+ .offset_parent()
105
+ .map(|x| !x.is_null())
106
+ .unwrap_or(false)
111
107
  }
112
108
 
113
- pub fn set_title(&self, title: Option<String>) {
114
- self.name.borrow_mut().clone_from(&title);
115
- self.title_changed.emit(title);
109
+ pub fn is_active(&self, elem: &Option<Element>) -> bool {
110
+ elem.is_some() && &self.viewer_elem.shadow_root().unwrap().active_element() == elem
116
111
  }
117
112
 
118
113
  pub fn get_is_workspace(&self) -> bool {
119
114
  if self.is_workspace.borrow().is_none() {
115
+ if !self.viewer_elem.is_connected() {
116
+ return false;
117
+ }
118
+
120
119
  let is_workspace = self
121
120
  .viewer_elem
122
121
  .parent_element()
@@ -139,15 +138,16 @@ impl Presentation {
139
138
  *self.is_settings_open.borrow()
140
139
  }
141
140
 
142
- pub fn set_settings_open(&self, open: Option<bool>) -> ApiResult<bool> {
143
- let open_state = open.unwrap_or_else(|| !*self.is_settings_open.borrow());
144
- if *self.is_settings_open.borrow() != open_state {
145
- *self.is_settings_open.borrow_mut() = open_state;
146
- self.set_settings_attribute(open_state);
147
- self.settings_open_changed.emit(open_state);
141
+ pub fn set_settings_before_open(&self, open: bool) {
142
+ if *self.is_settings_open.borrow() != open {
143
+ *self.is_settings_open.borrow_mut() = open;
144
+ self.set_settings_attribute(open);
145
+ self.settings_before_open_changed.emit(open);
148
146
  }
147
+ }
149
148
 
150
- Ok(open_state)
149
+ pub fn set_settings_open(&self, open: bool) {
150
+ self.settings_open_changed.emit(open);
151
151
  }
152
152
 
153
153
  /// Sets the currently opened column settings. Emits an internal event on
@@ -178,7 +178,7 @@ impl Presentation {
178
178
  let mut data = self.0.theme_data.lock().await;
179
179
  if data.themes.is_none() {
180
180
  await_dom_loaded().await?;
181
- let themes = get_theme_names(&self.0.viewer_elem)?;
181
+ let themes = sheets::get_theme_names(&self.0.viewer_elem)?;
182
182
  data.themes = Some(themes);
183
183
  }
184
184
 
@@ -230,6 +230,7 @@ impl Presentation {
230
230
  }
231
231
 
232
232
  pub async fn reset_theme(&self) -> ApiResult<()> {
233
+ *self.0.is_workspace.borrow_mut() = None;
233
234
  let themes = self.get_available_themes().await?;
234
235
  let default_theme = themes.first().map(|x| x.as_str());
235
236
  self.set_theme_name(default_theme).await?;
@@ -242,10 +243,10 @@ impl Presentation {
242
243
  /// A `bool` indicating whether the internal state changed.
243
244
  pub async fn set_theme_name(&self, theme: Option<&str>) -> ApiResult<bool> {
244
245
  let (themes, selected) = self.get_selected_theme_config().await?;
245
- if let Some(x) = selected
246
- && themes.get(x).map(|x| x.as_str()) == theme
247
- {
248
- return Ok(false);
246
+ if let Some(x) = selected {
247
+ if themes.get(x).map(|x| x.as_str()) == theme {
248
+ return Ok(false);
249
+ }
249
250
  }
250
251
 
251
252
  let index = if let Some(theme) = theme {
@@ -308,63 +309,3 @@ impl Presentation {
308
309
  }
309
310
  }
310
311
  }
311
-
312
- macro_rules! iter_index {
313
- ($x:expr) => {
314
- (0..$x.length()).map(|x| $x.item(x))
315
- };
316
- }
317
-
318
- fn fill_rule_theme_names(
319
- themes: &mut Vec<String>,
320
- rule: &Option<CssRule>,
321
- elem: &HtmlElement,
322
- ) -> ApiResult<()> {
323
- if let Some(rule) = rule.as_ref().into_apierror()?.dyn_ref::<CssStyleRule>() {
324
- let txt = rule.selector_text();
325
- if elem.matches(&txt)? {
326
- let style = rule.style();
327
- let x = (0..style.length()).map(|x| style.item(x));
328
- for property in x {
329
- if property == "--theme-name" {
330
- let name = style.get_property_value("--theme-name")?;
331
- let trimmed = name.trim();
332
- themes.push(trimmed[1..trimmed.len() - 1].to_owned());
333
- }
334
- }
335
- }
336
- }
337
-
338
- Ok(())
339
- }
340
-
341
- fn fill_sheet_theme_names(
342
- themes: &mut Vec<String>,
343
- sheet: &Option<StyleSheet>,
344
- elem: &HtmlElement,
345
- ) -> ApiResult<()> {
346
- let sheet = sheet
347
- .as_ref()
348
- .into_apierror()?
349
- .unchecked_ref::<CssStyleSheet>();
350
-
351
- if let Ok(rules) = sheet.css_rules() {
352
- for rule in iter_index!(&rules) {
353
- fill_rule_theme_names(themes, &rule, elem).unwrap_or_default();
354
- }
355
- }
356
-
357
- Ok(())
358
- }
359
-
360
- /// Search the document's `styleSheets` for rules which apply to `elem` and
361
- /// provide the `--theme-name` CSS custom property.
362
- fn get_theme_names(elem: &HtmlElement) -> Result<Vec<String>, JsValue> {
363
- let sheets = global::document().style_sheets();
364
- let mut themes: Vec<String> = vec![];
365
- for sheet in iter_index!(sheets) {
366
- fill_sheet_theme_names(&mut themes, &sheet, elem)?;
367
- }
368
-
369
- Ok(themes)
370
- }
@@ -39,11 +39,26 @@ pub async fn activate_plugin<T>(
39
39
  }
40
40
 
41
41
  let result = task.await;
42
- let first_child = viewer.children().item(0).unwrap();
43
- if first_child != *plugin.unchecked_ref::<Element>() {
44
- viewer.remove_child(&first_child)?;
45
- }
46
-
47
42
  html_plugin.style().set_property("opacity", "1")?;
48
43
  result
49
44
  }
45
+
46
+ pub fn remove_inactive_plugin(
47
+ viewer: &HtmlElement,
48
+ plugin: &JsPerspectiveViewerPlugin,
49
+ plugins: &[JsPerspectiveViewerPlugin],
50
+ ) -> ApiResult<()> {
51
+ for idx in 0..viewer.children().length() {
52
+ let elem = viewer.children().item(idx).unwrap();
53
+ if &elem != plugin.unchecked_ref::<Element>()
54
+ && plugins
55
+ .iter()
56
+ .any(|x| *x.unchecked_ref::<Element>() == elem)
57
+ {
58
+ viewer.remove_child(&elem)?;
59
+ break;
60
+ }
61
+ }
62
+
63
+ Ok(())
64
+ }
@@ -49,152 +49,3 @@ pub async fn get_row_and_col_limits(
49
49
  },
50
50
  }
51
51
  }
52
-
53
- // #[cfg(test)]
54
- // mod tests {
55
- // use super::*;
56
-
57
- // fn closure_helper<T>(x: T) -> Closure<dyn Fn(JsValue) -> js_sys::Promise>
58
- // where
59
- // T: Into<JsValue>,
60
- // {
61
- // let val = x.into();
62
- // Closure::new(move |_: JsValue| {
63
- // clone!(val);
64
- // future_to_promise(async move { Ok(val.clone()) })
65
- // })
66
- // }
67
-
68
- // #[wasm_bindgen_test]
69
- // pub async fn test_emtpy_schema_no_columns() {
70
- // let closure = closure_helper(0);
71
- // let closure2 = closure_helper(json!({}));
72
- // let view = json!({
73
- // "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
74
- // "num_rows": closure.as_ref().unchecked_ref::<JsValue>(),
75
- // "schema": closure2.as_ref().unchecked_ref::<JsValue>()
76
- // })
77
- // .unchecked_into::<JsPerspectiveView>();
78
-
79
- // let reqs = ViewConfigRequirements {
80
- // render_warning: true,
81
- // ..ViewConfigRequirements::default()
82
- // };
83
- // let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
84
- // &reqs).await.unwrap(); assert_eq!(max_cols, None);
85
- // assert_eq!(max_rows, None);
86
- // }
87
-
88
- // #[wasm_bindgen_test]
89
- // pub async fn test_columns_do_not_exceed_max_columns() {
90
- // let closure = closure_helper(1);
91
- // let closure2 = closure_helper(0);
92
- // let closure3 = closure_helper(json!({}));
93
- // let view = json!({
94
- // "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
95
- // "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
96
- // "schema": closure3.as_ref().unchecked_ref::<JsValue>()
97
- // })
98
- // .unchecked_into::<JsPerspectiveView>();
99
-
100
- // let reqs = ViewConfigRequirements {
101
- // max_columns: Some(2),
102
- // render_warning: true,
103
- // ..ViewConfigRequirements::default()
104
- // };
105
-
106
- // let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
107
- // &reqs).await.unwrap(); assert_eq!(max_cols, None);
108
- // assert_eq!(max_rows, None);
109
- // }
110
-
111
- // #[wasm_bindgen_test]
112
- // pub async fn test_columns_exceed_max_columns() {
113
- // let closure = closure_helper(2);
114
- // let closure2 = closure_helper(0);
115
- // let closure3 = closure_helper(json!({}));
116
- // let view = json!({
117
- // "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
118
- // "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
119
- // "schema": closure3.as_ref().unchecked_ref::<JsValue>()
120
- // })
121
- // .unchecked_into::<JsPerspectiveView>();
122
-
123
- // let reqs = ViewConfigRequirements {
124
- // max_columns: Some(1),
125
- // render_warning: true,
126
- // ..ViewConfigRequirements::default()
127
- // };
128
- // let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
129
- // &reqs).await.unwrap(); assert_eq!(max_cols, Some(1));
130
- // assert_eq!(max_rows, None);
131
- // }
132
-
133
- // #[wasm_bindgen_test]
134
- // pub async fn test_when_schema_columns_are_present() {
135
- // let closure = closure_helper(100);
136
- // let closure2 = closure_helper(0);
137
- // let closure3 = closure_helper(json!({"x": "string", "y": "string"}));
138
- // let view = json!({
139
- // "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
140
- // "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
141
- // "schema": closure3.as_ref().unchecked_ref::<JsValue>()
142
- // })
143
- // .unchecked_into::<JsPerspectiveView>();
144
-
145
- // let reqs = ViewConfigRequirements {
146
- // max_columns: Some(3),
147
- // render_warning: true,
148
- // ..ViewConfigRequirements::default()
149
- // };
150
- // let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
151
- // &reqs).await.unwrap(); assert_eq!(max_cols, Some(4));
152
- // assert_eq!(max_rows, None);
153
- // }
154
-
155
- // #[wasm_bindgen_test]
156
- // pub async fn test_when_max_cells_exits() {
157
- // let closure = closure_helper(1);
158
- // let closure2 = closure_helper(0);
159
- // let closure3 = closure_helper(json!({}));
160
- // let view = json!({
161
- // "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
162
- // "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
163
- // "schema": closure3.as_ref().unchecked_ref::<JsValue>()
164
- // })
165
- // .unchecked_into::<JsPerspectiveView>();
166
-
167
- // let reqs = ViewConfigRequirements {
168
- // max_cells: Some(2),
169
- // render_warning: true,
170
- // ..ViewConfigRequirements::default()
171
- // };
172
- // let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
173
- // &reqs).await.unwrap(); assert_eq!(max_cols, None);
174
- // assert_eq!(max_rows, Some(2));
175
- // }
176
-
177
- // #[wasm_bindgen_test]
178
- // pub async fn test_when_columns_exceed_max_columns_and_max_cells_exists()
179
- // { let closure = closure_helper(4);
180
- // let closure2 = closure_helper(0);
181
- // let closure3 = closure_helper(json!({}));
182
- // let view = json!({
183
- // "num_columns": closure.as_ref().unchecked_ref::<JsValue>(),
184
- // "num_rows": closure2.as_ref().unchecked_ref::<JsValue>(),
185
- // "schema": closure3.as_ref().unchecked_ref::<JsValue>()
186
- // })
187
- // .unchecked_into::<JsPerspectiveView>();
188
-
189
- // let reqs = ViewConfigRequirements {
190
- // max_columns: Some(2),
191
- // max_cells: Some(10),
192
- // render_warning: true,
193
- // ..ViewConfigRequirements::default()
194
- // };
195
-
196
- // let (_, _, max_cols, max_rows) = get_row_and_col_limits(&view,
197
- // &reqs).await.unwrap(); assert_eq!(max_cols, Some(2));
198
- // assert_eq!(max_rows, Some(5));
199
- // }
200
- // }
@@ -171,7 +171,7 @@ impl Default for RenderTimerState {
171
171
 
172
172
  impl From<&RenderTimerState> for RenderTimerStats {
173
173
  fn from(value: &RenderTimerState) -> Self {
174
- let perf = global::window().performance().unwrap();
174
+ let perf = global::performance();
175
175
  let now = perf.now();
176
176
  let total_time = now - value.start_time;
177
177
  RenderTimerStats {
@@ -31,7 +31,8 @@ use std::pin::Pin;
31
31
  use std::rc::Rc;
32
32
 
33
33
  use futures::future::{join_all, select_all};
34
- use perspective_client::ViewWindow;
34
+ use perspective_client::utils::*;
35
+ use perspective_client::{View, ViewWindow};
35
36
  use perspective_js::json;
36
37
  use perspective_js::utils::ApiResult;
37
38
  use wasm_bindgen::prelude::*;
@@ -46,12 +47,8 @@ use self::render_timer::*;
46
47
  use crate::config::*;
47
48
  use crate::js::plugin::*;
48
49
  use crate::presentation::ColumnConfigMap;
49
- use crate::session::*;
50
50
  use crate::utils::*;
51
51
 
52
- #[derive(Clone)]
53
- pub struct Renderer(Rc<RendererData>);
54
-
55
52
  /// Immutable state
56
53
  pub struct RendererData {
57
54
  plugin_data: RefCell<RendererMutData>,
@@ -75,6 +72,10 @@ pub struct RendererMutData {
75
72
 
76
73
  type RenderLimits = (usize, usize, Option<usize>, Option<usize>);
77
74
 
75
+ /// The state object responsible for the active [`JsPerspectiveViewerPlugin`].
76
+ #[derive(Clone)]
77
+ pub struct Renderer(Rc<RendererData>);
78
+
78
79
  impl Deref for Renderer {
79
80
  type Target = RendererData;
80
81
 
@@ -135,10 +136,8 @@ impl Renderer {
135
136
  }
136
137
 
137
138
  pub fn delete(&self) -> ApiResult<()> {
138
- // self.get_active_plugin()?.delete();
139
- self.0.plugin_data.borrow().viewer_elem.set_inner_text("");
140
-
141
- // self.plugin_data.borrow_mut().metadata = ViewConfigRequirements::default();
139
+ self.get_active_plugin().map(|x| x.delete()).unwrap_or_log();
140
+ self.plugin_data.borrow().viewer_elem.set_inner_text("");
142
141
  let new_state = Self::new(&self.plugin_data.borrow().viewer_elem);
143
142
  std::mem::swap(
144
143
  &mut *self.plugin_data.borrow_mut(),
@@ -193,6 +192,13 @@ impl Renderer {
193
192
  Ok(result.unwrap())
194
193
  }
195
194
 
195
+ pub fn is_plugin_activated(&self) -> ApiResult<bool> {
196
+ Ok(self
197
+ .get_active_plugin()?
198
+ .unchecked_ref::<HtmlElement>()
199
+ .is_connected())
200
+ }
201
+
196
202
  pub async fn restyle_all(&self, view: &perspective_client::View) -> ApiResult<JsValue> {
197
203
  let plugins = self.get_all_plugins();
198
204
  let tasks = plugins
@@ -235,7 +241,6 @@ impl Renderer {
235
241
  };
236
242
 
237
243
  let idx = self.find_plugin_idx(name).expect("f");
238
-
239
244
  let changed = !matches!(
240
245
  self.0.borrow().plugins_idx,
241
246
  Some(selected_idx) if selected_idx == idx
@@ -318,19 +323,23 @@ impl Renderer {
318
323
  }
319
324
 
320
325
  /// This will take a future which _should_ create a new view and then will
321
- /// draw it.
322
- pub async fn draw(&self, session: impl Future<Output = ApiResult<&Session>>) -> ApiResult<()> {
326
+ /// draw it. As the `session` closure is asynchronous, it can be cancelled
327
+ /// by returning `None`.
328
+ pub async fn draw(
329
+ &self,
330
+ session: impl Future<Output = ApiResult<Option<View>>>,
331
+ ) -> ApiResult<()> {
323
332
  self.draw_plugin(session, false).await
324
333
  }
325
334
 
326
335
  /// This will update an already existing view
327
- pub async fn update(&self, session: &Session) -> ApiResult<()> {
336
+ pub async fn update(&self, session: Option<View>) -> ApiResult<()> {
328
337
  self.draw_plugin(async { Ok(session) }, true).await
329
338
  }
330
339
 
331
340
  async fn draw_plugin(
332
341
  &self,
333
- session: impl Future<Output = ApiResult<&Session>>,
342
+ session: impl Future<Output = ApiResult<Option<View>>>,
334
343
  is_update: bool,
335
344
  ) -> ApiResult<()> {
336
345
  let timer = self.render_timer();
@@ -339,9 +348,10 @@ impl Renderer {
339
348
  set_timeout(timer.get_throttle()).await?;
340
349
  }
341
350
 
342
- if let Some(view) = session.await?.get_view() {
351
+ if let Some(view) = session.await? {
343
352
  timer.capture_time(self.draw_view(&view, is_update)).await
344
353
  } else {
354
+ tracing::debug!("Render skipped, no `View` attached");
345
355
  Ok(())
346
356
  }
347
357
  };
@@ -362,17 +372,23 @@ impl Renderer {
362
372
  let viewer_elem = &self.0.borrow().viewer_elem.clone();
363
373
  if is_update {
364
374
  let task = plugin.update(view.clone().into(), limits.2, limits.3, false);
365
- activate_plugin(viewer_elem, &plugin, task).await
375
+ activate_plugin(viewer_elem, &plugin, task).await?;
366
376
  } else {
367
377
  let task = plugin.draw(view.clone().into(), limits.2, limits.3, false);
368
- activate_plugin(viewer_elem, &plugin, task).await
378
+ activate_plugin(viewer_elem, &plugin, task).await?;
369
379
  }
380
+
381
+ remove_inactive_plugin(
382
+ viewer_elem,
383
+ &plugin,
384
+ self.plugin_data.borrow_mut().plugin_store.plugins(),
385
+ )
370
386
  }
371
387
 
372
388
  /// Decide whether to draw plugin or self first based on whether the panel
373
389
  /// is opening or closing, then draw with a timeout. If the timeout
374
390
  /// triggers, draw self and resolve `on_toggle` but still await the
375
- /// completion of the draw task
391
+ /// completion of the draw task.
376
392
  pub async fn presize(
377
393
  &self,
378
394
  open: bool,
@@ -0,0 +1,50 @@
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
+
13
+ use std::cell::{Ref, RefCell, RefMut};
14
+ use std::rc::Rc;
15
+
16
+ use derivative::Derivative;
17
+ use yew::{AppHandle, Component};
18
+
19
+ #[derive(Default, Derivative)]
20
+ #[derivative(Clone(bound = ""))]
21
+ pub struct Root<T: Component>(Rc<RefCell<Option<AppHandle<T>>>>);
22
+
23
+ impl<T: Component> PartialEq for Root<T> {
24
+ fn eq(&self, other: &Self) -> bool {
25
+ Rc::ptr_eq(&self.0, &other.0)
26
+ }
27
+ }
28
+
29
+ // We want to use libraries that are designed to require thread-safety, in the
30
+ // JavaScript environment that (currently) does not allow threads. If we were
31
+ // to implement a threaded build, we'd need to replace with true
32
+ // synchronization.
33
+ unsafe impl<T: Component> Send for Root<T> {}
34
+ unsafe impl<T: Component> Sync for Root<T> {}
35
+
36
+ impl<T: Component> Root<T> {
37
+ pub fn new(shadow_root: web_sys::Element, props: T::Properties) -> Self {
38
+ Self(Rc::new(RefCell::new(Some(
39
+ yew::Renderer::with_root_and_props(shadow_root, props).render(),
40
+ ))))
41
+ }
42
+
43
+ pub fn borrow(&self) -> Ref<'_, Option<AppHandle<T>>> {
44
+ self.0.borrow()
45
+ }
46
+
47
+ pub fn borrow_mut(&self) -> RefMut<'_, Option<AppHandle<T>>> {
48
+ self.0.borrow_mut()
49
+ }
50
+ }
@@ -111,7 +111,7 @@ pub impl ViewConfigUpdate {
111
111
  }
112
112
  } else if self.columns.is_none() {
113
113
  let mut columns = columns.to_vec();
114
- let initial_len = columns.len();
114
+ let initial_len = self.columns.as_ref().map(|x| x.len()).unwrap_or_default();
115
115
  if let Some(last_filled) = columns.iter().rposition(|x| x.is_some()) {
116
116
  columns.truncate(last_filled + 1);
117
117
  if let ViewConfigRequirements {
@@ -131,10 +131,10 @@ pub impl ViewConfigUpdate {
131
131
  .filter(|x| x.is_some())
132
132
  .collect::<Vec<_>>();
133
133
  }
134
+ }
134
135
 
135
- if initial_len != columns.len() {
136
- self.columns = Some(columns);
137
- }
136
+ if initial_len != columns.len() {
137
+ self.columns = Some(columns);
138
138
  }
139
139
  }
140
140
  }
@@ -12,8 +12,8 @@
12
12
 
13
13
  use perspective_client::config::*;
14
14
 
15
- use crate::dragdrop::{DragEffect, DragTarget};
16
15
  use crate::js::plugin::ViewConfigRequirements;
16
+ use crate::utils::{DragEffect, DragTarget};
17
17
 
18
18
  #[allow(clippy::too_many_arguments)]
19
19
  #[extend::ext]