@perspective-dev/viewer 4.4.1 → 4.5.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 (227) hide show
  1. package/dist/cdn/perspective-viewer.js +1 -2
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -1
  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/phosphor.css +1 -1
  17. package/dist/css/pro-dark.css +1 -1
  18. package/dist/css/pro.css +1 -1
  19. package/dist/css/solarized-dark.css +1 -1
  20. package/dist/css/solarized.css +1 -1
  21. package/dist/css/themes.css +1 -1
  22. package/dist/css/vaporwave.css +1 -1
  23. package/dist/esm/bootstrap.d.ts +2 -1
  24. package/dist/esm/column-format.d.ts +51 -0
  25. package/dist/esm/extensions.d.ts +2 -0
  26. package/dist/esm/perspective-viewer.d.ts +3 -1
  27. package/dist/esm/perspective-viewer.inline.js +1 -2
  28. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  29. package/dist/esm/perspective-viewer.js +1 -2
  30. package/dist/esm/perspective-viewer.js.map +4 -4
  31. package/dist/esm/perspective-viewer.worker.d.ts +2 -0
  32. package/dist/esm/plugin.d.ts +16 -72
  33. package/dist/esm/ts-rs/ColumnSelectMode.d.ts +1 -0
  34. package/dist/esm/ts-rs/PluginStaticConfig.d.ts +77 -0
  35. package/dist/esm/ts-rs/ViewerConfig.d.ts +6 -3
  36. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +7 -4
  37. package/dist/wasm/perspective-viewer.d.ts +77 -18
  38. package/dist/wasm/perspective-viewer.js +302 -148
  39. package/dist/wasm/perspective-viewer.wasm +0 -0
  40. package/dist/wasm/perspective-viewer.wasm.d.ts +20 -15
  41. package/package.json +24 -2
  42. package/src/css/column-selector.css +3 -2
  43. package/src/css/column-settings-panel.css +44 -9
  44. package/src/css/column-style.css +35 -2
  45. package/src/css/containers/scroll-panel.css +2 -1
  46. package/src/css/containers/tabs.css +8 -52
  47. package/src/css/dom/checkbox.css +2 -6
  48. package/src/css/form/code-editor.css +1 -0
  49. package/src/css/form/debug.css +3 -10
  50. package/src/css/plugin-selector.css +33 -0
  51. package/src/css/plugin-settings-panel.css +99 -0
  52. package/src/css/viewer.css +143 -3
  53. package/src/rust/components/column_dropdown.rs +3 -1
  54. package/src/rust/components/column_selector/active_column.rs +16 -19
  55. package/src/rust/components/column_selector/config_selector.rs +20 -20
  56. package/src/rust/components/column_selector/filter_column.rs +14 -14
  57. package/src/rust/components/column_selector/inactive_column.rs +10 -15
  58. package/src/rust/components/column_selector/pivot_column.rs +7 -7
  59. package/src/rust/components/column_selector/sort_column.rs +7 -7
  60. package/src/rust/components/column_selector.rs +55 -37
  61. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +15 -7
  62. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +395 -0
  63. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +15 -6
  64. package/src/rust/components/column_settings_sidebar/style_tab.rs +267 -136
  65. package/src/rust/components/column_settings_sidebar.rs +44 -49
  66. package/src/rust/components/containers/dragdrop_list.rs +32 -5
  67. package/src/rust/components/containers/mod.rs +0 -1
  68. package/src/rust/components/containers/scroll_panel.rs +21 -7
  69. package/src/rust/components/containers/sidebar.rs +8 -6
  70. package/src/rust/components/containers/split_panel.rs +3 -3
  71. package/src/rust/components/containers/tab_list.rs +3 -9
  72. package/src/rust/components/copy_dropdown.rs +2 -3
  73. package/src/rust/components/datetime_column_style.rs +19 -81
  74. package/src/rust/components/editable_header.rs +17 -3
  75. package/src/rust/components/export_dropdown.rs +2 -3
  76. package/src/rust/components/expression_editor.rs +29 -17
  77. package/src/rust/components/filter_dropdown.rs +2 -1
  78. package/src/rust/components/form/color_range_selector.rs +14 -7
  79. package/src/rust/components/form/debug.rs +47 -37
  80. package/src/rust/components/main_panel.rs +24 -65
  81. package/src/rust/components/mod.rs +2 -1
  82. package/src/rust/components/number_series_style.rs +161 -0
  83. package/src/rust/components/plugin_tab.rs +221 -0
  84. package/src/rust/components/settings_panel.rs +181 -59
  85. package/src/rust/components/status_bar.rs +141 -174
  86. package/src/rust/components/status_indicator.rs +15 -22
  87. package/src/rust/components/string_column_style.rs +20 -82
  88. package/src/rust/components/style_controls/number_string_format.rs +14 -30
  89. package/src/rust/components/viewer.rs +169 -132
  90. package/src/rust/config/column_config_schema.rs +195 -0
  91. package/src/rust/config/columns_config.rs +4 -97
  92. package/src/rust/config/datetime_column_style.rs +0 -5
  93. package/src/rust/config/mod.rs +8 -2
  94. package/src/rust/config/number_series_style.rs +79 -0
  95. package/src/rust/config/plugin_static_config.rs +144 -0
  96. package/src/rust/config/string_column_style.rs +0 -5
  97. package/src/rust/config/viewer_config.rs +5 -6
  98. package/src/rust/custom_elements/copy_dropdown.rs +30 -18
  99. package/src/rust/custom_elements/debug_plugin.rs +1 -3
  100. package/src/rust/custom_elements/export_dropdown.rs +26 -18
  101. package/src/rust/custom_elements/viewer.rs +62 -73
  102. package/src/rust/custom_events.rs +181 -224
  103. package/src/rust/js/plugin.rs +45 -117
  104. package/src/rust/lib.rs +34 -5
  105. package/src/rust/presentation/drag_helpers.rs +206 -0
  106. package/src/rust/presentation/props.rs +8 -0
  107. package/src/rust/presentation.rs +256 -41
  108. package/src/rust/{tasks → queries}/column_locator.rs +17 -73
  109. package/src/rust/queries/column_values.rs +59 -0
  110. package/src/rust/{tasks → queries}/columns_iter_set.rs +11 -18
  111. package/src/rust/queries/exports.rs +96 -0
  112. package/src/rust/queries/fetch_column_stats.rs +94 -0
  113. package/src/rust/queries/get_viewer_config.rs +54 -0
  114. package/src/rust/queries/mod.rs +44 -0
  115. package/src/rust/queries/plugin_column_styles.rs +101 -0
  116. package/src/rust/{engines.rs → queries/validate_expression.rs} +26 -15
  117. package/src/rust/renderer/activate.rs +1 -0
  118. package/src/rust/renderer/limits.rs +9 -4
  119. package/src/rust/renderer/plugin_store.rs +12 -0
  120. package/src/rust/renderer/props.rs +28 -3
  121. package/src/rust/renderer/registry.rs +40 -15
  122. package/src/rust/renderer.rs +703 -60
  123. package/src/rust/session/column_defaults_update.rs +20 -28
  124. package/src/rust/session/drag_drop_update.rs +10 -10
  125. package/src/rust/session/metadata.rs +31 -16
  126. package/src/rust/session/props.rs +15 -6
  127. package/src/rust/session/view_subscription.rs +10 -0
  128. package/src/rust/session.rs +109 -147
  129. package/src/rust/tasks/copy_export.rs +178 -158
  130. package/src/rust/tasks/{structural.rs → dismiss_render_warning.rs} +20 -40
  131. package/src/rust/tasks/edit_expression.rs +68 -88
  132. package/src/rust/tasks/eject.rs +25 -22
  133. package/src/rust/tasks/intersection_observer.rs +8 -21
  134. package/src/rust/tasks/mod.rs +19 -21
  135. package/src/rust/tasks/reset_all.rs +98 -0
  136. package/src/rust/tasks/resize_observer.rs +11 -33
  137. package/src/rust/tasks/restore_and_render.rs +128 -90
  138. package/src/rust/tasks/{get_viewer_config.rs → send_column_config.rs} +39 -35
  139. package/src/rust/tasks/send_plugin_config.rs +33 -33
  140. package/src/rust/tasks/update_and_render.rs +75 -49
  141. package/src/rust/{components/containers/trap_door_panel.rs → tasks/update_theme.rs} +34 -33
  142. package/src/rust/tasks/validate_expression.rs +61 -0
  143. package/src/rust/utils/browser/selection.rs +4 -4
  144. package/src/rust/utils/mod.rs +0 -63
  145. package/src/svg/checkbox-checked-icon.svg +1 -1
  146. package/src/svg/checkbox-unchecked-icon.svg +1 -1
  147. package/src/svg/mega-menu-icons-density.svg +23 -0
  148. package/src/svg/mega-menu-icons-map-density.svg +24 -0
  149. package/src/svg/mega-menu-icons-map-line.svg +19 -0
  150. package/src/themes/botanical.css +27 -53
  151. package/src/themes/defaults.css +24 -36
  152. package/src/themes/dracula.css +36 -54
  153. package/src/themes/gruvbox-dark.css +39 -59
  154. package/src/themes/gruvbox.css +16 -28
  155. package/src/themes/icons.css +5 -0
  156. package/src/themes/intl/de.css +43 -6
  157. package/src/themes/intl/es.css +43 -6
  158. package/src/themes/intl/fr.css +43 -6
  159. package/src/themes/intl/ja.css +43 -6
  160. package/src/themes/intl/pt.css +43 -6
  161. package/src/themes/intl/zh.css +43 -6
  162. package/src/themes/intl.css +38 -4
  163. package/src/themes/monokai.css +45 -61
  164. package/src/themes/phosphor.css +20 -29
  165. package/src/themes/pro-dark.css +25 -34
  166. package/src/themes/solarized-dark.css +21 -36
  167. package/src/themes/solarized.css +13 -23
  168. package/src/themes/vaporwave.css +40 -74
  169. package/src/ts/bootstrap.ts +14 -3
  170. package/src/ts/column-format.ts +162 -0
  171. package/src/ts/extensions.ts +4 -0
  172. package/src/ts/perspective-viewer.ts +9 -1
  173. package/src/{rust/components/column_settings_sidebar/style_tab/stub.rs → ts/perspective-viewer.worker.ts} +2 -22
  174. package/src/ts/plugin.ts +25 -101
  175. package/src/ts/ts-rs/{FormatUnit.ts → ColumnSelectMode.ts} +1 -1
  176. package/src/ts/ts-rs/PluginStaticConfig.ts +78 -0
  177. package/src/ts/ts-rs/ViewerConfig.ts +1 -2
  178. package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -3
  179. package/dist/esm/ts-rs/ColumnConfigValues.d.ts +0 -31
  180. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +0 -1
  181. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +0 -15
  182. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +0 -18
  183. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  184. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +0 -6
  185. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  186. package/dist/esm/ts-rs/FormatUnit.d.ts +0 -1
  187. package/dist/esm/ts-rs/NumberBackgroundMode.d.ts +0 -1
  188. package/dist/esm/ts-rs/NumberForegroundMode.d.ts +0 -1
  189. package/dist/esm/ts-rs/PluginConfig.d.ts +0 -2
  190. package/dist/esm/ts-rs/RoundingMode.d.ts +0 -1
  191. package/dist/esm/ts-rs/RoundingPriority.d.ts +0 -1
  192. package/dist/esm/ts-rs/SignDisplay.d.ts +0 -1
  193. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +0 -1
  194. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +0 -6
  195. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  196. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +0 -1
  197. package/dist/esm/ts-rs/UseGrouping.d.ts +0 -1
  198. package/src/rust/components/number_column_style.rs +0 -491
  199. package/src/rust/config/number_column_style.rs +0 -136
  200. package/src/rust/dragdrop.rs +0 -481
  201. package/src/rust/tasks/plugin_column_styles.rs +0 -98
  202. package/src/ts/ts-rs/ColumnConfigValues.ts +0 -14
  203. package/src/ts/ts-rs/CustomDatetimeFormat.ts +0 -3
  204. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +0 -5
  205. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +0 -8
  206. package/src/ts/ts-rs/DatetimeColorMode.ts +0 -3
  207. package/src/ts/ts-rs/DatetimeFormatType.ts +0 -8
  208. package/src/ts/ts-rs/FormatMode.ts +0 -3
  209. package/src/ts/ts-rs/NumberBackgroundMode.ts +0 -3
  210. package/src/ts/ts-rs/NumberForegroundMode.ts +0 -3
  211. package/src/ts/ts-rs/PluginConfig.ts +0 -4
  212. package/src/ts/ts-rs/RoundingMode.ts +0 -3
  213. package/src/ts/ts-rs/RoundingPriority.ts +0 -3
  214. package/src/ts/ts-rs/SignDisplay.ts +0 -3
  215. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +0 -3
  216. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +0 -4
  217. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  218. package/src/ts/ts-rs/TrailingZeroDisplay.ts +0 -3
  219. package/src/ts/ts-rs/UseGrouping.ts +0 -3
  220. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline0.js +0 -0
  221. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline1.js +0 -0
  222. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline2.js +0 -0
  223. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline3.js +0 -0
  224. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline4.js +0 -0
  225. /package/src/rust/{tasks → config}/export_method.rs +0 -0
  226. /package/src/rust/{tasks → queries}/export_app.rs +0 -0
  227. /package/src/rust/{tasks → queries}/is_invalid_drop.rs +0 -0
@@ -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
+ //! Copy/export side effects: render the current `View` to one of the
14
+ //! supported [`ExportMethod`] formats and return a [`Blob`] / [`JsValue`].
15
+
13
16
  use std::collections::HashSet;
14
17
 
15
18
  use base64::prelude::*;
16
19
  use futures::join;
17
20
  use itertools::Itertools;
18
21
  use perspective_client::ViewWindow;
19
- use perspective_js::utils::ApiResult;
20
- use wasm_bindgen::{JsCast, JsValue, intern};
21
- use wasm_bindgen_futures::JsFuture;
22
-
23
- use super::export_app;
24
- use super::export_method::*;
25
- use super::get_viewer_config::*;
26
- use super::structural::*;
22
+ use perspective_js::utils::*;
23
+ use wasm_bindgen::{JsCast, JsValue};
24
+
25
+ use crate::config::ExportMethod;
27
26
  use crate::js::JsPerspectiveViewerPlugin;
27
+ use crate::presentation::Presentation;
28
+ use crate::queries::{export_app, get_viewer_config};
29
+ use crate::renderer::Renderer;
30
+ use crate::session::Session;
28
31
  use crate::utils::*;
29
32
 
30
33
  fn tag_name_to_package(plugin: &JsPerspectiveViewerPlugin) -> String {
@@ -33,162 +36,179 @@ fn tag_name_to_package(plugin: &JsPerspectiveViewerPlugin) -> String {
33
36
  Itertools::intersperse(tag_parts, "-".to_owned()).collect::<String>()
34
37
  }
35
38
 
36
- /// A model trait for Copy/Export UI task behavior,
37
- ///
38
- /// Export functionality, for downloads and copy-to-clipboard, are mostly shared
39
- /// behavior, but require access to a few state objects depending on which
40
- /// format is desired. The `CopyExportModel` groups this functionality in a
41
- /// shared location, rather than distributing in the various state objects
42
- /// that are relevent per-format.
43
- pub trait CopyExportModel:
44
- HasSession + HasRenderer + HasPresentation + GetViewerConfigModel
45
- {
46
- async fn html_as_jsvalue(&self) -> ApiResult<JsValue> {
47
- let view_config = self.get_viewer_config();
48
- let session = self.session().clone();
49
- let plugins = self
50
- .renderer()
51
- .get_all_plugins()
52
- .iter()
53
- .map(tag_name_to_package)
54
- .collect::<HashSet<String>>()
55
- .into_iter()
56
- .collect::<Vec<_>>();
57
-
58
- let (arrow, config) = join!(session.arrow_as_vec(true, None), view_config);
59
- let arrow = arrow?;
60
- let mut config = config?;
61
- config.settings = false;
62
- let js_config = serde_json::to_string(&config)?;
63
- let html = export_app::render(&BASE64_STANDARD.encode(arrow), &js_config, &plugins);
64
- Ok(js_sys::JsString::from(html.trim()).into())
65
- }
39
+ /// Render the current view as a self-contained HTML document (Arrow data +
40
+ /// JSON layout embedded as base64 + JSON, with `<script type=module>` imports
41
+ /// for the active plugins).
42
+ pub async fn html_as_jsvalue(
43
+ session: &Session,
44
+ renderer: &Renderer,
45
+ presentation: &Presentation,
46
+ ) -> ApiResult<JsValue> {
47
+ let view_config = get_viewer_config(session, renderer, presentation);
48
+ let plugins = renderer
49
+ .get_all_plugins()
50
+ .iter()
51
+ .map(tag_name_to_package)
52
+ .collect::<HashSet<String>>()
53
+ .into_iter()
54
+ .collect::<Vec<_>>();
66
55
 
67
- /// Create a blob of this plugin's `.png` rendering by calling the
68
- /// `Plugin::render` method dynamically (as it may not exist e.g. for
69
- /// datagrid).
70
- ///
71
- /// # Errors
72
- ///
73
- /// It is assumed that `Plugin::render` exists on the plugin's Custom
74
- /// Element.
75
- async fn png_as_jsvalue(&self) -> ApiResult<web_sys::Blob> {
76
- let renderer = self.renderer().clone();
77
- let plugin = renderer.get_active_plugin()?;
78
- let render = js_sys::Reflect::get(&plugin, &intern("render").into())?;
79
- let render_fun = render.unchecked_into::<js_sys::Function>();
80
- let png = render_fun.call0(&plugin)?;
81
- let result = JsFuture::from(png.unchecked_into::<js_sys::Promise>())
82
- .await?
83
- .unchecked_into();
84
- Ok(result)
85
- }
56
+ let (arrow, config) = join!(
57
+ crate::queries::arrow_as_vec(session, true, None),
58
+ view_config
59
+ );
60
+ let arrow = arrow?;
61
+ let mut config = config?;
62
+ config.settings = false;
63
+ let js_config = serde_json::to_string(&config)?;
64
+ let html = export_app::render(&BASE64_STANDARD.encode(arrow), &js_config, &plugins);
65
+ Ok(js_sys::JsString::from(html.trim()).into())
66
+ }
86
67
 
87
- async fn txt_as_jsvalue(&self, viewport: Option<ViewWindow>) -> ApiResult<web_sys::Blob> {
88
- let renderer = self.renderer().clone();
89
- let plugin = renderer.get_active_plugin()?;
90
- let render = js_sys::Reflect::get(&plugin, &intern("render").into())?;
91
- let render_fun = render.unchecked_into::<js_sys::Function>();
92
- let txt = render_fun.call1(&plugin, &serde_wasm_bindgen::to_value(&viewport)?)?;
93
- let result = JsFuture::from(txt.unchecked_into::<js_sys::Promise>())
94
- .await?
95
- .unchecked_into();
96
- Ok(result)
97
- }
68
+ /// Render the current view as a `.png` `Blob` via the active plugin's
69
+ /// `render` method (typically only available for chart plugins).
70
+ pub async fn png_as_jsvalue(session: &Session, renderer: &Renderer) -> ApiResult<web_sys::Blob> {
71
+ let plugin = renderer.get_active_plugin()?;
72
+ let view: perspective_client::View = session
73
+ .get_view()
74
+ .ok_or(ApiError::from(ApiErrorType::NoTableError))?;
98
75
 
99
- /// Generate a result `Blob` for all types of `ExportMethod`.
100
- async fn export_method_to_blob(&self, method: ExportMethod) -> ApiResult<web_sys::Blob> {
101
- let viewport = self.renderer().get_selection();
76
+ let png = plugin.render(view.into(), None).await?;
77
+ Ok(png)
78
+ }
102
79
 
103
- match method {
104
- ExportMethod::Csv => self.session().csv_as_jsvalue(false, None).await?.as_blob(),
105
- ExportMethod::CsvSelected => self
106
- .session()
107
- .csv_as_jsvalue(false, viewport)
108
- .await?
109
- .as_blob(),
110
- ExportMethod::CsvAll => self.session().csv_as_jsvalue(true, None).await?.as_blob(),
111
- ExportMethod::Json => self.session().json_as_jsvalue(false, None).await?.as_blob(),
112
- ExportMethod::JsonSelected => self
113
- .session()
114
- .json_as_jsvalue(false, viewport)
115
- .await?
116
- .as_blob(),
117
- ExportMethod::JsonAll => self.session().json_as_jsvalue(true, None).await?.as_blob(),
118
- ExportMethod::Ndjson => self
119
- .session()
120
- .ndjson_as_jsvalue(false, None)
121
- .await?
122
- .as_blob(),
123
- ExportMethod::NdjsonSelected => self
124
- .session()
125
- .ndjson_as_jsvalue(false, viewport)
126
- .await?
127
- .as_blob(),
128
- ExportMethod::NdjsonAll => self
129
- .session()
130
- .ndjson_as_jsvalue(true, None)
131
- .await?
132
- .as_blob(),
133
- ExportMethod::Arrow => self
134
- .session()
135
- .arrow_as_jsvalue(false, None)
136
- .await?
137
- .as_blob(),
138
- ExportMethod::ArrowSelected => self
139
- .session()
140
- .arrow_as_jsvalue(false, viewport)
141
- .await?
142
- .as_blob(),
143
- ExportMethod::ArrowAll => self.session().arrow_as_jsvalue(true, None).await?.as_blob(),
144
- ExportMethod::Html => self.html_as_jsvalue().await?.as_blob(),
145
- ExportMethod::Plugin if self.renderer().is_chart() => self.png_as_jsvalue().await,
146
- ExportMethod::Plugin => self.txt_as_jsvalue(viewport).await,
147
- ExportMethod::JsonConfig => {
148
- js_sys::JSON::stringify(&self.get_viewer_config().await?.encode()?)?.as_blob()
149
- },
150
- }
151
- }
80
+ /// Render the current view as a `.txt` `Blob` via the active plugin's
81
+ /// `render` method (typically used for the datagrid).
82
+ pub async fn txt_as_jsvalue(
83
+ session: &Session,
84
+ renderer: &Renderer,
85
+ viewport: Option<ViewWindow>,
86
+ ) -> ApiResult<web_sys::Blob> {
87
+ let plugin = renderer.get_active_plugin()?;
88
+ let view: perspective_client::View = session
89
+ .get_view()
90
+ .ok_or(ApiError::from(ApiErrorType::NoTableError))?;
152
91
 
153
- /// Generate a result `Blob` for all types of `ExportMethod`.
154
- async fn export_method_to_jsvalue(&self, method: ExportMethod) -> ApiResult<JsValue> {
155
- let viewport = self.renderer().get_selection();
156
-
157
- Ok(match method {
158
- ExportMethod::Csv => self.session().csv_as_jsvalue(false, None).await?.into(),
159
- ExportMethod::CsvSelected => {
160
- self.session().csv_as_jsvalue(false, viewport).await?.into()
161
- },
162
- ExportMethod::CsvAll => self.session().csv_as_jsvalue(true, None).await?.into(),
163
- ExportMethod::Json => self.session().json_as_jsvalue(false, None).await?.into(),
164
- ExportMethod::JsonSelected => self
165
- .session()
166
- .json_as_jsvalue(false, viewport)
167
- .await?
168
- .into(),
169
- ExportMethod::JsonAll => self.session().json_as_jsvalue(true, None).await?.into(),
170
- ExportMethod::Ndjson => self.session().ndjson_as_jsvalue(false, None).await?.into(),
171
- ExportMethod::NdjsonSelected => self
172
- .session()
173
- .ndjson_as_jsvalue(false, viewport)
174
- .await?
175
- .into(),
176
- ExportMethod::NdjsonAll => self.session().ndjson_as_jsvalue(true, None).await?.into(),
177
- ExportMethod::Arrow => self.session().arrow_as_jsvalue(false, None).await?.into(),
178
- ExportMethod::ArrowSelected => self
179
- .session()
180
- .arrow_as_jsvalue(false, viewport)
92
+ let txt = plugin
93
+ .render(view.into(), viewport.map(|x| x.into()))
94
+ .await?;
95
+
96
+ Ok(txt)
97
+ }
98
+
99
+ /// Generate a result `Blob` for all types of [`ExportMethod`].
100
+ pub async fn export_method_to_blob(
101
+ session: &Session,
102
+ renderer: &Renderer,
103
+ presentation: &Presentation,
104
+ method: ExportMethod,
105
+ ) -> ApiResult<web_sys::Blob> {
106
+ let viewport = renderer.get_selection();
107
+
108
+ match method {
109
+ ExportMethod::Csv => crate::queries::csv_as_jsvalue(session, false, None)
110
+ .await?
111
+ .as_blob(),
112
+ ExportMethod::CsvSelected => crate::queries::csv_as_jsvalue(session, false, viewport)
113
+ .await?
114
+ .as_blob(),
115
+ ExportMethod::CsvAll => crate::queries::csv_as_jsvalue(session, true, None)
116
+ .await?
117
+ .as_blob(),
118
+ ExportMethod::Json => crate::queries::json_as_jsvalue(session, false, None)
119
+ .await?
120
+ .as_blob(),
121
+ ExportMethod::JsonSelected => crate::queries::json_as_jsvalue(session, false, viewport)
122
+ .await?
123
+ .as_blob(),
124
+ ExportMethod::JsonAll => crate::queries::json_as_jsvalue(session, true, None)
125
+ .await?
126
+ .as_blob(),
127
+ ExportMethod::Ndjson => crate::queries::ndjson_as_jsvalue(session, false, None)
128
+ .await?
129
+ .as_blob(),
130
+ ExportMethod::NdjsonSelected => crate::queries::ndjson_as_jsvalue(session, false, viewport)
131
+ .await?
132
+ .as_blob(),
133
+ ExportMethod::NdjsonAll => crate::queries::ndjson_as_jsvalue(session, true, None)
134
+ .await?
135
+ .as_blob(),
136
+ ExportMethod::Arrow => crate::queries::arrow_as_jsvalue(session, false, None)
137
+ .await?
138
+ .as_blob(),
139
+ ExportMethod::ArrowSelected => crate::queries::arrow_as_jsvalue(session, false, viewport)
140
+ .await?
141
+ .as_blob(),
142
+ ExportMethod::ArrowAll => crate::queries::arrow_as_jsvalue(session, true, None)
143
+ .await?
144
+ .as_blob(),
145
+ ExportMethod::Html => html_as_jsvalue(session, renderer, presentation)
146
+ .await?
147
+ .as_blob(),
148
+ ExportMethod::Plugin if renderer.is_chart() => png_as_jsvalue(session, renderer).await,
149
+ ExportMethod::Plugin => txt_as_jsvalue(session, renderer, viewport).await,
150
+ ExportMethod::JsonConfig => js_sys::JSON::stringify(
151
+ &get_viewer_config(session, renderer, presentation)
181
152
  .await?
182
- .into(),
183
- ExportMethod::ArrowAll => self.session().arrow_as_jsvalue(true, None).await?.into(),
184
- ExportMethod::Html => self.html_as_jsvalue().await?,
185
- ExportMethod::Plugin if self.renderer().is_chart() => {
186
- self.png_as_jsvalue().await?.into()
187
- },
188
- ExportMethod::Plugin => self.txt_as_jsvalue(viewport).await?.into(),
189
- ExportMethod::JsonConfig => self.get_viewer_config().await?.encode()?,
190
- })
153
+ .encode()?,
154
+ )?
155
+ .as_blob(),
191
156
  }
192
157
  }
193
158
 
194
- impl<T: HasRenderer + HasSession + HasPresentation> CopyExportModel for T {}
159
+ /// Generate a result `JsValue` for all types of [`ExportMethod`].
160
+ pub async fn export_method_to_jsvalue(
161
+ session: &Session,
162
+ renderer: &Renderer,
163
+ presentation: &Presentation,
164
+ method: ExportMethod,
165
+ ) -> ApiResult<JsValue> {
166
+ let viewport = renderer.get_selection();
167
+
168
+ Ok(match method {
169
+ ExportMethod::Csv => crate::queries::csv_as_jsvalue(session, false, None)
170
+ .await?
171
+ .into(),
172
+ ExportMethod::CsvSelected => crate::queries::csv_as_jsvalue(session, false, viewport)
173
+ .await?
174
+ .into(),
175
+ ExportMethod::CsvAll => crate::queries::csv_as_jsvalue(session, true, None)
176
+ .await?
177
+ .into(),
178
+ ExportMethod::Json => crate::queries::json_as_jsvalue(session, false, None)
179
+ .await?
180
+ .into(),
181
+ ExportMethod::JsonSelected => crate::queries::json_as_jsvalue(session, false, viewport)
182
+ .await?
183
+ .into(),
184
+ ExportMethod::JsonAll => crate::queries::json_as_jsvalue(session, true, None)
185
+ .await?
186
+ .into(),
187
+ ExportMethod::Ndjson => crate::queries::ndjson_as_jsvalue(session, false, None)
188
+ .await?
189
+ .into(),
190
+ ExportMethod::NdjsonSelected => crate::queries::ndjson_as_jsvalue(session, false, viewport)
191
+ .await?
192
+ .into(),
193
+ ExportMethod::NdjsonAll => crate::queries::ndjson_as_jsvalue(session, true, None)
194
+ .await?
195
+ .into(),
196
+ ExportMethod::Arrow => crate::queries::arrow_as_jsvalue(session, false, None)
197
+ .await?
198
+ .into(),
199
+ ExportMethod::ArrowSelected => crate::queries::arrow_as_jsvalue(session, false, viewport)
200
+ .await?
201
+ .into(),
202
+ ExportMethod::ArrowAll => crate::queries::arrow_as_jsvalue(session, true, None)
203
+ .await?
204
+ .into(),
205
+ ExportMethod::Html => html_as_jsvalue(session, renderer, presentation).await?,
206
+ ExportMethod::Plugin if renderer.is_chart() => {
207
+ png_as_jsvalue(session, renderer).await?.into()
208
+ },
209
+ ExportMethod::Plugin => txt_as_jsvalue(session, renderer, viewport).await?.into(),
210
+ ExportMethod::JsonConfig => get_viewer_config(session, renderer, presentation)
211
+ .await?
212
+ .encode()?,
213
+ })
214
+ }
@@ -10,44 +10,24 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- //! Structural typing traits that allow task methods to be automatically defined
14
- //! for any struct that holds the necessary state handles.
15
-
16
- use crate::custom_events::*;
17
- use crate::dragdrop::*;
18
- use crate::presentation::*;
19
- use crate::renderer::*;
20
- use crate::session::*;
21
-
22
- pub trait HasCustomEvents {
23
- fn custom_events(&self) -> &'_ CustomEvents;
24
- }
25
-
26
- pub trait HasDragDrop {
27
- fn dragdrop(&self) -> &'_ DragDrop;
28
- }
29
-
30
- pub trait HasPresentation {
31
- fn presentation(&self) -> &'_ Presentation;
32
- }
33
-
34
- pub trait HasRenderer {
35
- fn renderer(&self) -> &'_ Renderer;
36
- }
37
-
38
- pub trait HasSession {
39
- fn session(&self) -> &'_ Session;
40
- }
41
-
42
- impl HasSession for Session {
43
- fn session(&self) -> &'_ Session {
44
- self
45
- }
46
- }
47
-
48
- pub trait StateProvider {
49
- type State: Clone + 'static;
50
-
51
- /// Clones just the state object fields into a new dedicated state struct.
52
- fn clone_state(&self) -> Self::State;
13
+ use perspective_js::utils::*;
14
+ use yew::prelude::*;
15
+
16
+ use crate::renderer::Renderer;
17
+ use crate::session::Session;
18
+ use crate::utils::*;
19
+
20
+ /// Mute the active plugin's render-size warning and re-issue the existing
21
+ /// view to the renderer. Used by the `RenderWarning` dismiss button; the
22
+ /// component dispatches this task instead of reading `session.get_view()`
23
+ /// itself.
24
+ pub fn dismiss_render_warning_callback(session: &Session, renderer: &Renderer) -> Callback<()> {
25
+ clone!(session, renderer);
26
+ Callback::from(move |_: ()| {
27
+ clone!(session, renderer);
28
+ ApiFuture::spawn(async move {
29
+ renderer.disable_active_plugin_render_warning();
30
+ renderer.update(session.get_view()).await
31
+ });
32
+ })
53
33
  }
@@ -10,107 +10,87 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ //! Expression-editor side effects: update / save / delete an expression in
14
+ //! the active session, then re-render and reopen the column-settings drawer
15
+ //! on the affected column.
16
+
17
+ use perspective_client::clone;
13
18
  use perspective_client::config::{Expression, ViewConfigUpdate};
14
19
 
15
- use super::UpdateAndRender;
16
- use super::structural::*;
20
+ use super::update_and_render;
17
21
  use crate::presentation::{ColumnLocator, ColumnSettingsTab, OpenColumnSettings, Presentation};
18
22
  use crate::renderer::Renderer;
19
23
  use crate::session::Session;
20
24
  use crate::*;
21
25
 
22
- #[derive(Clone, PartialEq)]
23
- pub struct ExpressionUpdater {
24
- presentation: Presentation,
25
- renderer: Renderer,
26
- session: Session,
27
- }
26
+ /// Replace the expression at `old_name` with `new_expr` and re-render. After
27
+ /// the render completes, opens the column-settings drawer on the renamed
28
+ /// expression.
29
+ pub fn update_expr(
30
+ session: &Session,
31
+ renderer: &Renderer,
32
+ presentation: &Presentation,
33
+ old_name: String,
34
+ new_expr: Expression<'static>,
35
+ ) {
36
+ clone!(session, renderer, presentation);
37
+ ApiFuture::spawn(async move {
38
+ let update = session
39
+ .to_props()
40
+ .create_replace_expression_update(&old_name, &new_expr);
41
+
42
+ update_and_render(&session, &renderer, update)?.await?;
43
+ presentation.set_open_column_settings(Some(OpenColumnSettings {
44
+ locator: Some(ColumnLocator::Expression(new_expr.name.to_string())),
45
+ tab: Some(ColumnSettingsTab::Attributes),
46
+ }));
28
47
 
29
- impl HasPresentation for ExpressionUpdater {
30
- fn presentation(&self) -> &Presentation {
31
- &self.presentation
32
- }
33
- }
34
-
35
- impl HasRenderer for ExpressionUpdater {
36
- fn renderer(&self) -> &Renderer {
37
- &self.renderer
38
- }
39
- }
40
-
41
- impl HasSession for ExpressionUpdater {
42
- fn session(&self) -> &Session {
43
- &self.session
44
- }
48
+ Ok(())
49
+ });
45
50
  }
46
51
 
47
- pub trait EditExpression: HasPresentation + HasRenderer + HasSession + UpdateAndRender {
48
- fn get_expression_updater(&self) -> ExpressionUpdater {
49
- ExpressionUpdater {
50
- presentation: self.presentation().clone(),
51
- renderer: self.renderer().clone(),
52
- session: self.session().clone(),
53
- }
54
- }
55
-
56
- /// Spawns a future which will update the expression.
57
- fn update_expr(&self, old_name: String, new_expr: Expression<'static>) {
58
- let this = self.get_expression_updater();
59
- ApiFuture::spawn(async move {
60
- let update = this
61
- .session
62
- .to_props()
63
- .create_replace_expression_update(&old_name, &new_expr);
64
-
65
- this.update_and_render(update)?.await?;
66
- this.presentation
67
- .set_open_column_settings(Some(OpenColumnSettings {
68
- locator: Some(ColumnLocator::Expression(new_expr.name.to_string())),
69
- tab: Some(ColumnSettingsTab::Attributes),
70
- }));
71
-
72
- Ok(())
73
- });
74
- }
75
-
76
- /// Saves a new expression. Spawns a future.
77
- fn save_expr(&self, expr: Expression) -> ApiResult<()> {
78
- let presentation = self.presentation().clone();
79
- let expr_name: String = expr.name.clone().into();
80
- let task = {
81
- let mut serde_exprs = self.session().get_view_config().expressions.clone();
82
- serde_exprs.insert(&expr);
83
- self.update_and_render(ViewConfigUpdate {
84
- expressions: Some(serde_exprs),
85
- ..Default::default()
86
- })
87
- }?;
88
-
89
- ApiFuture::spawn(async move {
90
- task.await?;
91
- presentation.set_open_column_settings(Some(OpenColumnSettings {
92
- locator: Some(ColumnLocator::Expression(expr_name)),
93
- tab: Some(ColumnSettingsTab::Attributes),
94
- }));
52
+ /// Insert `expr` into the session's expressions and re-render. After the
53
+ /// render completes, opens the column-settings drawer on the new expression.
54
+ pub fn save_expr(
55
+ session: &Session,
56
+ renderer: &Renderer,
57
+ presentation: &Presentation,
58
+ expr: Expression,
59
+ ) -> ApiResult<()> {
60
+ let presentation = presentation.clone();
61
+ let expr_name: String = expr.name.clone().into();
62
+ let task = {
63
+ let mut serde_exprs = session.get_view_config().expressions.clone();
64
+ serde_exprs.insert(&expr);
65
+ update_and_render(session, renderer, ViewConfigUpdate {
66
+ expressions: Some(serde_exprs),
67
+ ..Default::default()
68
+ })
69
+ }?;
95
70
 
96
- Ok(())
97
- });
71
+ ApiFuture::spawn(async move {
72
+ task.await?;
73
+ presentation.set_open_column_settings(Some(OpenColumnSettings {
74
+ locator: Some(ColumnLocator::Expression(expr_name)),
75
+ tab: Some(ColumnSettingsTab::Attributes),
76
+ }));
98
77
 
99
78
  Ok(())
100
- }
79
+ });
101
80
 
102
- fn delete_expr(&self, expr_name: &str) -> ApiResult<()> {
103
- let mut serde_exprs = self.session().get_view_config().expressions.clone();
104
- serde_exprs.remove(expr_name);
105
- let config = ViewConfigUpdate {
106
- expressions: Some(serde_exprs),
107
- ..ViewConfigUpdate::default()
108
- };
109
-
110
- let task = self.update_and_render(config)?;
111
- ApiFuture::spawn(task);
112
- Ok(())
113
- }
81
+ Ok(())
114
82
  }
115
83
 
116
- impl<T: HasRenderer + HasSession + HasPresentation + UpdateAndRender> EditExpression for T {}
84
+ /// Remove the expression named `expr_name` from the session and re-render.
85
+ pub fn delete_expr(session: &Session, renderer: &Renderer, expr_name: &str) -> ApiResult<()> {
86
+ let mut serde_exprs = session.get_view_config().expressions.clone();
87
+ serde_exprs.remove(expr_name);
88
+ let config = ViewConfigUpdate {
89
+ expressions: Some(serde_exprs),
90
+ ..ViewConfigUpdate::default()
91
+ };
92
+
93
+ let task = update_and_render(session, renderer, config)?;
94
+ ApiFuture::spawn(task);
95
+ Ok(())
96
+ }
@@ -13,29 +13,32 @@
13
13
  use perspective_client::clone;
14
14
  use yew::Component;
15
15
 
16
- use super::{HasRenderer, HasSession};
17
16
  use crate::ApiFuture;
17
+ use crate::renderer::Renderer;
18
18
  use crate::root::Root;
19
- use crate::session::{ResetOptions, TableIntermediateState};
19
+ use crate::session::{ResetOptions, Session, TableIntermediateState};
20
20
 
21
- pub trait DeleteAll: HasSession + HasRenderer {
22
- fn delete_all<T: Component>(&self, root: &Root<T>) -> ApiFuture<()> {
23
- self.session().table_unloaded.emit(false);
24
- clone!(self.renderer(), self.session(), root);
25
- ApiFuture::new(self.renderer().clone().with_lock(async move {
26
- renderer.delete()?;
27
- root.borrow_mut().take().ok_or("Already deleted")?.destroy();
28
- session
29
- .reset(ResetOptions {
30
- config: true,
31
- expressions: true,
32
- table: Some(TableIntermediateState::Ejected),
33
- ..ResetOptions::default()
34
- })
35
- .await?;
36
- Ok(())
37
- }))
38
- }
21
+ /// Tear down the entire viewer: emit `table_unloaded`, lock the renderer
22
+ /// while it's destroyed, drop the Yew root, and reset the session to its
23
+ /// ejected state.
24
+ pub fn delete_all<T: Component>(
25
+ session: &Session,
26
+ renderer: &Renderer,
27
+ root: &Root<T>,
28
+ ) -> ApiFuture<()> {
29
+ session.table_unloaded.emit(false);
30
+ clone!(session, renderer, root);
31
+ ApiFuture::new(renderer.clone().with_lock(async move {
32
+ renderer.delete()?;
33
+ root.borrow_mut().take().ok_or("Already deleted")?.destroy();
34
+ session
35
+ .reset(ResetOptions {
36
+ config: true,
37
+ expressions: true,
38
+ table: Some(TableIntermediateState::Ejected),
39
+ ..ResetOptions::default()
40
+ })
41
+ .await?;
42
+ Ok(())
43
+ }))
39
44
  }
40
-
41
- impl<T: HasSession + HasRenderer> DeleteAll for T {}