@perspective-dev/viewer 4.0.1 → 4.1.1

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 (183) 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 +1242 -753
  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 +367 -169
  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/get_viewer_config.rs +4 -28
  126. package/src/rust/model/intersection_observer.rs +20 -8
  127. package/src/rust/model/mod.rs +11 -4
  128. package/src/rust/model/plugin_column_styles.rs +0 -31
  129. package/src/rust/model/reset_all.rs +38 -0
  130. package/src/rust/model/resize_observer.rs +34 -7
  131. package/src/rust/model/restore_and_render.rs +12 -7
  132. package/src/rust/{utils/scope.rs → model/send_plugin_config.rs} +32 -35
  133. package/src/rust/model/structural.rs +194 -23
  134. package/src/rust/model/update_and_render.rs +14 -4
  135. package/src/rust/{model/create_col.rs → presentation/column_locator.rs} +73 -42
  136. package/src/rust/{utils/wasm_abi.rs → presentation/sheets.rs} +54 -40
  137. package/src/rust/presentation.rs +60 -119
  138. package/src/rust/renderer/activate.rs +20 -5
  139. package/src/rust/renderer/limits.rs +0 -149
  140. package/src/rust/renderer/render_timer.rs +1 -1
  141. package/src/rust/renderer.rs +34 -18
  142. package/src/rust/root.rs +50 -0
  143. package/src/rust/session/column_defaults_update.rs +4 -4
  144. package/src/rust/session/drag_drop_update.rs +1 -1
  145. package/src/rust/session/metadata.rs +3 -17
  146. package/src/rust/session/replace_expression_update.rs +1 -2
  147. package/src/rust/session.rs +162 -82
  148. package/src/rust/utils/browser/blob.rs +16 -2
  149. package/src/rust/utils/browser/download.rs +1 -0
  150. package/src/rust/{components/column_settings_sidebar/mod.rs → utils/browser/dragdrop.rs} +14 -5
  151. package/src/rust/utils/browser/mod.rs +8 -4
  152. package/src/rust/utils/browser/selection.rs +5 -0
  153. package/src/rust/utils/custom_element.rs +28 -13
  154. package/src/rust/utils/datetime.rs +5 -0
  155. package/src/rust/utils/debounce.rs +7 -1
  156. package/src/rust/utils/hooks/use_async_callback.rs +7 -17
  157. package/src/rust/utils/mod.rs +28 -40
  158. package/src/rust/utils/number_format.rs +6 -5
  159. package/src/rust/utils/pubsub.rs +15 -10
  160. package/src/rust/utils/weak_scope.rs +11 -1
  161. package/src/svg/bookmark-icon.svg +4 -0
  162. package/src/svg/drag-handle copy.svg +10 -0
  163. package/src/svg/drawer-tab-hover.svg +5 -7
  164. package/src/svg/drawer-tab-invert-hover.svg +4 -8
  165. package/src/svg/drawer-tab-invert.svg +4 -7
  166. package/src/svg/drawer-tab.svg +4 -6
  167. package/src/svg/status_ok.svg +24 -24
  168. package/src/ts/extensions.ts +51 -3
  169. package/src/ts/perspective-viewer.ts +2 -14
  170. package/src/ts/plugin.ts +1 -1
  171. package/src/ts/ts-rs/ViewerConfigUpdate.ts +1 -1
  172. package/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +0 -177
  173. package/src/rust/components/containers/tests/mod.rs +0 -11
  174. package/src/rust/components/containers/tests/split_panel.rs +0 -91
  175. package/src/rust/js/testing.rs +0 -149
  176. package/src/rust/utils/tee.rs +0 -88
  177. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-11a3c51b6310ee99}/inline0.js +0 -0
  178. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-11a3c51b6310ee99}/inline1.js +0 -0
  179. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-11a3c51b6310ee99}/inline2.js +0 -0
  180. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-11a3c51b6310ee99}/inline3.js +0 -0
  181. /package/dist/wasm/snippets/{perspective-viewer-9a89352df1552d2b → perspective-viewer-11a3c51b6310ee99}/inline4.js +0 -0
  182. /package/src/rust/components/{style_controls.rs → style_controls/mod.rs} +0 -0
  183. /package/src/rust/{components/containers → config}/kvpair.rs +0 -0
@@ -29,17 +29,18 @@ use crate::session::Session;
29
29
  use crate::utils::*;
30
30
  use crate::*;
31
31
 
32
- /// A collection of `Subscription` which should trigger an event on the
33
- /// JavaScript Custom Element as a `CustomEvent`. There are no public methods
32
+ /// A collection of [`Subscription`]s which should trigger an event on the
33
+ /// JavaScript Custom Element as a [`CustomEvent`]. There are no public methods
34
34
  /// on `CustomElements`, but when it is `drop()` the Custom Element will no
35
35
  /// longer dispatch events such as `"perspective-config-change"`.
36
36
  #[derive(Clone)]
37
- pub struct CustomEvents(Rc<(CustomEventsDataRc, [Subscription; 6])>);
37
+ pub struct CustomEvents(Rc<(CustomEventsDataRc, [Subscription; 8])>);
38
38
 
39
39
  impl ImplicitClone for CustomEvents {}
40
+
40
41
  impl PartialEq for CustomEvents {
41
- fn eq(&self, _: &Self) -> bool {
42
- true
42
+ fn eq(&self, other: &Self) -> bool {
43
+ Rc::ptr_eq(&self.0, &other.0)
43
44
  }
44
45
  }
45
46
 
@@ -54,6 +55,7 @@ impl Deref for CustomEventsDataRc {
54
55
  }
55
56
  }
56
57
 
58
+ #[derive(PerspectiveProperties!)]
57
59
  struct CustomEventsData {
58
60
  elem: HtmlElement,
59
61
  session: Session,
@@ -62,8 +64,6 @@ struct CustomEventsData {
62
64
  last_dispatched: RefCell<Option<ViewerConfig>>,
63
65
  }
64
66
 
65
- derive_model!(Renderer, Session, Presentation for CustomEventsData);
66
-
67
67
  impl CustomEvents {
68
68
  pub fn new(
69
69
  elem: &HtmlElement,
@@ -86,12 +86,20 @@ impl CustomEvents {
86
86
 
87
87
  let settings_sub = presentation.settings_open_changed.add_listener({
88
88
  clone!(data);
89
- move |open| {
90
- data.dispatch_settings_open_changed(open);
89
+ move |open: bool| {
90
+ data.dispatch_event("toggle-settings", open).unwrap();
91
91
  data.clone().dispatch_config_update();
92
92
  }
93
93
  });
94
94
 
95
+ let before_settings_sub = presentation.settings_before_open_changed.add_listener({
96
+ clone!(data);
97
+ move |open: bool| {
98
+ data.dispatch_event("toggle-settings-before", open).unwrap();
99
+ // data.clone().dispatch_config_update();
100
+ }
101
+ });
102
+
95
103
  let column_settings_sub = presentation.column_settings_open_changed.add_listener({
96
104
  clone!(data);
97
105
  move |(open, column_name)| {
@@ -113,57 +121,72 @@ impl CustomEvents {
113
121
  move |_| data.clone().dispatch_config_update()
114
122
  });
115
123
 
116
- let title_sub = presentation.title_changed.add_listener({
124
+ let title_sub = session.title_changed.add_listener({
117
125
  clone!(data);
118
126
  move |_| data.clone().dispatch_config_update()
119
127
  });
120
128
 
129
+ let unload_sub = session.table_unloaded.add_listener({
130
+ clone!(data);
131
+ move |x: bool| {
132
+ if !x {
133
+ data.clone()
134
+ .dispatch_event("table-delete-before", JsValue::UNDEFINED)
135
+ .unwrap();
136
+ } else {
137
+ data.clone()
138
+ .dispatch_event("table-delete", JsValue::UNDEFINED)
139
+ .unwrap()
140
+ }
141
+ }
142
+ });
143
+
121
144
  Self(Rc::new((data, [
122
145
  theme_sub,
146
+ before_settings_sub,
123
147
  settings_sub,
124
148
  column_settings_sub,
125
149
  plugin_sub,
126
150
  view_sub,
127
151
  title_sub,
152
+ unload_sub,
128
153
  ])))
129
154
  }
130
155
 
131
- pub fn dispatch_column_style_changed(&self, config: &JsValue) {
132
- let event_init = web_sys::CustomEventInit::new();
133
- event_init.set_detail(config);
134
- let event = web_sys::CustomEvent::new_with_event_init_dict(
135
- "perspective-column-style-change",
136
- &event_init,
137
- );
138
- self.0.0.elem.dispatch_event(&event.unwrap()).unwrap();
156
+ pub fn dispatch_column_style_changed(&self, config: &JsValue) -> ApiResult<()> {
157
+ self.dispatch_event("column-style-change", config)?;
139
158
  self.0.0.clone().dispatch_config_update();
159
+ Ok(())
140
160
  }
141
161
 
142
162
  pub fn dispatch_select(&self, view_window: Option<&ViewWindow>) -> ApiResult<()> {
143
- let event_init = web_sys::CustomEventInit::new();
144
- event_init.set_detail(&serde_wasm_bindgen::to_value(&view_window)?);
145
- let event =
146
- web_sys::CustomEvent::new_with_event_init_dict("perspective-select", &event_init);
147
-
148
- self.0.0.elem.dispatch_event(&event.unwrap()).unwrap();
163
+ self.dispatch_event("select", &serde_wasm_bindgen::to_value(&view_window)?)?;
149
164
  self.0.0.clone().dispatch_config_update();
150
165
  Ok(())
151
166
  }
167
+
168
+ pub fn dispatch_event<T>(&self, name: &str, event: T) -> ApiResult<()>
169
+ where
170
+ T: Into<JsValue>,
171
+ {
172
+ self.0.0.dispatch_event(name, event)
173
+ }
152
174
  }
153
175
 
154
176
  impl CustomEventsDataRc {
155
- fn dispatch_settings_open_changed(&self, open: bool) {
177
+ pub fn dispatch_event<T>(&self, name: &str, event: T) -> ApiResult<()>
178
+ where
179
+ T: Into<JsValue>,
180
+ {
156
181
  let event_init = web_sys::CustomEventInit::new();
157
- event_init.set_detail(&JsValue::from(open));
182
+ event_init.set_detail(&event.into());
158
183
  let event = web_sys::CustomEvent::new_with_event_init_dict(
159
- "perspective-toggle-settings",
184
+ format!("perspective-{}", name).as_str(),
160
185
  &event_init,
161
- );
186
+ )?;
162
187
 
163
- self.elem
164
- .toggle_attribute_with_force("settings", open)
165
- .unwrap();
166
- self.elem.dispatch_event(&event.unwrap()).unwrap();
188
+ self.elem.dispatch_event(&event)?;
189
+ Ok(())
167
190
  }
168
191
 
169
192
  fn dispatch_column_settings_open_changed(&self, open: bool, column_name: Option<String>) {
@@ -23,21 +23,6 @@ use yew::prelude::*;
23
23
  use crate::utils::*;
24
24
  use crate::*;
25
25
 
26
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
27
- pub enum DragTarget {
28
- Active,
29
- GroupBy,
30
- SplitBy,
31
- Sort,
32
- Filter,
33
- }
34
-
35
- #[derive(Clone, Copy, Debug, Eq, PartialEq)]
36
- pub enum DragEffect {
37
- Copy,
38
- Move(DragTarget),
39
- }
40
-
41
26
  #[derive(Clone, Debug)]
42
27
  struct DragFrom {
43
28
  column: String,
@@ -50,19 +35,14 @@ struct DragOver {
50
35
  index: usize,
51
36
  }
52
37
 
53
- #[derive(Debug)]
38
+ #[derive(Debug, Default)]
54
39
  enum DragState {
40
+ #[default]
55
41
  NoDrag,
56
42
  DragInProgress(DragFrom),
57
43
  DragOverInProgress(DragFrom, DragOver),
58
44
  }
59
45
 
60
- impl Default for DragState {
61
- fn default() -> Self {
62
- Self::NoDrag
63
- }
64
- }
65
-
66
46
  impl DragState {
67
47
  const fn is_drag_in_progress(&self) -> bool {
68
48
  !matches!(self, Self::NoDrag)
@@ -249,7 +229,7 @@ impl DragDrop {
249
229
  pub fn dragenter_helper(callback: impl Fn() + 'static, target: NodeRef) -> Callback<DragEvent> {
250
230
  Callback::from({
251
231
  move |event: DragEvent| {
252
- js_log_maybe!({
232
+ maybe_log!({
253
233
  event.stop_propagation();
254
234
  event.prevent_default();
255
235
  if event.related_target().is_none() {
@@ -273,7 +253,7 @@ pub fn dragleave_helper(callback: impl Fn() + 'static, drag_ref: NodeRef) -> Cal
273
253
  Callback::from({
274
254
  clone!(drag_ref);
275
255
  move |event: DragEvent| {
276
- js_log_maybe!({
256
+ maybe_log!({
277
257
  event.stop_propagation();
278
258
  event.prevent_default();
279
259
 
@@ -18,7 +18,7 @@ use yew::prelude::*;
18
18
  /// Because ExprTK reports errors in column/row coordinates and visually needs
19
19
  /// to be applied to an entire token rather than a single character, we need
20
20
  /// fairly obnoxious counter logic to figure out how to generate the resulting
21
- /// syntax-highlighted HTML. The `Counter<'a>` struct encpsulates this logic,
21
+ /// syntax-highlighted HTML. The `Counter<'a>` struct encapsulates this logic,
22
22
  /// generating a `NodeRef` to any autocomplete-able `<span>` tokens, as well
23
23
  /// as other convenient data for HTML rendering, and can be called incrementally
24
24
  /// while iterating tokens after parsing.
@@ -26,13 +26,22 @@ pub struct Cursor<'a> {
26
26
  row: u32,
27
27
  col: u32,
28
28
  index: u32,
29
+
30
+ /// Error text
29
31
  pub err: &'a Option<ExprValidationError>,
32
+
33
+ /// Expression text
30
34
  pub txt: &'a str,
35
+
36
+ /// Actual element
31
37
  pub noderef: NodeRef,
38
+
39
+ /// Auto complete text
32
40
  pub auto: Option<String>,
33
41
  }
34
42
 
35
43
  impl<'a> Cursor<'a> {
44
+ /// Create a new Cursor (given its error state).
36
45
  pub fn new(err: &'a Option<ExprValidationError>) -> Self {
37
46
  Self {
38
47
  row: 1,
@@ -10,6 +10,8 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ //! Data processing functions for the Perspective/ExprTK language.
14
+
13
15
  mod cursor;
14
16
  mod tokenize;
15
17
 
@@ -35,22 +35,39 @@ use self::symbol::*;
35
35
  /// frequent pattern matching necessary when handling enum tokens.
36
36
  #[derive(Clone, Copy, Debug, PartialEq, Eq)]
37
37
  pub enum Token<'a> {
38
+ /// `//``
38
39
  Comment(&'a str),
40
+
41
+ /// ` `
39
42
  Whitespace(&'a str),
43
+
44
+ /// `\n`
40
45
  Break(&'a str),
46
+
47
+ /// `x`
41
48
  Symbol(&'a str),
49
+
50
+ /// `12`
42
51
  Literal(&'a str),
52
+
53
+ /// `+`
43
54
  Operator(&'a str),
55
+
56
+ /// `#`
44
57
  Unknown(&'a str),
58
+
59
+ /// `"Sales"`
45
60
  Column(&'a str),
46
61
  }
47
62
 
48
63
  use Token::*;
49
64
 
50
- impl ToHtml for Token<'_> {
51
- fn to_html(&self) -> Html {
65
+ impl Token<'_> {
66
+ pub fn to_html(&self) -> Html {
52
67
  html! {
53
- if matches!(self, Break(_)) { <br /> } else {
68
+ if matches!(self, Break(_)) {
69
+ <br />
70
+ } else {
54
71
  <span class={self.class_name()}>{ self.content() }</span>
55
72
  }
56
73
  }
@@ -25,7 +25,7 @@ use crate::js::clipboard_item::*;
25
25
  use crate::*;
26
26
 
27
27
  pub async fn paste_from_clipboard() -> Option<String> {
28
- JsFuture::from(global::clipboard().read_text())
28
+ JsFuture::from(global::navigator().clipboard().read_text())
29
29
  .await
30
30
  .ok()
31
31
  .and_then(|x| x.as_string())
@@ -58,7 +58,7 @@ fn poll(
58
58
  js_sys::Reflect::set(&options, &mimetype.into(), js_val)?;
59
59
  let item = ClipboardItem::new(&options);
60
60
  let items = std::iter::once(item).collect::<js_sys::Array>();
61
- let _promise = global::clipboard().write(&items.into());
61
+ let _promise = global::navigator().clipboard().write(&items.into());
62
62
  } else {
63
63
  clone!(js_ref);
64
64
  if count == 200 {
@@ -14,18 +14,13 @@ use std::fmt::Display;
14
14
 
15
15
  use wasm_bindgen::prelude::*;
16
16
 
17
- #[derive(Clone, Copy, Eq, PartialEq)]
17
+ #[derive(Clone, Copy, Default, Eq, PartialEq)]
18
18
  pub enum MimeType {
19
+ #[default]
19
20
  TextPlain,
20
21
  ImagePng,
21
22
  }
22
23
 
23
- impl Default for MimeType {
24
- fn default() -> Self {
25
- Self::TextPlain
26
- }
27
- }
28
-
29
24
  impl From<MimeType> for JsValue {
30
25
  fn from(x: MimeType) -> Self {
31
26
  Self::from(format!("{x}"))
@@ -20,7 +20,6 @@ mod intersection_observer;
20
20
  mod mimetype;
21
21
  pub mod plugin;
22
22
  pub mod resize_observer;
23
- mod testing;
24
23
 
25
24
  pub use self::clipboard::*;
26
25
  pub use self::intersection_observer::*;
@@ -20,6 +20,13 @@ use crate::presentation::ColumnConfigMap;
20
20
  #[wasm_bindgen]
21
21
  #[rustfmt::skip]
22
22
  extern "C" {
23
+
24
+ #[derive(Clone)]
25
+ pub type JsPerspectiveViewer;
26
+
27
+ #[wasm_bindgen(method)]
28
+ pub fn get_model(this: &JsPerspectiveViewer) -> JsValue;
29
+
23
30
  #[derive(Clone)]
24
31
  pub type JsPerspectiveViewerPlugin;
25
32
 
package/src/rust/lib.rs CHANGED
@@ -10,21 +10,24 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- //! The API for the [`@perspective-dev/viewer`]() JavaScript
14
- //! library.
13
+ //! The API for the [`@perspective-dev/viewer`](https://perspective-dev.github.io)
14
+ //! JavaScript library.
15
15
 
16
16
  // Required by yew's `html` macro.
17
17
  #![recursion_limit = "1024"]
18
18
  #![feature(const_type_name)]
19
- #![feature(let_chains)]
20
19
  #![feature(macro_metavar_expr)]
21
20
  #![feature(iter_intersperse)]
22
21
  #![feature(stmt_expr_attributes)]
22
+ #![feature(try_blocks)]
23
23
  #![allow(async_fn_in_trait)]
24
+ #![feature(more_qualified_paths)]
24
25
  #![warn(
25
26
  clippy::all,
26
27
  clippy::panic_in_result_fn,
27
- clippy::await_holding_refcell_ref
28
+ clippy::await_holding_refcell_ref,
29
+ clippy::fallible_impl_from,
30
+ clippy::unneeded_field_pattern
28
31
  )]
29
32
 
30
33
  pub mod components;
@@ -34,6 +37,7 @@ mod custom_events;
34
37
  mod dragdrop;
35
38
  pub mod exprtk;
36
39
  mod js;
40
+ mod root;
37
41
 
38
42
  #[doc(hidden)]
39
43
  pub mod model;
@@ -42,6 +46,10 @@ mod renderer;
42
46
  mod session;
43
47
  pub mod utils;
44
48
 
49
+ #[macro_use]
50
+ extern crate macro_rules_attribute;
51
+ extern crate alloc;
52
+
45
53
  use perspective_js::utils::*;
46
54
  use wasm_bindgen::prelude::*;
47
55
 
@@ -54,6 +62,7 @@ use crate::utils::define_web_component;
54
62
  #[wasm_bindgen(typescript_custom_section)]
55
63
  const TS_APPEND_CONTENT: &'static str = r#"
56
64
  import type {
65
+ ColumnType,
57
66
  TableInitOptions,
58
67
  ColumnWindow,
59
68
  ViewWindow,
@@ -82,6 +91,7 @@ pub fn registerPlugin(name: &str) {
82
91
  #[cfg(not(feature = "external-bootstrap"))]
83
92
  #[wasm_bindgen(js_name = "init")]
84
93
  pub fn js_init() {
94
+ console_error_panic_hook::set_once();
85
95
  perspective_js::utils::set_global_logging();
86
96
  define_web_components!("export * as psp from '../../perspective-viewer.js'");
87
97
  tracing::info!("Perspective initialized.");
@@ -96,11 +106,14 @@ pub fn js_init() {
96
106
  pub fn bootstrap_web_components(psp: &JsValue) {
97
107
  define_web_component::<PerspectiveViewerElement>(psp);
98
108
  define_web_component::<PerspectiveDebugPluginElement>(psp);
99
-
100
109
  define_web_component::<ExportDropDownMenuElement>(psp);
101
110
  define_web_component::<CopyDropDownMenuElement>(psp);
102
111
  }
103
112
 
113
+ /// Defining the web components needs an extern struct to reference the
114
+ /// generated JavaSript glue. This is parameterized by an attribute macro which
115
+ /// needs to be determined by the top-level compiled module - the JavaScript
116
+ /// glue code emitted by `wasm-bindgen-cli`.
104
117
  #[macro_export]
105
118
  macro_rules! define_web_components {
106
119
  ($x:expr) => {{
@@ -0,0 +1,82 @@
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 perspective_client::config::ColumnType;
14
+
15
+ use super::HasSession;
16
+ use crate::model::{HasPresentation, HasRenderer, PluginColumnStyles};
17
+ use crate::presentation::ColumnLocator;
18
+
19
+ pub trait ColumnLocatorExt: HasSession {
20
+ fn locator_name_or_default(&self, locator: &ColumnLocator) -> String {
21
+ match locator {
22
+ ColumnLocator::Table(s) | ColumnLocator::Expression(s) => s.clone(),
23
+ ColumnLocator::NewExpression => self.session().metadata().make_new_column_name(None),
24
+ }
25
+ }
26
+
27
+ fn is_locator_active(&self, locator: &ColumnLocator) -> bool {
28
+ locator
29
+ .name()
30
+ .map(|name| self.session().is_column_active(name))
31
+ .unwrap_or_default()
32
+ }
33
+
34
+ fn locator_view_type(&self, locator: &ColumnLocator) -> Option<ColumnType> {
35
+ let name = locator.name().cloned().unwrap_or_default();
36
+ self.session()
37
+ .metadata()
38
+ .get_column_view_type(name.as_str())
39
+ }
40
+
41
+ /// This function will return a [`ColumnLocator`] for agiven column name, or
42
+ /// [`None`] if no such column already exists. If you want to
43
+ /// create a new expression column, use ColumnLocator::Expr(None)
44
+ fn get_column_locator(&self, name: Option<String>) -> Option<ColumnLocator> {
45
+ name.and_then(|name| {
46
+ if self.session().metadata().is_column_expression(&name) {
47
+ Some(ColumnLocator::Expression(name))
48
+ } else {
49
+ self.session().metadata().get_table_columns().and_then(|x| {
50
+ x.iter()
51
+ .find_map(|n| (n == &name).then_some(ColumnLocator::Table(name.clone())))
52
+ })
53
+ }
54
+ })
55
+ }
56
+ }
57
+
58
+ impl<T: HasSession> ColumnLocatorExt for T {}
59
+
60
+ pub trait ColumnLocatorCurrentExt:
61
+ HasPresentation + HasRenderer + HasSession + PluginColumnStyles
62
+ {
63
+ /// Gets a [`ColumnLocator`] for the current UI's column settings state,
64
+ /// or [`None`] if it is not currently active.
65
+ fn get_current_column_locator(&self) -> Option<ColumnLocator> {
66
+ self.presentation()
67
+ .get_open_column_settings()
68
+ .locator
69
+ .filter(|locator| match locator {
70
+ ColumnLocator::Table(name) => {
71
+ self.session().is_locator_active(locator)
72
+ && self.can_render_column_styles(name).unwrap_or_default()
73
+ },
74
+ _ => true,
75
+ })
76
+ }
77
+ }
78
+
79
+ impl<T: HasPresentation + HasRenderer + HasSession + PluginColumnStyles> ColumnLocatorCurrentExt
80
+ for T
81
+ {
82
+ }
@@ -19,6 +19,7 @@ use super::{HasRenderer, HasSession, IsInvalidDrop};
19
19
  use crate::dragdrop::*;
20
20
  use crate::renderer::*;
21
21
  use crate::session::*;
22
+ use crate::utils::DragTarget;
22
23
  use crate::*;
23
24
 
24
25
  /// The possible states of a column (row) in the active columns list, including
@@ -16,14 +16,13 @@ use futures::join;
16
16
  use itertools::Itertools;
17
17
  use perspective_client::ViewWindow;
18
18
  use perspective_js::utils::ApiResult;
19
- use wasm_bindgen::JsCast;
19
+ use wasm_bindgen::{JsCast, JsValue, intern};
20
20
  use wasm_bindgen_futures::JsFuture;
21
21
 
22
22
  use super::export_app;
23
23
  use super::export_method::*;
24
24
  use super::get_viewer_config::*;
25
25
  use super::structural::*;
26
- use crate::config::*;
27
26
  use crate::js::JsPerspectiveViewerPlugin;
28
27
  use crate::utils::*;
29
28
 
@@ -43,7 +42,7 @@ fn tag_name_to_package(plugin: &JsPerspectiveViewerPlugin) -> String {
43
42
  pub trait CopyExportModel:
44
43
  HasSession + HasRenderer + HasPresentation + GetViewerConfigModel
45
44
  {
46
- async fn html_as_jsvalue(&self) -> ApiResult<web_sys::Blob> {
45
+ async fn html_as_jsvalue(&self) -> ApiResult<JsValue> {
47
46
  let view_config = self.get_viewer_config();
48
47
  let session = self.session().clone();
49
48
  let plugins = self
@@ -61,7 +60,7 @@ pub trait CopyExportModel:
61
60
  config.settings = false;
62
61
  let js_config = serde_json::to_string(&config)?;
63
62
  let html = export_app::render(&base64::encode(arrow), &js_config, &plugins);
64
- js_sys::JsString::from(html.trim()).as_blob()
63
+ Ok(js_sys::JsString::from(html.trim()).into())
65
64
  }
66
65
 
67
66
  /// Create a blob of this plugin's `.png` rendering by calling the
@@ -75,7 +74,7 @@ pub trait CopyExportModel:
75
74
  async fn png_as_jsvalue(&self) -> ApiResult<web_sys::Blob> {
76
75
  let renderer = self.renderer().clone();
77
76
  let plugin = renderer.get_active_plugin()?;
78
- let render = js_sys::Reflect::get(&plugin, js_intern::js_intern!("render"))?;
77
+ let render = js_sys::Reflect::get(&plugin, &intern("render").into())?;
79
78
  let render_fun = render.unchecked_into::<js_sys::Function>();
80
79
  let png = render_fun.call0(&plugin)?;
81
80
  let result = JsFuture::from(png.unchecked_into::<js_sys::Promise>())
@@ -87,7 +86,7 @@ pub trait CopyExportModel:
87
86
  async fn txt_as_jsvalue(&self, viewport: Option<ViewWindow>) -> ApiResult<web_sys::Blob> {
88
87
  let renderer = self.renderer().clone();
89
88
  let plugin = renderer.get_active_plugin()?;
90
- let render = js_sys::Reflect::get(&plugin, js_intern::js_intern!("render"))?;
89
+ let render = js_sys::Reflect::get(&plugin, &intern("render").into())?;
91
90
  let render_fun = render.unchecked_into::<js_sys::Function>();
92
91
  let txt = render_fun.call1(&plugin, &serde_wasm_bindgen::to_value(&viewport)?)?;
93
92
  let result = JsFuture::from(txt.unchecked_into::<js_sys::Promise>())
@@ -97,7 +96,7 @@ pub trait CopyExportModel:
97
96
  }
98
97
 
99
98
  /// Generate a result `Blob` for all types of `ExportMethod`.
100
- async fn export_method_to_jsvalue(&self, method: ExportMethod) -> ApiResult<web_sys::Blob> {
99
+ async fn export_method_to_blob(&self, method: ExportMethod) -> ApiResult<web_sys::Blob> {
101
100
  let viewport = self.renderer().get_selection();
102
101
 
103
102
  match method {
@@ -141,17 +140,54 @@ pub trait CopyExportModel:
141
140
  .await?
142
141
  .as_blob(),
143
142
  ExportMethod::ArrowAll => self.session().arrow_as_jsvalue(true, None).await?.as_blob(),
144
- ExportMethod::Html => self.html_as_jsvalue().await,
143
+ ExportMethod::Html => self.html_as_jsvalue().await?.as_blob(),
145
144
  ExportMethod::Plugin if self.renderer().is_chart() => self.png_as_jsvalue().await,
146
145
  ExportMethod::Plugin => self.txt_as_jsvalue(viewport).await,
147
- ExportMethod::JsonConfig => self
148
- .get_viewer_config()
149
- .await?
150
- .encode(&Some(ViewerConfigEncoding::JSONString))?
151
- .dyn_into::<js_sys::JsString>()?
152
- .as_blob(),
146
+ ExportMethod::JsonConfig => {
147
+ js_sys::JSON::stringify(&self.get_viewer_config().await?.encode()?)?.as_blob()
148
+ },
153
149
  }
154
150
  }
151
+
152
+ /// Generate a result `Blob` for all types of `ExportMethod`.
153
+ async fn export_method_to_jsvalue(&self, method: ExportMethod) -> ApiResult<JsValue> {
154
+ let viewport = self.renderer().get_selection();
155
+
156
+ Ok(match method {
157
+ ExportMethod::Csv => self.session().csv_as_jsvalue(false, None).await?.into(),
158
+ ExportMethod::CsvSelected => {
159
+ self.session().csv_as_jsvalue(false, viewport).await?.into()
160
+ },
161
+ ExportMethod::CsvAll => self.session().csv_as_jsvalue(true, None).await?.into(),
162
+ ExportMethod::Json => self.session().json_as_jsvalue(false, None).await?.into(),
163
+ ExportMethod::JsonSelected => self
164
+ .session()
165
+ .json_as_jsvalue(false, viewport)
166
+ .await?
167
+ .into(),
168
+ ExportMethod::JsonAll => self.session().json_as_jsvalue(true, None).await?.into(),
169
+ ExportMethod::Ndjson => self.session().ndjson_as_jsvalue(false, None).await?.into(),
170
+ ExportMethod::NdjsonSelected => self
171
+ .session()
172
+ .ndjson_as_jsvalue(false, viewport)
173
+ .await?
174
+ .into(),
175
+ ExportMethod::NdjsonAll => self.session().ndjson_as_jsvalue(true, None).await?.into(),
176
+ ExportMethod::Arrow => self.session().arrow_as_jsvalue(false, None).await?.into(),
177
+ ExportMethod::ArrowSelected => self
178
+ .session()
179
+ .arrow_as_jsvalue(false, viewport)
180
+ .await?
181
+ .into(),
182
+ ExportMethod::ArrowAll => self.session().arrow_as_jsvalue(true, None).await?.into(),
183
+ ExportMethod::Html => self.html_as_jsvalue().await?,
184
+ ExportMethod::Plugin if self.renderer().is_chart() => {
185
+ self.png_as_jsvalue().await?.into()
186
+ },
187
+ ExportMethod::Plugin => self.txt_as_jsvalue(viewport).await?.into(),
188
+ ExportMethod::JsonConfig => self.get_viewer_config().await?.encode()?,
189
+ })
190
+ }
155
191
  }
156
192
 
157
193
  impl<T: HasRenderer + HasSession + HasPresentation> CopyExportModel for T {}